diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 000000000..ff0c43dc6 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,3 @@ +# Global access +* @knopers8 @justonedev1 + diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 000000000..fb7011437 --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,18 @@ +## ALICE Experiment Control System (AliECS) + +The ALICE Experiment Control System (**AliECS**) is the piece of software to drive and control data taking activities in the experiment. +It is a distributed system that combines state of the art cluster resource management and experiment control functionalities into a single comprehensive solution. + +### Copilot agent instructions + +- Running `make` in the main directory lets you build all golang components of AliECS. +- Running `make test` in the main directory lets you run all golang tests of AliECS. +- Do not attempt to build C++ components unless you already have access to their dependencies (FairMQ and FairLogger) and the task requires you to do it. +- When changing API interfaces, run `make docs` to regenerate the API documentation. +- When changing gRPC interfaces and proto files, run `make generate` to regenerate the gRPC code. +- Do not include regenerated Go files in your commits unless you are changing the API or proto files. If you do, include only the files which are related to your task. +- Do not include modified go.mod and go.sum files in your commits unless you are adding or upgrading a dependency. +- When adding features or fixing bugs, add a corresponding unit test if feasible. Use Ginkgo/Gomega, unless a package already uses a different testing framework. +- When adding a new feature, extend the documentation accordingly, follow the existing style and structure, and make sure that Tables of Contents are updated. See [Documentation guidelines](docs/CONTRIBUTING.md#documentation-guidelines) for some more details. +- When providing a fix, explain what was causing the issue and how it was fixed. +- Avoid using abbreviations and acronyms for variable and function names. They are acceptable if they are commonly used in a given domain, such as "cfg" for "configura +++tion" or "LHC" for "Large Hadron Collider". diff --git a/.github/workflows/control-operator-release.yml b/.github/workflows/control-operator-release.yml new file mode 100644 index 000000000..c05c536f9 --- /dev/null +++ b/.github/workflows/control-operator-release.yml @@ -0,0 +1,59 @@ +name: AliECS Control Operator Release + +on: + push: + tags: + - 'control-operator-v*' + +permissions: + contents: read + packages: write + +defaults: + run: + working-directory: control-operator + +jobs: + release: + name: Test, Build & Publish Images + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v7 + + - uses: actions/setup-go@v7 + with: + go-version: '1.26.8' + + - uses: arduino/setup-protoc@v3 + with: + version: '35.1' + repo-token: ${{ secrets.GITHUB_TOKEN }} + + - name: Derive version from tag + id: version + run: | + echo "version=${GITHUB_REF_NAME#control-operator-v}" >> "$GITHUB_OUTPUT" + + - name: Verify tag matches Makefile VERSION + run: | + MAKEFILE_VERSION=$(sed -n 's/^VERSION ?= //p' Makefile) + if [ "$MAKEFILE_VERSION" != "${{ steps.version.outputs.version }}" ]; then + echo "Tag version (${{ steps.version.outputs.version }}) does not match Makefile VERSION ($MAKEFILE_VERSION)" >&2 + exit 1 + fi + + - name: Run tests + run: make test + + - name: Log in to GHCR + uses: docker/login-action@v4 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build & push images + run: | + make docker-build docker-push \ + TASK_IMG=ghcr.io/aliceo2group/control-operator/task-manager:v${{ steps.version.outputs.version }} \ + ENVIRONMENT_IMG=ghcr.io/aliceo2group/control-operator/environment-manager:v${{ steps.version.outputs.version }} diff --git a/.github/workflows/control-operator.yml b/.github/workflows/control-operator.yml new file mode 100644 index 000000000..db9bfbcb7 --- /dev/null +++ b/.github/workflows/control-operator.yml @@ -0,0 +1,31 @@ +name: AliECS Control Operator + +on: + pull_request: + push: + branches: + - 'master' + +jobs: + control-operator-basic-check: + name: Basic Make Check + runs-on: ${{ matrix.os }} + defaults: + run: + working-directory: control-operator + strategy: + fail-fast: false + matrix: + os: [macOS-latest, ubuntu-24.04] + go: [ '1.26.8' ] + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-go@v2 + with: + go-version: ${{matrix.go}} + - uses: arduino/setup-protoc@v3 + with: + version: '35.1' + repo-token: ${{ secrets.GITHUB_TOKEN }} + - run: make + - run: make test diff --git a/.github/workflows/control.yml b/.github/workflows/control.yml new file mode 100644 index 000000000..fa272d1ca --- /dev/null +++ b/.github/workflows/control.yml @@ -0,0 +1,25 @@ +name: AliECS Core + +on: + pull_request: + push: + branches: + - 'master' + +jobs: + control-basic-check: + name: Basic Make Check + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [macOS-latest, ubuntu-24.04] + go: [ '1.26.8' ] + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-go@v2 + with: + go-version: ${{matrix.go}} + - run: (make vendor) + - run: (make) + - run: (make test) diff --git a/.github/workflows/linkspector.yml b/.github/workflows/linkspector.yml new file mode 100644 index 000000000..75d766990 --- /dev/null +++ b/.github/workflows/linkspector.yml @@ -0,0 +1,15 @@ +name: Linkspector +on: [pull_request] +jobs: + check-links: + name: runner / linkspector + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Run linkspector + uses: umbrelladocs/action-linkspector@v1 + with: + github_token: ${{ secrets.github_token }} + reporter: github-pr-check + fail_level: any + filter_mode: added diff --git a/.gitignore b/.gitignore index f980c25ba..469eadb09 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,9 @@ build-* vendor tools bin +wf-unprocessed* +wf-processed* +*.*.old +coverage_results +.envrc +core/environment/runcounter.txt diff --git a/Gopkg.lock b/Gopkg.lock deleted file mode 100644 index 4f942b8ed..000000000 --- a/Gopkg.lock +++ /dev/null @@ -1,1051 +0,0 @@ -# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. - - -[[projects]] - branch = "master" - digest = "1:1237f2e0fe3256bbce813cc1bafddaf26b85bddbd2f8c08c30eb845140198aee" - name = "github.com/armon/consul-api" - packages = ["."] - pruneopts = "UT" - revision = "eb2c6b5be1b66bab83016e0b05f01b8d5496ffbd" - -[[projects]] - branch = "master" - digest = "1:7a9140c806333a78122763d08ec6c0d7358c7fff1df8cfbb01881c5dc46e24e5" - name = "github.com/armon/go-metrics" - packages = ["."] - pruneopts = "UT" - revision = "ec5e00d3c878b2a97bbe0884ef45ffd1b4f669f5" - -[[projects]] - digest = "1:d6afaeed1502aa28e80a4ed0981d570ad91b2579193404256ce672ed0a609e0d" - name = "github.com/beorn7/perks" - packages = ["quantile"] - pruneopts = "UT" - revision = "37c8de3658fcb183f997c4e13e8337516ab753e6" - version = "v1.0.1" - -[[projects]] - digest = "1:937ce6e0cd5ccfec205f444a0d9c74f5680cbb68cd0a992b000559bf964ea20b" - name = "github.com/briandowns/spinner" - packages = ["."] - pruneopts = "UT" - revision = "e3fb08e7443c496a847cb2eef48e3883f3e12c38" - version = "v1.6.1" - -[[projects]] - digest = "1:53cbf1b46de4361416179ade70a96517fe6263301e65a7afa6afde8d92bab4c6" - name = "github.com/coreos/etcd" - packages = [ - "client", - "pkg/pathutil", - "pkg/srv", - "pkg/types", - "version", - ] - pruneopts = "UT" - revision = "98d308426819d892e149fe45f6fd542464cb1f9d" - version = "v3.3.13" - -[[projects]] - digest = "1:05ffeeed3f0f05520de0679f6aa3219ffee69cfd6d9fb6c194879d4c818ad670" - name = "github.com/coreos/go-semver" - packages = ["semver"] - pruneopts = "UT" - revision = "e214231b295a8ea9479f11b70b35d5acf3556d9b" - version = "v0.3.0" - -[[projects]] - digest = "1:7cb4fdca4c251b3ef8027c90ea35f70c7b661a593b9eeae34753c65499098bb1" - name = "github.com/cpuguy83/go-md2man" - packages = ["md2man"] - pruneopts = "UT" - revision = "7762f7e404f8416dfa1d9bb6a8c192aa9acb4d19" - version = "v1.0.10" - -[[projects]] - digest = "1:b498b36dbb2b306d1c5205ee5236c9e60352be8f9eea9bf08186723a9f75b4f3" - name = "github.com/emirpasic/gods" - packages = [ - "containers", - "lists", - "lists/arraylist", - "trees", - "trees/binaryheap", - "utils", - ] - pruneopts = "UT" - revision = "1615341f118ae12f353cc8a983f35b584342c9b3" - version = "v1.12.0" - -[[projects]] - digest = "1:865079840386857c809b72ce300be7580cb50d3d3129ce11bf9aa6ca2bc1934a" - name = "github.com/fatih/color" - packages = ["."] - pruneopts = "UT" - revision = "5b77d2a35fb0ede96d138fc9a99f5c9b6aef11b4" - version = "v1.7.0" - -[[projects]] - digest = "1:abeb38ade3f32a92943e5be54f55ed6d6e3b6602761d74b4aab4c9dd45c18abd" - name = "github.com/fsnotify/fsnotify" - packages = ["."] - pruneopts = "UT" - revision = "c2828203cd70a50dcccfb2761f8b1f8ceef9a8e9" - version = "v1.4.7" - -[[projects]] - digest = "1:0e16b03c72bd42739ca68d055c10369e60d17838428f512f3b29ae115ba788e0" - name = "github.com/gdamore/encoding" - packages = ["."] - pruneopts = "UT" - revision = "6289cdc94c00ac4aa177771c5fce7af2f96b626d" - version = "v1.0.0" - -[[projects]] - digest = "1:0c4039930c835217e8bd7b4b58f9eccae17ff87720d2f62447ba8a416cdb7d7f" - name = "github.com/gdamore/tcell" - packages = [ - ".", - "terminfo", - "terminfo/a/ansi", - "terminfo/base", - "terminfo/dynamic", - "terminfo/v/vt100", - "terminfo/v/vt102", - "terminfo/v/vt220", - "terminfo/x/xterm", - ] - pruneopts = "UT" - revision = "1922b3786eaefecbba6fd8e11dbfd3531fad7a54" - version = "v1.2.0" - -[[projects]] - digest = "1:9ae31ce33b4bab257668963e844d98765b44160be4ee98cafc44637a213e530d" - name = "github.com/gobwas/glob" - packages = [ - ".", - "compiler", - "match", - "syntax", - "syntax/ast", - "syntax/lexer", - "util/runes", - "util/strings", - ] - pruneopts = "UT" - revision = "5ccd90ef52e1e632236f7326478d4faa74f99438" - version = "v0.2.3" - -[[projects]] - digest = "1:33f49b72fdad072a03d13b31a42edfff08088b7c27198f4073b8213a58884f27" - name = "github.com/gogo/protobuf" - packages = [ - "gogoproto", - "proto", - "protoc-gen-gogo/descriptor", - "sortkeys", - ] - pruneopts = "UT" - revision = "ba06b47c162d49f2af050fb4c75bcbc86a159d5c" - version = "v1.2.1" - -[[projects]] - digest = "1:7ade0e7351787347d3dd3456abb365fd71ea75c19a859505e6a427064e1f9e42" - name = "github.com/golang/protobuf" - packages = [ - "proto", - "protoc-gen-go/descriptor", - "ptypes", - "ptypes/any", - "ptypes/duration", - "ptypes/timestamp", - ] - pruneopts = "UT" - revision = "6c65a5562fc06764971b7c5d05c76c75e84bdbf7" - version = "v1.3.2" - -[[projects]] - digest = "1:582b704bebaa06b48c29b0cec224a6058a09c86883aaddabde889cd1a5f73e1b" - name = "github.com/google/uuid" - packages = ["."] - pruneopts = "UT" - revision = "0cd6bf5da1e1c83f8b45653022c74f71af0538a4" - version = "v1.1.1" - -[[projects]] - digest = "1:8b20ed6a6df0628913a69e4c1709537b2e26c7be4aca856bf52b431cf1ce6d90" - name = "github.com/hashicorp/consul" - packages = ["api"] - pruneopts = "UT" - revision = "a42ded477cf4e5ac1a850b42ec5d25672cd2545d" - version = "v1.5.3" - -[[projects]] - digest = "1:af105c7c5dc0b4ae41991f122cae860b9600f7d226072c2a83127048c991660c" - name = "github.com/hashicorp/go-cleanhttp" - packages = ["."] - pruneopts = "UT" - revision = "eda1e5db218aad1db63ca4642c8906b26bcf2744" - version = "v0.5.1" - -[[projects]] - digest = "1:6e9806a06d00f4d1f90806d7b5cfb11e35dca76503390ca6e704b05ea7051bff" - name = "github.com/hashicorp/go-immutable-radix" - packages = ["."] - pruneopts = "UT" - revision = "7dd1121b595e4e1bd6dd5caa78e0f5c454740379" - version = "v1.1.0" - -[[projects]] - digest = "1:7b893c9e1181e224506c523777dea0d16f4bd20a7627b100cc800e14229f405c" - name = "github.com/hashicorp/go-rootcerts" - packages = ["."] - pruneopts = "UT" - revision = "df8e78a645e18d56ed7bb9ae10ffb8174ab892e2" - version = "v1.0.1" - -[[projects]] - digest = "1:7fae9ec96d10b2afce0da23c378c8b3389319b7f92fa092f2621bba3078cfb4b" - name = "github.com/hashicorp/golang-lru" - packages = ["simplelru"] - pruneopts = "UT" - revision = "7f827b33c0f158ec5dfbba01bb0b14a4541fd81d" - version = "v0.5.3" - -[[projects]] - digest = "1:c0d19ab64b32ce9fe5cf4ddceba78d5bc9807f0016db6b1183599da3dcc24d10" - name = "github.com/hashicorp/hcl" - packages = [ - ".", - "hcl/ast", - "hcl/parser", - "hcl/printer", - "hcl/scanner", - "hcl/strconv", - "hcl/token", - "json/parser", - "json/scanner", - "json/token", - ] - pruneopts = "UT" - revision = "8cb6e5b959231cc1119e43259c4a608f9c51a241" - version = "v1.0.0" - -[[projects]] - digest = "1:acc81e4e4289587b257ccdfccbc6eaf16d4c2fb57dda73c6bb349bf50f02501f" - name = "github.com/hashicorp/serf" - packages = ["coordinate"] - pruneopts = "UT" - revision = "15cfd05de3dffb3664aa37b06e91f970b825e380" - version = "v0.8.3" - -[[projects]] - digest = "1:a1038ef593beb4771c8f0f9c26e8b00410acd800af5c6864651d9bf160ea1813" - name = "github.com/hpcloud/tail" - packages = [ - ".", - "ratelimiter", - "util", - "watch", - "winfile", - ] - pruneopts = "UT" - revision = "a30252cb686a21eb2d0b98132633053ec2f7f1e5" - version = "v1.0.0" - -[[projects]] - digest = "1:870d441fe217b8e689d7949fef6e43efbc787e50f200cb1e70dbca9204a1d6be" - name = "github.com/inconshreveable/mousetrap" - packages = ["."] - pruneopts = "UT" - revision = "76626ae9c91c4f2a10f34cad8ce83ea42c93bb75" - version = "v1.0" - -[[projects]] - branch = "master" - digest = "1:62fe3a7ea2050ecbd753a71889026f83d73329337ada66325cbafd5dea5f713d" - name = "github.com/jbenet/go-context" - packages = ["io"] - pruneopts = "UT" - revision = "d14ea06fba99483203c19d92cfcd13ebe73135f4" - -[[projects]] - branch = "master" - digest = "1:ae940e9156d0d778ce1991bcc99e58fa5ca2790ddf7c5f0bc05385854416f7a6" - name = "github.com/jinzhu/copier" - packages = ["."] - pruneopts = "UT" - revision = "976e0346caa839d22a17f8031a96bcd0870c0128" - -[[projects]] - digest = "1:709cd2a2c29cc9b89732f6c24846bbb9d6270f28ef5ef2128cc73bd0d6d7bff9" - name = "github.com/json-iterator/go" - packages = ["."] - pruneopts = "UT" - revision = "27518f6661eba504be5a7a9a9f6d9460d892ade3" - version = "v1.1.7" - -[[projects]] - digest = "1:86c75d55fde56a814f69515d092963634d6f5f6bc1c3e9091b01fbe373a9546e" - name = "github.com/k0kubun/pp" - packages = ["."] - pruneopts = "UT" - revision = "3d73dea227e0711e38b911ffa6fbafc8ff6b2991" - version = "v3.0.1" - -[[projects]] - digest = "1:fd7f169f32c221b096c74e756bda16fe22d3bb448bbf74042fd0700407a1f92f" - name = "github.com/kevinburke/ssh_config" - packages = ["."] - pruneopts = "UT" - revision = "6cfae18c12b8934b1afba3ce8159476fdef666ba" - version = "1.0" - -[[projects]] - digest = "1:31e761d97c76151dde79e9d28964a812c46efc5baee4085b86f68f0c654450de" - name = "github.com/konsorten/go-windows-terminal-sequences" - packages = ["."] - pruneopts = "UT" - revision = "f55edac94c9bbba5d6182a4be46d86a2c9b5b50e" - version = "v1.0.2" - -[[projects]] - branch = "master" - digest = "1:17e2b99f622792fcc12942bfc0f9089d1532f6dfc40878b44ae145b458fbab30" - name = "github.com/looplab/fsm" - packages = ["."] - pruneopts = "UT" - revision = "84b5307469f859464403f80919467950a79de1b1" - -[[projects]] - digest = "1:39de67207027b7374272d5ec9b1bf85457714150ba1263e8d236316629dad570" - name = "github.com/lucasb-eyer/go-colorful" - packages = ["."] - pruneopts = "UT" - revision = "30298f24079860c4dee452fdef6519b362a4a026" - version = "v1.0.2" - -[[projects]] - digest = "1:5a0ef768465592efca0412f7e838cdc0826712f8447e70e6ccc52eb441e9ab13" - name = "github.com/magiconair/properties" - packages = ["."] - pruneopts = "UT" - revision = "de8848e004dd33dc07a2947b3d76f618a7fc7ef1" - version = "v1.8.1" - -[[projects]] - digest = "1:c658e84ad3916da105a761660dcaeb01e63416c8ec7bc62256a9b411a05fcd67" - name = "github.com/mattn/go-colorable" - packages = ["."] - pruneopts = "UT" - revision = "167de6bfdfba052fa6b2d3664c8f5272e23c9072" - version = "v0.0.9" - -[[projects]] - digest = "1:9b90c7639a41697f3d4ad12d7d67dfacc9a7a4a6e0bbfae4fc72d0da57c28871" - name = "github.com/mattn/go-isatty" - packages = ["."] - pruneopts = "UT" - revision = "1311e847b0cb909da63b5fecfb5370aa66236465" - version = "v0.0.8" - -[[projects]] - digest = "1:0356f3312c9bd1cbeda81505b7fd437501d8e778ab66998ef69f00d7f9b3a0d7" - name = "github.com/mattn/go-runewidth" - packages = ["."] - pruneopts = "UT" - revision = "3ee7d812e62a0804a7d0a324e0249ca2db3476d3" - version = "v0.0.4" - -[[projects]] - digest = "1:ff5ebae34cfbf047d505ee150de27e60570e8c394b3b8fdbb720ff6ac71985fc" - name = "github.com/matttproud/golang_protobuf_extensions" - packages = ["pbutil"] - pruneopts = "UT" - revision = "c12348ce28de40eed0136aa2b644d0ee0650e56c" - version = "v1.0.1" - -[[projects]] - branch = "master" - digest = "1:30b526b2ebada8db8d58f4b05fd3b5c1159fa198523b1e15443f86daf52b288e" - name = "github.com/mesos/mesos-go" - packages = [ - "api/v1/cmd", - "api/v1/lib", - "api/v1/lib/backoff", - "api/v1/lib/client", - "api/v1/lib/debug", - "api/v1/lib/encoding", - "api/v1/lib/encoding/codecs", - "api/v1/lib/encoding/framing", - "api/v1/lib/encoding/json", - "api/v1/lib/encoding/proto", - "api/v1/lib/executor", - "api/v1/lib/executor/calls", - "api/v1/lib/executor/config", - "api/v1/lib/executor/events", - "api/v1/lib/extras/metrics", - "api/v1/lib/extras/scheduler/callrules", - "api/v1/lib/extras/scheduler/controller", - "api/v1/lib/extras/scheduler/eventrules", - "api/v1/lib/extras/store", - "api/v1/lib/httpcli", - "api/v1/lib/httpcli/apierrors", - "api/v1/lib/httpcli/httpexec", - "api/v1/lib/httpcli/httpsched", - "api/v1/lib/recordio", - "api/v1/lib/resourcefilters", - "api/v1/lib/resources", - "api/v1/lib/roles", - "api/v1/lib/scheduler", - "api/v1/lib/scheduler/calls", - "api/v1/lib/scheduler/events", - "api/v1/lib/time", - ] - pruneopts = "UT" - revision = "56ac038085ac996051390598b156bff1c6ef0d3d" - -[[projects]] - branch = "master" - digest = "1:2b32af4d2a529083275afc192d1067d8126b578c7a9613b26600e4df9c735155" - name = "github.com/mgutz/ansi" - packages = ["."] - pruneopts = "UT" - revision = "9520e82c474b0a04dd04f8a40959027271bab992" - -[[projects]] - digest = "1:5d231480e1c64a726869bc4142d270184c419749d34f167646baa21008eb0a79" - name = "github.com/mitchellh/go-homedir" - packages = ["."] - pruneopts = "UT" - revision = "af06845cf3004701891bf4fdb884bfe4920b3727" - version = "v1.1.0" - -[[projects]] - digest = "1:53bc4cd4914cd7cd52139990d5170d6dc99067ae31c56530621b18b35fc30318" - name = "github.com/mitchellh/mapstructure" - packages = ["."] - pruneopts = "UT" - revision = "3536a929edddb9a5b34bd6861dc4a9647cb459fe" - version = "v1.1.2" - -[[projects]] - digest = "1:33422d238f147d247752996a26574ac48dcf472976eda7f5134015f06bf16563" - name = "github.com/modern-go/concurrent" - packages = ["."] - pruneopts = "UT" - revision = "bacd9c7ef1dd9b15be4a9909b8ac7a4e313eec94" - version = "1.0.3" - -[[projects]] - digest = "1:e32bdbdb7c377a07a9a46378290059822efdce5c8d96fe71940d87cb4f918855" - name = "github.com/modern-go/reflect2" - packages = ["."] - pruneopts = "UT" - revision = "4b7aa43c6742a2c18fdef89dd197aaae7dac7ccd" - version = "1.0.1" - -[[projects]] - digest = "1:b56c589214f01a5601e0821387db484617392d0042f26234bf2da853a2f498a1" - name = "github.com/naoina/go-stringutil" - packages = ["."] - pruneopts = "UT" - revision = "6b638e95a32d0c1131db0e7fe83775cbea4a0d0b" - version = "v0.1.0" - -[[projects]] - digest = "1:f58c3d0e46b64878d00652fedba24ee879725191ab919dca7b62586859281c04" - name = "github.com/naoina/toml" - packages = [ - ".", - "ast", - ] - pruneopts = "UT" - revision = "e6f5723bf2a66af014955e0888881314cf294129" - version = "v0.1.1" - -[[projects]] - digest = "1:abcdbf03ca6ca13d3697e2186edc1f33863bbdac2b3a44dfa39015e8903f7409" - name = "github.com/olekukonko/tablewriter" - packages = ["."] - pruneopts = "UT" - revision = "e6d60cf7ba1f42d86d54cdf5508611c4aafb3970" - version = "v0.0.1" - -[[projects]] - digest = "1:e9c3bb68a6c9470302b8046d4647e0612a2ea6037b9c6a47de60c0a90db504f8" - name = "github.com/onsi/ginkgo" - packages = [ - ".", - "config", - "internal/codelocation", - "internal/containernode", - "internal/failer", - "internal/leafnodes", - "internal/remote", - "internal/spec", - "internal/spec_iterator", - "internal/specrunner", - "internal/suite", - "internal/testingtproxy", - "internal/writer", - "reporters", - "reporters/stenographer", - "reporters/stenographer/support/go-colorable", - "reporters/stenographer/support/go-isatty", - "types", - ] - pruneopts = "UT" - revision = "eea6ad008b96acdaa524f5b409513bf062b500ad" - version = "v1.8.0" - -[[projects]] - digest = "1:fe167e8f858cbfc10c721f2000f2446140800bb53fa83c088e03d191275611a4" - name = "github.com/onsi/gomega" - packages = [ - ".", - "format", - "internal/assertion", - "internal/asyncassertion", - "internal/oraclematcher", - "internal/testingtsupport", - "matchers", - "matchers/support/goraph/bipartitegraph", - "matchers/support/goraph/edge", - "matchers/support/goraph/node", - "matchers/support/goraph/util", - "types", - ] - pruneopts = "UT" - revision = "90e289841c1ed79b7a598a7cd9959750cb5e89e2" - version = "v1.5.0" - -[[projects]] - digest = "1:e5d0bd87abc2781d14e274807a470acd180f0499f8bf5bb18606e9ec22ad9de9" - name = "github.com/pborman/uuid" - packages = ["."] - pruneopts = "UT" - revision = "adf5a7427709b9deb95d29d3fa8a2bf9cfd388f1" - version = "v1.2" - -[[projects]] - digest = "1:93131d8002d7025da13582877c32d1fc302486775a1b06f62241741006428c5e" - name = "github.com/pelletier/go-toml" - packages = ["."] - pruneopts = "UT" - revision = "728039f679cbcd4f6a54e080d2219a4c4928c546" - version = "v1.4.0" - -[[projects]] - branch = "master" - digest = "1:9d9989e96f765584e1b58e7f23e1b94740deb282fd363e5e7c5b5e30ae7b7c2c" - name = "github.com/pquerna/ffjson" - packages = [ - "fflib/v1", - "fflib/v1/internal", - ] - pruneopts = "UT" - revision = "e517b90714f7c0eabe6d2e570a5886ae077d6db6" - -[[projects]] - digest = "1:eb04f69c8991e52eff33c428bd729e04208bf03235be88e4df0d88497c6861b9" - name = "github.com/prometheus/client_golang" - packages = [ - "prometheus", - "prometheus/internal", - "prometheus/promhttp", - ] - pruneopts = "UT" - revision = "170205fb58decfd011f1550d4cfb737230d7ae4f" - version = "v1.1.0" - -[[projects]] - branch = "master" - digest = "1:2d5cd61daa5565187e1d96bae64dbbc6080dacf741448e9629c64fd93203b0d4" - name = "github.com/prometheus/client_model" - packages = ["go"] - pruneopts = "UT" - revision = "fd36f4220a901265f90734c3183c5f0c91daa0b8" - -[[projects]] - digest = "1:8dcedf2e8f06c7f94e48267dea0bc0be261fa97b377f3ae3e87843a92a549481" - name = "github.com/prometheus/common" - packages = [ - "expfmt", - "internal/bitbucket.org/ww/goautoneg", - "model", - ] - pruneopts = "UT" - revision = "31bed53e4047fd6c510e43a941f90cb31be0972a" - version = "v0.6.0" - -[[projects]] - digest = "1:366f5aa02ff6c1e2eccce9ca03a22a6d983da89eecff8a89965401764534eb7c" - name = "github.com/prometheus/procfs" - packages = [ - ".", - "internal/fs", - ] - pruneopts = "UT" - revision = "3f98efb27840a48a7a2898ec80be07674d19f9c8" - version = "v0.0.3" - -[[projects]] - branch = "master" - digest = "1:d6e9ea49a9c5241d906762898a0d7ccb12a3f3e6f949586010e56ca2203312bc" - name = "github.com/rivo/tview" - packages = ["."] - pruneopts = "UT" - revision = "23dc8a0944e4cdc31297e081606be7959b5c4efc" - -[[projects]] - digest = "1:557a3527e466dd95ec976b81888f6d5a12059be38f5198a5804564cebf9f5277" - name = "github.com/rivo/uniseg" - packages = ["."] - pruneopts = "UT" - revision = "f8f8f751c732fdcd5e158c13c28f8863ad80e9a5" - version = "v0.1.0" - -[[projects]] - digest = "1:b36a0ede02c4c2aef7df7f91cbbb7bb88a98b5d253509d4f997dda526e50c88c" - name = "github.com/russross/blackfriday" - packages = ["."] - pruneopts = "UT" - revision = "05f3235734ad95d0016f6a23902f06461fcf567a" - version = "v1.5.2" - -[[projects]] - digest = "1:d917313f309bda80d27274d53985bc65651f81a5b66b820749ac7f8ef061fd04" - name = "github.com/sergi/go-diff" - packages = ["diffmatchpatch"] - pruneopts = "UT" - revision = "1744e2970ca51c86172c8190fadad617561ed6e7" - version = "v1.0.0" - -[[projects]] - digest = "1:04457f9f6f3ffc5fea48e71d62f2ca256637dee0a04d710288e27e05c8b41976" - name = "github.com/sirupsen/logrus" - packages = ["."] - pruneopts = "UT" - revision = "839c75faf7f98a33d445d181f3018b5c3409a45e" - version = "v1.4.2" - -[[projects]] - digest = "1:bb495ec276ab82d3dd08504bbc0594a65de8c3b22c6f2aaa92d05b73fbf3a82e" - name = "github.com/spf13/afero" - packages = [ - ".", - "mem", - ] - pruneopts = "UT" - revision = "588a75ec4f32903aa5e39a2619ba6a4631e28424" - version = "v1.2.2" - -[[projects]] - digest = "1:08d65904057412fc0270fc4812a1c90c594186819243160dc779a402d4b6d0bc" - name = "github.com/spf13/cast" - packages = ["."] - pruneopts = "UT" - revision = "8c9545af88b134710ab1cd196795e7f2388358d7" - version = "v1.3.0" - -[[projects]] - digest = "1:abe9f3f23399646a6263682cacc9e86969f6c7e768f0ef036449926aa24cbbef" - name = "github.com/spf13/cobra" - packages = [ - ".", - "doc", - ] - pruneopts = "UT" - revision = "f2b07da1e2c38d5f12845a4f607e2e1018cbb1f5" - version = "v0.0.5" - -[[projects]] - digest = "1:1b753ec16506f5864d26a28b43703c58831255059644351bbcb019b843950900" - name = "github.com/spf13/jwalterweatherman" - packages = ["."] - pruneopts = "UT" - revision = "94f6ae3ed3bceceafa716478c5fbf8d29ca601a1" - version = "v1.1.0" - -[[projects]] - digest = "1:c1b1102241e7f645bc8e0c22ae352e8f0dc6484b6cb4d132fa9f24174e0119e2" - name = "github.com/spf13/pflag" - packages = ["."] - pruneopts = "UT" - revision = "298182f68c66c05229eb03ac171abe6e309ee79a" - version = "v1.0.3" - -[[projects]] - digest = "1:5d7e98ad29b0dc062d7cb720c4f8c85b521b64a508ffd6d82d5c288d682a2728" - name = "github.com/spf13/viper" - packages = [ - ".", - "remote", - ] - pruneopts = "UT" - revision = "b5bf975e5823809fb22c7644d008757f78a4259e" - version = "v1.4.0" - -[[projects]] - digest = "1:e4ed0afd67bf7be353921665cdac50834c867ff1bba153efc0745b755a7f5905" - name = "github.com/src-d/gcfg" - packages = [ - ".", - "scanner", - "token", - "types", - ] - pruneopts = "UT" - revision = "1ac3a1ac202429a54835fe8408a92880156b489d" - version = "v1.4.0" - -[[projects]] - branch = "master" - digest = "1:cb73172668cfdb965aee8d0eb9ada2b278c85ddc8f4593c4666dd0fe6491ed38" - name = "github.com/teo/logrus-prefixed-formatter" - packages = ["."] - pruneopts = "UT" - revision = "d4c78d9812958d3657170b1369c57a6be0023907" - -[[projects]] - digest = "1:172f94a6b3644a8f9e6b5e5b7fc9fe1e42d424f52a0300b2e7ab1e57db73f85d" - name = "github.com/xanzy/ssh-agent" - packages = ["."] - pruneopts = "UT" - revision = "6a3e2ff9e7c564f36873c2e36413f634534f1c44" - version = "v0.2.1" - -[[projects]] - branch = "master" - digest = "1:fbbce3ff584585118f3c270df893dea88eb3e7ddbece46ea322410af9ae27829" - name = "github.com/xlab/treeprint" - packages = ["."] - pruneopts = "UT" - revision = "a009c3971eca89777614839eb7f69abed3ea3959" - -[[projects]] - branch = "master" - digest = "1:b2faf2256a00b05784390fbd8ea4b22519ea62daf751fef4ab4ce583c66abf1b" - name = "github.com/xordataexchange/crypt" - packages = [ - "backend", - "backend/consul", - "backend/etcd", - "config", - "encoding/secconf", - ] - pruneopts = "UT" - revision = "b2862e3d0a775f18c7cfe02273500ae307b61218" - -[[projects]] - branch = "master" - digest = "1:2d4cab5a9ea650aa99394d7e768e280f0cfc1d0b7c0fa08f2447391ab1eb5480" - name = "golang.org/x/crypto" - packages = [ - "cast5", - "curve25519", - "ed25519", - "ed25519/internal/edwards25519", - "internal/chacha20", - "internal/subtle", - "openpgp", - "openpgp/armor", - "openpgp/elgamal", - "openpgp/errors", - "openpgp/packet", - "openpgp/s2k", - "poly1305", - "ssh", - "ssh/agent", - "ssh/knownhosts", - "ssh/terminal", - ] - pruneopts = "UT" - revision = "4def268fd1a49955bfb3dda92fe3db4f924f2285" - -[[projects]] - branch = "master" - digest = "1:a7e913ba6e6a12039af83d8f10cd5e478e53e0cf3682e7b5e6f907c96927d67a" - name = "golang.org/x/net" - packages = [ - "context", - "html", - "html/atom", - "html/charset", - "http/httpguts", - "http2", - "http2/hpack", - "idna", - "internal/socks", - "internal/timeseries", - "proxy", - "trace", - ] - pruneopts = "UT" - revision = "ca1201d0de80cfde86cb01aea620983605dfe99b" - -[[projects]] - branch = "master" - digest = "1:afce8593ac5918df05a46499b04b8c4e46a91e99566055e4a8ba829a8f389a09" - name = "golang.org/x/sys" - packages = [ - "cpu", - "unix", - "windows", - ] - pruneopts = "UT" - revision = "51ab0e2deafac1f46c46ad59cf0921be2f180c3d" - -[[projects]] - digest = "1:28deae5fe892797ff37a317b5bcda96d11d1c90dadd89f1337651df3bc4c586e" - name = "golang.org/x/text" - packages = [ - "collate", - "collate/build", - "encoding", - "encoding/charmap", - "encoding/htmlindex", - "encoding/internal", - "encoding/internal/identifier", - "encoding/japanese", - "encoding/korean", - "encoding/simplifiedchinese", - "encoding/traditionalchinese", - "encoding/unicode", - "internal/colltab", - "internal/gen", - "internal/language", - "internal/language/compact", - "internal/tag", - "internal/triegen", - "internal/ucd", - "internal/utf8internal", - "language", - "runes", - "secure/bidirule", - "transform", - "unicode/bidi", - "unicode/cldr", - "unicode/norm", - "unicode/rangetable", - ] - pruneopts = "UT" - revision = "342b2e1fbaa52c93f31447ad2c6abc048c63e475" - version = "v0.3.2" - -[[projects]] - branch = "master" - digest = "1:583a0c80f5e3a9343d33aea4aead1e1afcc0043db66fdf961ddd1fe8cd3a4faf" - name = "google.golang.org/genproto" - packages = ["googleapis/rpc/status"] - pruneopts = "UT" - revision = "fa694d86fc64c7654a660f8908de4e879866748d" - -[[projects]] - digest = "1:89eba2efa76562459d64d23c595954cfb3531b05a2970ca94253b6dc316a8e71" - name = "google.golang.org/grpc" - packages = [ - ".", - "balancer", - "balancer/base", - "balancer/roundrobin", - "binarylog/grpc_binarylog_v1", - "codes", - "connectivity", - "credentials", - "credentials/internal", - "encoding", - "encoding/proto", - "grpclog", - "internal", - "internal/backoff", - "internal/balancerload", - "internal/binarylog", - "internal/channelz", - "internal/envconfig", - "internal/grpcrand", - "internal/grpcsync", - "internal/syscall", - "internal/transport", - "keepalive", - "metadata", - "naming", - "peer", - "reflection", - "reflection/grpc_reflection_v1alpha", - "resolver", - "resolver/dns", - "resolver/passthrough", - "serviceconfig", - "stats", - "status", - "tap", - ] - pruneopts = "UT" - revision = "045159ad57f3781d409358e3ade910a018c16b30" - version = "v1.22.1" - -[[projects]] - digest = "1:abeb38ade3f32a92943e5be54f55ed6d6e3b6602761d74b4aab4c9dd45c18abd" - name = "gopkg.in/fsnotify.v1" - packages = ["."] - pruneopts = "UT" - revision = "c2828203cd70a50dcccfb2761f8b1f8ceef9a8e9" - source = "gopkg.in/fsnotify/fsnotify.v1" - version = "v1.4.7" - -[[projects]] - digest = "1:eb27cfcaf8d7e4155224dd0a209f1d0ab19784fef01be142638b78b7b6becd6b" - name = "gopkg.in/src-d/go-billy.v4" - packages = [ - ".", - "helper/chroot", - "helper/polyfill", - "osfs", - "util", - ] - pruneopts = "UT" - revision = "780403cfc1bc95ff4d07e7b26db40a6186c5326e" - version = "v4.3.2" - -[[projects]] - digest = "1:b2ad0a18676cd4d5b4b180709c1ea34dbabd74b3d7db0cc01e6d287d5f1e3a99" - name = "gopkg.in/src-d/go-git.v4" - packages = [ - ".", - "config", - "internal/revision", - "internal/url", - "plumbing", - "plumbing/cache", - "plumbing/filemode", - "plumbing/format/config", - "plumbing/format/diff", - "plumbing/format/gitignore", - "plumbing/format/idxfile", - "plumbing/format/index", - "plumbing/format/objfile", - "plumbing/format/packfile", - "plumbing/format/pktline", - "plumbing/object", - "plumbing/protocol/packp", - "plumbing/protocol/packp/capability", - "plumbing/protocol/packp/sideband", - "plumbing/revlist", - "plumbing/storer", - "plumbing/transport", - "plumbing/transport/client", - "plumbing/transport/file", - "plumbing/transport/git", - "plumbing/transport/http", - "plumbing/transport/internal/common", - "plumbing/transport/server", - "plumbing/transport/ssh", - "storage", - "storage/filesystem", - "storage/filesystem/dotgit", - "storage/memory", - "utils/binary", - "utils/diff", - "utils/ioutil", - "utils/merkletrie", - "utils/merkletrie/filesystem", - "utils/merkletrie/index", - "utils/merkletrie/internal/frame", - "utils/merkletrie/noder", - ] - pruneopts = "UT" - revision = "0d1a009cbb604db18be960db5f1525b99a55d727" - version = "v4.13.1" - -[[projects]] - branch = "v1" - digest = "1:0caa92e17bc0b65a98c63e5bc76a9e844cd5e56493f8fdbb28fad101a16254d9" - name = "gopkg.in/tomb.v1" - packages = ["."] - pruneopts = "UT" - revision = "dd632973f1e7218eb1089048e0798ec9ae7dceb8" - -[[projects]] - digest = "1:78d374b493e747afa9fbb2119687e3740a7fb8d0ebabddfef0a012593aaecbb3" - name = "gopkg.in/warnings.v0" - packages = ["."] - pruneopts = "UT" - revision = "ec4a0fea49c7b46c2aeb0b51aac55779c607e52b" - version = "v0.1.2" - -[[projects]] - digest = "1:4d2e5a73dc1500038e504a8d78b986630e3626dc027bc030ba5c75da257cdb96" - name = "gopkg.in/yaml.v2" - packages = ["."] - pruneopts = "UT" - revision = "51d6538a90f86fe93ac480b35f37b2be17fef232" - version = "v2.2.2" - -[solve-meta] - analyzer-name = "dep" - analyzer-version = 1 - input-imports = [ - "github.com/briandowns/spinner", - "github.com/fatih/color", - "github.com/gdamore/tcell", - "github.com/gobwas/glob", - "github.com/gogo/protobuf/proto", - "github.com/golang/protobuf/proto", - "github.com/google/uuid", - "github.com/hashicorp/consul/api", - "github.com/jinzhu/copier", - "github.com/k0kubun/pp", - "github.com/looplab/fsm", - "github.com/mesos/mesos-go/api/v1/cmd", - "github.com/mesos/mesos-go/api/v1/lib", - "github.com/mesos/mesos-go/api/v1/lib/backoff", - "github.com/mesos/mesos-go/api/v1/lib/encoding", - "github.com/mesos/mesos-go/api/v1/lib/encoding/codecs", - "github.com/mesos/mesos-go/api/v1/lib/executor", - "github.com/mesos/mesos-go/api/v1/lib/executor/calls", - "github.com/mesos/mesos-go/api/v1/lib/executor/config", - "github.com/mesos/mesos-go/api/v1/lib/executor/events", - "github.com/mesos/mesos-go/api/v1/lib/extras/metrics", - "github.com/mesos/mesos-go/api/v1/lib/extras/scheduler/callrules", - "github.com/mesos/mesos-go/api/v1/lib/extras/scheduler/controller", - "github.com/mesos/mesos-go/api/v1/lib/extras/scheduler/eventrules", - "github.com/mesos/mesos-go/api/v1/lib/extras/store", - "github.com/mesos/mesos-go/api/v1/lib/httpcli", - "github.com/mesos/mesos-go/api/v1/lib/httpcli/httpexec", - "github.com/mesos/mesos-go/api/v1/lib/httpcli/httpsched", - "github.com/mesos/mesos-go/api/v1/lib/resources", - "github.com/mesos/mesos-go/api/v1/lib/scheduler", - "github.com/mesos/mesos-go/api/v1/lib/scheduler/calls", - "github.com/mesos/mesos-go/api/v1/lib/scheduler/events", - "github.com/mitchellh/go-homedir", - "github.com/naoina/toml", - "github.com/olekukonko/tablewriter", - "github.com/onsi/ginkgo", - "github.com/onsi/gomega", - "github.com/pborman/uuid", - "github.com/prometheus/client_golang/prometheus", - "github.com/prometheus/client_golang/prometheus/promhttp", - "github.com/rivo/tview", - "github.com/sirupsen/logrus", - "github.com/spf13/cobra", - "github.com/spf13/cobra/doc", - "github.com/spf13/pflag", - "github.com/spf13/viper", - "github.com/spf13/viper/remote", - "github.com/teo/logrus-prefixed-formatter", - "github.com/xlab/treeprint", - "golang.org/x/net/context", - "golang.org/x/sys/unix", - "google.golang.org/grpc", - "google.golang.org/grpc/codes", - "google.golang.org/grpc/reflection", - "google.golang.org/grpc/status", - "gopkg.in/src-d/go-git.v4", - "gopkg.in/src-d/go-git.v4/plumbing", - "gopkg.in/yaml.v2", - ] - solver-name = "gps-cdcl" - solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml deleted file mode 100644 index af8be30cc..000000000 --- a/Gopkg.toml +++ /dev/null @@ -1,61 +0,0 @@ -# Gopkg.toml example -# -# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md -# for detailed Gopkg.toml documentation. -# -# required = ["github.com/user/thing/cmd/thing"] -# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] -# -# [[constraint]] -# name = "github.com/user/project" -# version = "1.0.0" -# -# [[constraint]] -# name = "github.com/user/project2" -# branch = "dev" -# source = "github.com/myfork/project2" -# -# [[override]] -# name = "github.com/x/y" -# version = "2.4.0" -[prune] - unused-packages = true - go-tests = true - -[[constraint]] - branch = "master" - name = "github.com/looplab/fsm" - -[[constraint]] - name = "github.com/mesos/mesos-go" - branch = "master" - -[[constraint]] - name = "github.com/pborman/uuid" - version = "1.2.0" - -[[constraint]] - name = "github.com/sirupsen/logrus" - version = "1.2.0" - -[[constraint]] - name = "github.com/teo/logrus-prefixed-formatter" - branch = "master" - -[[override]] - name = "gopkg.in/fsnotify.v1" - source = "gopkg.in/fsnotify/fsnotify.v1" - -# Workaround for https://github.com/rivo/tview/issues/205 -[[override]] - name = "github.com/mattn/go-runewidth" - version = "v0.0.4" - -#[[override]] -# name = "github.com/cpuguy83/go-md2man" -# branch = "master" - -[[override]] - name = "github.com/russross/blackfriday" -# branch = "master" - version = "v1.5.2" diff --git a/Makefile b/Makefile index dab12206c..88a426438 100644 --- a/Makefile +++ b/Makefile @@ -1,9 +1,9 @@ # === This file is part of ALICE O² === # -# Copyright 2017-2018 CERN and copyright holders of ALICE O². +# Copyright 2017-2023 CERN and copyright holders of ALICE O². # Author: Teo Mrnjavac # -# Based on Torus project Makefile +# Partially derived from Torus Makefile # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -28,120 +28,309 @@ BUILD := `git rev-parse --short HEAD` ROOT_DIR:=$(shell dirname $(realpath $(lastword $(MAKEFILE_LIST)))) +MINIMUM_SUPPORTED_GO_MAJOR_VERSION = 1 +MINIMUM_SUPPORTED_GO_MINOR_VERSION = 20 + +GO_MAJOR_VERSION = $(shell go version | cut -c 14- | cut -d' ' -f1 | cut -d'.' -f1) +GO_MINOR_VERSION = $(shell go version | cut -c 14- | cut -d' ' -f1 | cut -d'.' -f2) +WHICH_GO = $(shell which go) +GO_VERSION_VALIDATION_ERR_MSG = Go version $(GO_MAJOR_VERSION).$(GO_MINOR_VERSION) at $(WHICH_GO) is not supported, please update to at least $(MINIMUM_SUPPORTED_GO_MAJOR_VERSION).$(MINIMUM_SUPPORTED_GO_MINOR_VERSION) + HOST_GOOS=$(shell go env GOOS) HOST_GOARCH=$(shell go env GOARCH) +CGO_LDFLAGS= +BUILD_FLAGS=$(BUILD_ENV_FLAGS) +ifdef WITH_LIBINFOLOGGER CGO_LDFLAGS=CGO_LDFLAGS="$(ROOT_DIR)/vendor/infoLoggerForGo/infoLoggerForGo.a -static-libstdc++ -static-libgcc" BUILD_FLAGS=$(CGO_LDFLAGS) $(BUILD_ENV_FLAGS) +endif REPOPATH = github.com/AliceO2Group/Control +DCS_PROTO="https://gitlab.cern.ch/api/v4/projects/137621/repository/files/proto%2Fdcs.proto/raw?ref=master" +ODC_PROTO="https://raw.githubusercontent.com/FairRootGroup/ODC/master/odc/grpc/odc.proto" +DD_PROTO="https://raw.githubusercontent.com/AliceO2Group/DataDistribution/master/src/DataDistControl/DataDistControl.proto" +BK_COM_PROTO="https://raw.githubusercontent.com/AliceO2Group/Bookkeeping/main/proto/common.proto" +BK_ENV_PROTO="https://raw.githubusercontent.com/AliceO2Group/Bookkeeping/main/proto/environment.proto" +BK_FLP_PROTO="https://raw.githubusercontent.com/AliceO2Group/Bookkeeping/main/proto/flp.proto" +BK_LOG_PROTO="https://raw.githubusercontent.com/AliceO2Group/Bookkeeping/main/proto/log.proto" +BK_RUN_PROTO="https://raw.githubusercontent.com/AliceO2Group/Bookkeeping/main/proto/run.proto" +BK_LHCFILL_PROTO="https://raw.githubusercontent.com/AliceO2Group/Bookkeeping/main/proto/lhcFill.proto" VERBOSE_1 := -v VERBOSE_2 := -v -x -WHAT := o2control-core o2control-executor coconut peanut -WHAT_o2control-core_BUILD_FLAGS=$(CGO_LDFLAGS) $(BUILD_ENV_FLAGS) -WHAT_o2control-executor_BUILD_FLAGS=$(CGO_LDFLAGS) $(BUILD_ENV_FLAGS) -WHAT_coconut_BUILD_FLAGS=$(CGO_LDFLAGS) $(BUILD_ENV_FLAGS) -WHAT_peanut_BUILD_FLAGS=$(CGO_LDFLAGS) $(BUILD_ENV_FLAGS) +WHAT := o2-aliecs-core o2-aliecs-executor coconut peanut o2-apricot +WHAT_o2-aliecs-core_BUILD_FLAGS=$(BUILD_ENV_FLAGS) +WHAT_o2-aliecs-executor_BUILD_FLAGS=$(BUILD_ENV_FLAGS) +WHAT_coconut_BUILD_FLAGS=$(BUILD_ENV_FLAGS) +WHAT_peanut_BUILD_FLAGS=$(BUILD_ENV_FLAGS) +WHAT_o2-apricot_BUILD_FLAGS=$(BUILD_ENV_FLAGS) INSTALL_WHAT:=$(patsubst %, install_%, $(WHAT)) +GENERATE_DIRS := ./apricot ./coconut/cmd ./common ./common/runtype ./common/system ./core ./core/integration/ccdb ./core/integration/dcs ./core/integration/ddsched ./core/integration/kafka ./core/integration/odc ./executor ./core/integration/trg ./core/integration/bookkeeping +SRC_DIRS := ./apricot ./cmd/* ./core ./coconut ./executor ./common ./configuration ./occ/peanut +TEST_DIRS := ./apricot/local ./common/gera ./common/utils ./common/utils/safeacks ./configuration/cfgbackend ./configuration/componentcfg ./configuration/template ./core/task/sm ./core/workflow ./core/integration/odc/fairmq ./core/integration/ccdb ./core/integration ./core/environment +GO_TEST_DIRS := ./core/repos ./core/integration/dcs ./common/monitoring -GENERATE_DIRS := ./core ./executor ./coconut/cmd -SRC_DIRS := ./cmd/* ./core ./coconut ./executor ./common ./configuration ./occ/peanut +coverage:COVERAGE_PREFIX := ./coverage_results +coverage:GOTEST_COVERAGE_FILE := $(COVERAGE_PREFIX)/gotest.out +coverage:GOTEST_COVERAGE_FILE_HTML := $(COVERAGE_PREFIX)/gotest.html # Use linker flags to provide version/build settings to the target PROD :=-X=$(REPOPATH)/common/product -LDFLAGS=-ldflags "$(PROD).VERSION_MAJOR=$(VERSION_MAJOR) $(PROD).VERSION_MINOR=$(VERSION_MINOR) $(PROD).VERSION_PATCH=$(VERSION_PATCH) $(PROD).BUILD=$(BUILD)" -HAS_GOGOPROTO := $(shell command -v protoc-gen-gofast 2> /dev/null) +EXTLDFLAGS :="-static" +LDFLAGS=-ldflags "-extldflags $(EXTLDFLAGS) $(PROD).VERSION_MAJOR=$(VERSION_MAJOR) $(PROD).VERSION_MINOR=$(VERSION_MINOR) $(PROD).VERSION_PATCH=$(VERSION_PATCH) $(PROD).BUILD=$(BUILD)" -tags osusergo,netgo + +# To invoke debug build you need to pass DEBUG variable with any value, eg: DEBUG=1 make WHAT=... +ifdef DEBUG + GCFLAGS += -gcflags="all=-N -l" +endif + +# We expect to find the protoc-gen-go executable in $GOPATH/bin +GOPATH := $(shell go env GOPATH) +GOPROTOCPATH=$(ROOT_DIR)/tools/protoc-gen-go +HAS_PROTOC := $(shell command -v $(GOPROTOCPATH) 2> /dev/null) -GO_GET_U1 := $(addprefix github.com/gogo/protobuf/, proto protoc-gen-gofast protoc-gen-gogofast protoc-gen-gogofaster protoc-gen-gogoslick gogoproto) -GO_GET_U2 := $(addprefix github.com/golang/protobuf/, proto protoc-gen-go) -GO_GET_U2 += google.golang.org/grpc +.EXPORT_ALL_VARIABLES: +CGO_ENABLED = 0 -.PHONY: build all install generate test debugtest vet fmt clean cleanall help $(WHAT) tools vendor +.PHONY: build all install generate test debugtest vet fmt clean cleanall help $(WHAT) tools vendor doc docs fdset build: $(WHAT) +racedetector: validate-go-version + @for target in $(WHAT); do \ + echo -e "\033[1;33mCGO_ENABLED=1 go build -race -mod=vendor\033[0m ./cmd/$$target \033[1;33m==>\033[0m \033[1;34m./bin/$$target\033[0m"; \ + $(WHAT_$$target_BUILD_FLAGS) CGO_ENABLED=1 go build -race -mod=vendor $(VERBOSE_$(V)) -o bin/$$target $(LDFLAGS) ./cmd/$$target; \ + done + all: vendor generate build +validate-go-version: ## Validates the installed version of go against AliECS minimum requirement. + @if [ $(GO_MAJOR_VERSION) -gt $(MINIMUM_SUPPORTED_GO_MAJOR_VERSION) ]; then \ + exit 0 ;\ + elif [ $(GO_MAJOR_VERSION) -lt $(MINIMUM_SUPPORTED_GO_MAJOR_VERSION) ]; then \ + echo '$(GO_VERSION_VALIDATION_ERR_MSG)';\ + exit 1; \ + elif [ $(GO_MINOR_VERSION) -lt $(MINIMUM_SUPPORTED_GO_MINOR_VERSION) ] ; then \ + echo '$(GO_VERSION_VALIDATION_ERR_MSG)';\ + exit 1; \ + fi + install: $(INSTALL_WHAT) # @for w in $(WHAT); do \ # FLAGS="WHAT_$${w}_BUILD_FLAGS"; \ # echo -e "${$${FLAGS}}"; \ -# echo -e "\e[1;33mgo install\e[0m ./cmd/$$w \e[1;33m==>\e[0m \e[1;34m$$GOPATH/bin/$$w\e[0m"; \ +# echo -e "\033[1;33mgo install\033[0m ./cmd/$$w \033[1;33m==>\033[0m \033[1;34m$$GOPATH/bin/$$w\033[0m"; \ # $(WHAT_$${w}_BUILD_FLAGS) go install $(VERBOSE_$(V)) $(LDFLAGS) ./cmd/$$w; \ # done -$(WHAT): +$(WHAT): validate-go-version # @echo -e "WHAT_$@_BUILD_FLAGS $(WHAT_$@_BUILD_FLAGS)" - @echo -e "\e[1;33m$(WHAT_$@_BUILD_FLAGS) go build\e[0m ./cmd/$@ \e[1;33m==>\e[0m \e[1;34m./bin/$@\e[0m" + @echo -e "\033[1;33mgo build -mod=vendor\033[0m ./cmd/$@ \033[1;33m==>\033[0m \033[1;34m./bin/$@\033[0m" # @echo ${PWD} - @$(WHAT_$@_BUILD_FLAGS) go build $(VERBOSE_$(V)) -o bin/$@ $(LDFLAGS) ./cmd/$@ + @$(WHAT_$@_BUILD_FLAGS) go build -mod=vendor $(VERBOSE_$(V)) $(GCFLAGS) -o bin/$@ $(LDFLAGS) ./cmd/$@ -$(INSTALL_WHAT): +# special case: if the current WHAT is o2-aliecs-executor, also copy over the shmcleaner script + @if [ $@ = "o2-aliecs-executor" ]; then \ + echo -e "\033[1;33mcopy\033[0m ./o2-aliecs-shmcleaner \033[1;33m==>\033[0m \033[1;34m./bin/o2-aliecs-shmcleaner\033[0m"; \ + cp o2-aliecs-shmcleaner bin/o2-aliecs-shmcleaner; \ + chmod +x bin/o2-aliecs-shmcleaner; \ + fi; \ + +$(INSTALL_WHAT): validate-go-version # @echo -e "WHAT_$(@:install_%=%)_BUILD_FLAGS $(WHAT_$(@:install_%=%)_BUILD_FLAGS)" - @echo -e "\e[1;33m$(WHAT_$(@:install_%=%)_BUILD_FLAGS) go install\e[0m ./cmd/$(@:install_%=%) \e[1;33m==>\e[0m \e[1;34m$$GOPATH/bin/$(@:install_%=%)\e[0m" + @echo -e "\033[1;33mgo install -mod=vendor\033[0m ./cmd/$(@:install_%=%) \033[1;33m==>\033[0m \033[1;34m$$GOPATH/bin/$(@:install_%=%)\033[0m" # @echo ${PWD} - @$(WHAT_$(@:install_%=%)_BUILD_FLAGS) go install $(VERBOSE_$(V)) $(LDFLAGS) ./cmd/$(@:install_%=%) + @$(WHAT_$(@:install_%=%)_BUILD_FLAGS) go install -mod=vendor $(VERBOSE_$(V)) $(LDFLAGS) ./cmd/$(@:install_%=%) + +# special case: if the current WHAT is o2-aliecs-executor, also copy over the shmcleaner script + @if [ $@ = "install_o2-aliecs-executor" ]; then \ + echo -e "\033[1;33minstall\033[0m ./o2-aliecs-shmcleaner \033[1;33m==>\033[0m \033[1;34m$$GOPATH/bin/o2-aliecs-shmcleaner\033[0m"; \ + cp o2-aliecs-shmcleaner $${GOPATH}/bin/o2-aliecs-shmcleaner; \ + chmod +x $${GOPATH}/bin/o2-aliecs-shmcleaner; \ + fi; \ generate: -ifndef HAS_GOGOPROTO +ifndef HAS_PROTOC $(MAKE) tools/protoc endif + @if [ -z "$(PROTOBUF_VERSION)" ]; then \ + echo "Warning: PROTOBUF_VERSION env var not detected are you sure you are generating from alidist environment?"; \ + fi @for gendir in $(GENERATE_DIRS); do \ - echo -e "\e[1;33mgo generate\e[0m $$gendir"; \ - go generate $(VERBOSE_$(V)) $$gendir; \ + echo -e "\033[1;33mgo generate\033[0m $$gendir"; \ + PATH="$(ROOT_DIR)/tools:$$PATH" go generate $(VERBOSE_$(V)) $$gendir; \ done -test: tools/dep - $(BUILD_FLAGS) go test -v --race $(SRC_DIRS) -ginkgo.progress +test: + @echo -e "[Ginkgo] \033[1;33mgo test -v\033[0m $(TEST_DIRS)\033[0m" + @$(BUILD_FLAGS) go test -v $(TEST_DIRS) -ginkgo.show-node-events + + @echo -e "\n[gotest] \033[1;33mgo test -v\033[0m $(GO_TEST_DIRS)\033[0m" + @$(BUILD_FLAGS) go test -v $(GO_TEST_DIRS) -debugtest: tools/dep - $(BUILD_FLAGS) go test -v --race $(SRC_DIRS) -ginkgo.v -ginkgo.trace -ginkgo.progress +coverage: + mkdir -p $(COVERAGE_PREFIX) -vet: tools/dep + # -ginkgo.show-node-events is not necessary for coverage as it only increases verbosity of logs + @echo -e "\n[gotest] \033[1;33mgo test -coverprofile=$(GOTEST_COVERAGE_FILE) -v\033[0m $(TEST_DIRS) $(GO_TEST_DIRS)\033[0m" + @$(BUILD_FLAGS) go test -coverprofile=$(GOTEST_COVERAGE_FILE) -v $(TEST_DIRS) $(GO_TEST_DIRS) + @echo -e "\n[gotest] \033[1;33mgo tool cover -html=$(GOTEST_COVERAGE_FILE) -o $(GOTEST_COVERAGE_FILE_HTML)\033[0m" + @go tool cover -html=$(GOTEST_COVERAGE_FILE) -o $(GOTEST_COVERAGE_FILE_HTML) + +debugtest: + @echo -e "[Ginkgo] \033[1;33mgo test -v\033[0m $(TEST_DIRS)\033[0m" + @$(BUILD_FLAGS) go test -v $(TEST_DIRS) -ginkgo.v -ginkgo.trace -ginkgo.show-node-events + + @echo -e "\n[gotest] \033[1;33mgo test -v\033[0m $(GO_TEST_DIRS)\033[0m" + @$(BUILD_FLAGS) go test -v $(GO_TEST_DIRS) + +vet: go vet $(SRC_DIRS) -fmt: tools/dep +fmt: go fmt $(SRC_DIRS) clean: @rm -rf ./bin/* - @echo -e "clean done: \e[1;34mbin\e[0m" + @echo -e "clean done: \033[1;34mbin\033[0m" cleanall: @rm -rf bin tools vendor - @echo -e "clean done: \e[1;34mbin tools vendor\e[0m" + @echo -e "clean done: \033[1;34mbin tools vendor\033[0m" + +vendor: validate-go-version + @echo -e "\033[1;33mgo mod vendor\033[0m" + @go mod vendor + +# curl --header "PRIVATE-TOKEN: " "https://gitlab.example.com/api/v4/projects/13083/repository/files/scripts%2Frun.sh/raw?ref=master" + @echo -e "\033[1;33mcurl dcs.proto\033[0m" + @mkdir -p core/integration/dcs/protos + @curl --header "PRIVATE-TOKEN: glpat-z-r8s2Mfte8GBpLn7vcE" -s -L $(DCS_PROTO) -o core/integration/dcs/protos/dcs.proto + +# FIXME: these two protofiles should be committed in order to be available offline + @echo -e "\033[1;33mcurl odc.proto\033[0m" + @mkdir -p core/integration/odc/protos + @curl -s -L $(ODC_PROTO) -o core/integration/odc/protos/odc.proto + + @echo -e "\033[1;33mcurl ddsched.proto\033[0m" + @mkdir -p core/integration/ddsched/protos + @curl -s -L $(DD_PROTO) -o core/integration/ddsched/protos/ddsched.proto + + @echo -e "\033[1;33mcurl bkcommon.proto\033[0m" + @mkdir -p core/integration/bookkeeping/protos + @curl -s -L $(BK_COM_PROTO) -o core/integration/bookkeeping/protos/bkcommon.proto + + @echo -e "\033[1;33mcurl environment.proto\033[0m" + @mkdir -p core/integration/bookkeeping/protos + @curl -s -L $(BK_ENV_PROTO) -o core/integration/bookkeeping/protos/environment.proto + + @echo -e "\033[1;33mcurl flp.proto\033[0m" + @mkdir -p core/integration/bookkeeping/protos + @curl -s -L $(BK_FLP_PROTO) -o core/integration/bookkeeping/protos/flp.proto + + @echo -e "\033[1;33mcurl log.proto\033[0m" + @mkdir -p core/integration/bookkeeping/protos + @curl -s -L $(BK_LOG_PROTO) -o core/integration/bookkeeping/protos/log.proto + + @echo -e "\033[1;33mcurl run.proto\033[0m" + @mkdir -p core/integration/bookkeeping/protos + @curl -s -L $(BK_RUN_PROTO) -o core/integration/bookkeeping/protos/run.proto -vendor: tools/dep - @echo -e "\e[1;33mdep ensure\e[0m" - @./tools/dep ensure - @mkdir -p vendor/infoLoggerForGo - @cp ${INFOLOGGER_ROOT}/lib/infoLoggerForGo.* vendor/infoLoggerForGo/ + @echo -e "\033[1;33mcurl lhcFill.proto\033[0m" + @mkdir -p core/integration/bookkeeping/protos + @curl -s -L $(BK_LHCFILL_PROTO) -o core/integration/bookkeeping/protos/lhcFill.proto + +# WORKAROUND: In order to avoid the following issues: +# https://github.com/golang/protobuf/issues/992 +# https://github.com/golang/protobuf/issues/1158 +# we insert a go_package specification into the ODC protofile right +# after we download it. + @echo -e "\033[1;33mpatch odc.proto\033[0m" + @perl -pi -e '$$_.="option go_package = \"github.com/AliceO2Group/Control/core/integration/odc/protos;odc\";\n" if (/^package/)' core/integration/odc/protos/odc.proto + + @echo -e "\033[1;33mpatch ddsched.proto\033[0m" + @perl -pi -e '$$_.="option go_package = \"github.com/AliceO2Group/Control/core/integration/ddsched/protos;ddpb\";\n" if (/^package/)' core/integration/ddsched/protos/ddsched.proto + + @echo -e "\033[1;33mpatch common.proto\033[0m" + @perl -pi -e '$$_.="option go_package = \"github.com/AliceO2Group/Control/core/integration/bookkeeping/protos;bkpb\";\n" if (/^package/)' core/integration/bookkeeping/protos/bkcommon.proto + + @echo -e "\033[1;33mpatch environment.proto\033[0m" + @perl -pi -e '$$_.="option go_package = \"github.com/AliceO2Group/Control/core/integration/bookkeeping/protos;bkpb\";\n" if (/^package/)' core/integration/bookkeeping/protos/environment.proto + + @echo -e "\033[1;33mpatch flp.proto\033[0m" + @perl -pi -e '$$_.="option go_package = \"github.com/AliceO2Group/Control/core/integration/bookkeeping/protos;bkpb\";\n" if (/^package/)' core/integration/bookkeeping/protos/flp.proto + + @echo -e "\033[1;33mpatch log.proto\033[0m" + @perl -pi -e '$$_.="option go_package = \"github.com/AliceO2Group/Control/core/integration/bookkeeping/protos;bkpb\";\n" if (/^package/)' core/integration/bookkeeping/protos/log.proto + + @echo -e "\033[1;33mpatch run.proto\033[0m" + @perl -pi -e '$$_.="option go_package = \"github.com/AliceO2Group/Control/core/integration/bookkeeping/protos;bkpb\";\n" if (/^package/)' core/integration/bookkeeping/protos/run.proto + + @echo -e "\033[1;33mpatch lhcFill.proto\033[0m" + @perl -pi -e '$$_.="option go_package = \"github.com/AliceO2Group/Control/core/integration/bookkeeping/protos;bkpb\";\n" if (/^package/)' core/integration/bookkeeping/protos/lhcFill.proto + + @echo -e "\033[1;33mpatch environment.proto\033[0m" + @perl -pi -e 's/.*/import \"protos\/bkcommon\.proto\";/ if (/^import/)' core/integration/bookkeeping/protos/environment.proto + + @echo -e "\033[1;33mpatch flp.proto\033[0m" + @perl -pi -e 's/.*/import \"protos\/bkcommon\.proto\";/ if (/^import/)' core/integration/bookkeeping/protos/flp.proto + + @echo -e "\033[1;33mpatch log.proto\033[0m" + @perl -pi -e 's/.*/import \"protos\/bkcommon\.proto\";/ if (/^import/)' core/integration/bookkeeping/protos/log.proto + + @echo -e "\033[1;33mpatch run.proto\033[0m" + @perl -pi -e 's/.*/import \"protos\/bkcommon\.proto\";/ if (/^import/)' core/integration/bookkeeping/protos/run.proto + + @echo -e "\033[1;33mpatch lhcFill.proto\033[0m" + @perl -pi -e 's/.*/import \"protos\/bkcommon\.proto\";/ if (/^import/)' core/integration/bookkeeping/protos/lhcFill.proto + +# @echo -e "\033[1;33mdep ensure\033[0m" +# @./tools/dep ensure + +# @mkdir -p vendor/infoLoggerForGo +# @cp ${INFOLOGGER_ROOT}/lib/infoLoggerForGo.* vendor/infoLoggerForGo/ # For cgo, *.a means C, so by default it will use gcc when linking against it. For # this reason, we must create a dummy *.cpp file in the package dir to force cgo to # use g++ instead of gcc. - @touch vendor/infoLoggerForGo/infoLoggerForGo.cpp +# @touch vendor/infoLoggerForGo/infoLoggerForGo.cpp -tools: tools/dep tools/protoc - -tools/dep: - @echo "downloading dep" - mkdir -p tools - curl -L https://github.com/golang/dep/releases/download/v0.5.1/dep-$(HOST_GOOS)-$(HOST_GOARCH) -o tools/dep - chmod +x tools/dep +tools: tools/protoc tools/protoc: - @echo "installing Go protoc" - go get -u $(GO_GET_U1) - go get -u $(GO_GET_U2) + @echo "installing Protobuf tools" + + @export GOBIN="$(ROOT_DIR)/tools" && cat common/tools/tools.go | grep _ | awk -F'"' '{print $$2}' | xargs -tI % go install -mod=readonly % + +docs: docs/coconut docs/grpc docs/swaggo + +fdset: + @echo -e "building fdset files \033[1;33m==>\033[0m \033[1;34m./common/protos\033[0m" + + @mkdir -p fdset + @cd common/protos && protoc -I.. -I. -o common.fdset common.proto && protoc -I.. -I. -o events.fdset events.proto && cd ../.. + @mv common/protos/*.fdset fdset + + @echo -e "to consume with \033[1;33mhttps://github.com/sevagh/pq\033[0m: FDSET_PATH=./fdset pq kafka aliecs.environment --brokers kafka-broker-hostname:9092 --beginning --msgtype events.Event" + +docs/coconut: + @echo -e "generating coconut documentation \033[1;33m==>\033[0m \033[1;34m./coconut/doc\033[0m" + @cd coconut/doc && go run . && cd ../.. + +docs/grpc: + @echo -e "generating gRPC API documentation \033[1;33m==>\033[0m \033[1;34m./docs\033[0m" + @cd apricot/protos && PATH="$(ROOT_DIR)/tools:$$PATH" protoc --doc_out="$(ROOT_DIR)/docs" --doc_opt=markdown,apidocs_apricot.md "apricot.proto" + @cd core/protos && PATH="$(ROOT_DIR)/tools:$$PATH" protoc -I=. -I=../../common --doc_out="$(ROOT_DIR)/docs" --experimental_allow_proto3_optional --doc_opt=markdown,apidocs_aliecs.md o2control.proto ../../common/protos/events.proto ../../common/protos/common.proto + @cd occ/protos && PATH="$(ROOT_DIR)/tools:$$PATH" protoc --doc_out="$(ROOT_DIR)/docs" --doc_opt=markdown,apidocs_occ.md "occ.proto" -doc: - @echo -e "generating coconut documentation \e[1;33m==>\e[0m \e[1;34m./coconut/doc\e[0m" - @cd coconut/doc && go run . +docs/swaggo: + @echo -e "generating REST API documentation \033[1;33m==>\033[0m \033[1;34m./apricot/docs\033[0m" + @tools/swag fmt -d apricot + @tools/swag init -o apricot/docs -d apricot/local,apricot,cmd/o2-apricot -g servicehttp.go help: @echo "available make variables:" @echo " V - Build verbosity {0,1,2}." @echo " BUILD_ENV_FLAGS - Environment added to 'go build'." - @echo " WHAT - Command to build. (e.g. WHAT=o2control-core)" + @echo " WHAT - Command to build. (e.g. WHAT=o2-aliecs-core)" diff --git a/README.md b/README.md index 33d3ec6cd..2d1b3d87a 100644 --- a/README.md +++ b/README.md @@ -2,46 +2,201 @@ [![godoc](https://img.shields.io/badge/godoc-Reference-5272B4.svg)](https://godoc.org/github.com/AliceO2Group/Control) # AliECS -The ALICE Experiment Control System +The ALICE Experiment Control System (**AliECS**) is the piece of software to drive and control data taking activities in the experiment. +It is a distributed system that combines state of the art cluster resource management and experiment control functionalities into a single comprehensive solution. -## Install instructions +Please refer to the [CHEP 2023 paper](https://doi.org/10.1051/epjconf/202429502027) for the latest design overview. -What is your use case? +## How to get started -* I want to run AliECS (and other O²/FLP software) on a **single machine** +Regardless of your particular interests, it is recommended to get acquainted with the main [AliECS concepts](docs/handbook/concepts.md). - :arrow_right: [Single node O²/FLP software deployment instructions](https://gitlab.cern.ch/AliceO2Group/system-configuration/blob/master/ansible/docs/O2_INSTALL_FLP_STANDALONE.md) +After that, please find your concrete use case: -* I want to run AliECS (and other O²/FLP software) in a **multi-node setup** +### I want to **run AliECS** and other O²/FLP software - :construction: No user instructions yet - contact [alice-o2-flp-support](mailto:alice-o2-flp-support@cern.ch) for assistance with provisioning and deployment. - -* I want to ensure AliECS can **run and control my process** +See [O²/FLP Suite deployment instructions](https://alice-flp.docs.cern.ch/system-configuration/utils/o2-flp-setup/) - * My software is based on FairMQ and/or O² DPL - - :palm_tree: Nothing to do, AliECS natively supports FairMQ (and DPL) devices. - - * My software does not use FairMQ and/or DPL, but should be controlled through a state machine - - :telescope: See [the OCC documentation](occ/README.md) to learn how to integrate the O² Control and Configuration library with your software. [Readout](https://github.com/AliceO2Group/Readout) is currently the only example of this setup. - -* I want to build and run AliECS for **development** purposes +These instructions apply to both single-node and multi-node deployments. +Contact [alice-o2-flp-support](mailto:alice-o2-flp-support@cern.ch) for assistance with provisioning and deployment. - :hammer_and_wrench: [Building instructions](hacking/BUILDING.md) - - :arrow_right: [Running instructions](hacking/RUNNING.md) +There are two ways of interacting with AliECS: -## Using AliECS +- The AliECS GUI (a.k.a. Control GUI, COG) - not in this repository, but included in most deployments, recommended -There are two ways of interacting with AliECS: - -* The AliECS GUI - not in this repository, but included in most deployments, recommended + :arrow_right: [AliECS GUI documentation](hacking/COG.md) + +- `coconut` - the command-line control and configuration utility, included with AliECS core, typically for developers and advanced users + + :arrow_right: [Using `coconut`](https://alice-flp.docs.cern.ch/aliecs/coconut/) - :arrow_right: [AliECS GUI documentation](hacking/COG.md) + :arrow_right: [`coconut` command reference](https://alice-flp.docs.cern.ch/aliecs/coconut/doc/coconut/) -* `coconut` - the command-line control and configuration utility, included with AliECS core +### I want to ensure AliECS can **run and control my process** - :arrow_right: [Using `coconut`](coconut/README.md) +* **My software is based on FairMQ and/or O² DPL (Data Processing Layer)** + + AliECS natively supports FairMQ (and DPL) devices. + Head to [ControlWorkflows](https://github.com/AliceO2Group/ControlWorkflows) for instructions on how to configure your software to be controlled by AliECS. + +* **My software does not use FairMQ and/or DPL, but should be controlled through a state machine** + + See [the OCC documentation](occ/README.md) to learn how to integrate the O² Control and Configuration library with your software. [Readout](https://github.com/AliceO2Group/Readout) is an example of this setup. + + Once ready, head to [ControlWorkflows](https://github.com/AliceO2Group/ControlWorkflows) for instructions on how to configure it to be controlled by AliECS. + +* **My software is a command line utility with no state machine** + + AliECS natively supports generic commands. + Head to [ControlWorkflows](https://github.com/AliceO2Group/ControlWorkflows) for instructions to have your command ran by AliECS. + Make sure the task template for your command sets the control mode to `basic` ([see example](https://github.com/AliceO2Group/ControlWorkflows/blob/master/tasks/o2-roc-cleanup.yaml)). - :arrow_right: [`coconut` command reference](coconut/doc/coconut.md) +### I want to develop AliECS + +:hammer_and_wrench: Welcome to the team, please head to [contributing instructions](/docs/CONTRIBUTING.md) + +### I want to receive updates about environments or services controlled by AliECS + +:pager: Learn more about the [kafka event service](/docs/kafka.md) + +### I want my application to send requests to AliECS + +:scroll: See the API docs of AliECS components: + +- [core gRPC server](/docs/apidocs_aliecs.md) +- [apricot gRPC server](/docs/apidocs_apricot.md) +- [apricot HTTP server](/apricot/docs/apricot_http_service.md) + +### I want my service to be sent requests by AliECS + +:electric_plug: Learn more about the [plugin system](/core/integration/README.md) + +## Table of Contents + +* Introduction + * [Basic Concepts](/docs/handbook/concepts.md#basic-concepts) + * [Tasks](/docs/handbook/concepts.md#tasks) + * [Workflows, roles and environments](/docs/handbook/concepts.md#workflows-roles-and-environments) + * [Design Overview](/docs/handbook/overview.md#design-overview) + * [AliECS Structure](/docs/handbook/overview.md#aliecs-structure) + * [Resource Management](/docs/handbook/overview.md#resource-management) + * [FairMQ](/docs/handbook/overview.md#fairmq) + * [State machines](/docs/handbook/overview.md#state-machines) + +* Component reference + * AliECS GUI + * [AliECS GUI overview](/hacking/COG.md) + * AliECS core + * [Workflow Configuration](/docs/handbook/configuration.md#workflow-configuration) + * [The AliECS workflow template language](/docs/handbook/configuration.md#the-aliecs-workflow-template-language) + * [Workflow template structure](/docs/handbook/configuration.md#workflow-template-structure) + * [Task roles](/docs/handbook/configuration.md#task-roles) + * [Call roles](/docs/handbook/configuration.md#call-roles) + * [Aggregator roles](/docs/handbook/configuration.md#aggregator-roles) + * [Iterator roles](/docs/handbook/configuration.md#iterator-roles) + * [Include roles](/docs/handbook/configuration.md#include-roles) + * [Template expressions](/docs/handbook/configuration.md#template-expressions) + * [Task Configuration](/docs/handbook/configuration.md#task-configuration) + * [Task template structure](/docs/handbook/configuration.md#task-template-structure) + * [Variables pushed to controlled tasks](/docs/handbook/configuration.md#variables-pushed-to-controlled-tasks) + * [Resource wants and limits](/docs/handbook/configuration.md#resource-wants-and-limits) + * [EPN workflow generation](/docs/handbook/configuration.md#epn-workflow-generation) + * [Integration plugins](/core/integration/README.md#integration-plugins) + * [Plugin system overview](/core/integration/README.md#plugin-system-overview) + * [Integrated service operations](/core/integration/README.md#integrated-service-operations) + * [Bookkeeping](/core/integration/README.md#bookkeeping) + * [CCDB](/core/integration/README.md#ccdb) + * [DCS](/core/integration/README.md#dcs) + * [DCS operations](/core/integration/README.md#dcs-operations) + * [DCS PrepareForRun behaviour](/core/integration/README.md#dcs-prepareforrun-behaviour) + * [DCS StartOfRun behaviour](/core/integration/README.md#dcs-startofrun-behaviour) + * [DCS EndOfRun behaviour](/core/integration/README.md#dcs-endofrun-behaviour) + * [ECS2DCS2ECS mock server](/core/integration/README.md#ecs2dcs2ecs-mock-server) + * [DD Scheduler](/core/integration/README.md#dd-scheduler) + * [Kafka (legacy)](/core/integration/README.md#kafka-legacy) + * [LHC](/core/integration/README.md) + * [ODC](/core/integration/README.md#odc) + * [Test plugin](/core/integration/README.md#test-plugin) + * [Trigger](/core/integration/README.md#trigger) + * [Environment operation order](/docs/handbook/operation_order.md#environment-operation-order) + * [State machine triggers](/docs/handbook/operation_order.md#state-machine-triggers) + * [START_ACTIVITY (Start Of Run)](/docs/handbook/operation_order.md#start_activity-start-of-run) + * [STOP_ACTIVITY (End Of Run)](/docs/handbook/operation_order.md#stop_activity-end-of-run) + * [Virtual states and transitions](/docs/handbook/operation_order.md#virtual-states-and-transitions) + * [Protocol documentation](/docs/apidocs_aliecs.md) + * coconut + * [The O² control and configuration utility overview](/coconut/README.md#the-o-control-and-configuration-utility-overview) + * [Configuration file](/coconut/README.md#configuration-file) + * [Using coconut](/coconut/README.md#using-coconut) + * [Creating an environment](/coconut/README.md#creating-an-environment) + * [Controlling an environment](/coconut/README.md#controlling-an-environment) + * [Command reference](/coconut/doc/coconut.md) + * apricot + * [ALICE configuration service overview](/apricot/README.md#alice-configuration-service-overview) + * [HTTP service](/apricot/docs/apricot_http_service.md#apricot-http-service) + * [Configuration](/apricot/docs/apricot_http_service.md#configuration) + * [Usage and options](/apricot/docs/apricot_http_service.md#usage-and-options) + * [Examples](/apricot/docs/apricot_http_service.md#examples) + * [Protocol documentation](/docs/apidocs_apricot.md) + * [Command reference](/apricot/docs/apricot.md) + * occ + * [O² Control and Configuration Components](/occ/README.md#o-control-and-configuration-components) + * [Developer quick start instructions for OCClib](/occ/README.md#developer-quick-start-instructions-for-occlib) + * [Manual build instructions](/occ/README.md#manual-build-instructions) + * [Run example](/occ/README.md#run-example) + * [The OCC state machine](/occ/README.md#the-occ-state-machine) + * [Single process control with peanut](/occ/README.md#single-process-control-with-peanut) + * [OCC API debugging with grpcc](/occ/README.md#occ-api-debugging-with-grpcc) + * [Dummy process example for OCC library](/occ/occlib/examples/dummy-process/README.md#dummy-process-example-for-occ-library) + * [Protocol documentation](/docs/apidocs_occ.md) + * peanut + * [Process control and execution utility overview](/occ/peanut/README.md) + * Event service + * [Kafka producer functionality in AliECS core](/docs/kafka.md#kafka-producer-functionality-in-aliecs-core) + * [Making sure that AliECS sends messages](/docs/kafka.md#making-sure-that-aliecs-sends-messages) + * [Currently available topics](/docs/kafka.md#currently-available-topics) + * [Decoding the messages](/docs/kafka.md#decoding-the-messages) + * [Legacy events: Kafka plugin](/docs/kafka.md#legacy-events-kafka-plugin) + * [Making sure that AliECS sends messages](/docs/kafka.md#making-sure-that-aliecs-sends-messages-1) + * [Currently available topics](/docs/kafka.md#currently-available-topics-1) + * [Decoding the messages](/docs/kafka.md#decoding-the-messages-1) + * [Getting Start of Run and End of Run notifications](/docs/kafka.md#getting-start-of-run-and-end-of-run-notifications) + * [Using Kafka debug tools](/docs/kafka.md#using-kafka-debug-tools) + +* Developer documentation + * [Contributing](/docs/CONTRIBUTING.md) + * [Package pkg.go.dev documentation](https://pkg.go.dev/github.com/AliceO2Group/Control) + * [Building AliECS](/docs/building.md#building-aliecs) + * [Overview](/docs/building.md#overview) + * [Building with aliBuild](/docs/building.md#building-with-alibuild) + * [Manual build](/docs/building.md#manual-build) + * [Go environment](/docs/building.md#go-environment) + * [Clone and build (Go components only)](/docs/building.md#clone-and-build-go-components-only) + * [Makefile reference](/docs/makefile_reference.md) + * [Component Configuration](/docs/handbook/appconfiguration.md#component-configuration) + * [Apache Mesos](/docs/handbook/appconfiguration.md#apache-mesos) + * [Connectivity to controlled nodes](/docs/handbook/appconfiguration.md#connectivity-to-controlled-nodes) + * [Running AliECS as a developer](/docs/running.md#running-aliecs-as-a-developer) + * [Running the AliECS core](/docs/running.md#running-the-aliecs-core) + * [Running AliECS in production](/docs/running.md#running-aliecs-in-production) + * [Health checks](/docs/running.md#health-checks) + * [Development Information](/docs/development.md#development-information) + * [Release Procedure](/docs/development.md#release-procedure) + * [Metrics in ECS](/docs/metrics.md#metrics-in-ecs) + * [Overview and simple usage](/docs/metrics.md#overview-and-simple-usage) + * [Types and aggregation of metrics](/docs/metrics.md#types-and-aggregation-of-metrics) + * [Metric types](/docs/metrics.md#metric-types) + * [Aggregation](/docs/metrics.md#aggregation) + * [Implementation details](/docs/metrics.md#implementation-details) + * [Event loop](/docs/metrics.md#event-loop) + * [Hashing to aggregate](/docs/metrics.md#hashing-to-aggregate) + * [Sampling reservoir](/docs/metrics.md#sampling-reservoir) + * [OCC API debugging with grpcc](/docs/using_grpcc_occ.md#occ-api-debugging-with-grpcc) + * [Running tasks inside docker](/docs/running_docker.md#running-a-task-inside-a-docker-container) + * Kubernetes + * [Operator controller](/control-operator/README.md) + * [Testing manifests](/control-operator/ecs-manifests/kubernetes-ecs.md) + * [ECS bridge to Kubernetes](/docs/kubernetes_ecs.md) +* Resources + * T. Mrnjavac et. al, [AliECS: A New Experiment Control System for the ALICE Experiment](https://doi.org/10.1051/epjconf/202429502027), CHEP23 + diff --git a/VERSION b/VERSION index ebd6a6c6a..f1343ff45 100644 --- a/VERSION +++ b/VERSION @@ -1,5 +1,4 @@ # GNU Make syntax -VERSION_MAJOR := 0 -VERSION_MINOR := 10 +VERSION_MAJOR := 1 +VERSION_MINOR := 51 VERSION_PATCH := 0 - diff --git a/apricot/README.md b/apricot/README.md new file mode 100644 index 000000000..f3a63b6e5 --- /dev/null +++ b/apricot/README.md @@ -0,0 +1,10 @@ +# ALICE configuration service overview + +**A** **p**rocessor and **r**epos**i**tory for **co**nfiguration **t**emplates, or apricot, implements the configuration service for the ALICE data taking activities. +It adds templating, load balancing and caching on top of the configuration store. + +See also: + +* [apricot HTTP service](docs/apricot_http_service.md) - make essential cluster information available via a web server +* Protofile: [apricot.proto](protos/apricot.proto) +* [Command reference](docs/apricot.md) diff --git a/apricot/apricot.go b/apricot/apricot.go new file mode 100644 index 000000000..e9a517984 --- /dev/null +++ b/apricot/apricot.go @@ -0,0 +1,79 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2021 CERN and copyright holders of ALICE O². + * Author: Teo Mrnjavac + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +// Package apricot implements the ALICE configuration service with templating, +// load balancing and caching capabilities on top of the configuration store. +package apricot + +import ( + "fmt" + "net" + + "github.com/AliceO2Group/Control/apricot/local" + "github.com/AliceO2Group/Control/apricot/remote" + "github.com/AliceO2Group/Control/common/logger" + "github.com/AliceO2Group/Control/common/logger/infologger" + "github.com/AliceO2Group/Control/common/product" + "github.com/sirupsen/logrus" + "github.com/spf13/viper" +) + +//go:generate protoc --go_out=. --go_opt=paths=source_relative --go-grpc_opt=paths=source_relative --go-grpc_out=require_unimplemented_servers=false:. protos/apricot.proto + +var log = logger.New(logrus.StandardLogger(), "apricot") + +func Run() (err error) { + verbose := false + if viper.GetBool("verbose") { + logrus.SetLevel(logrus.DebugLevel) + verbose = true + } + log.WithField("level", infologger.IL_Support). + Infof("AliECS Configuration Service (apricot v%s build %s) starting up", product.VERSION, product.BUILD) + if verbose { + log.WithField("level", infologger.IL_Support). + Infof("AliECS Configuration Service running with verbose logging") + } + + s := remote.NewServer(Instance()) + httpsvr := local.NewHttpService(instance) + signals(s, httpsvr) // handle UNIX signals + + var lis net.Listener + lis, err = net.Listen("tcp", fmt.Sprintf(":%d", viper.GetInt("listenPort"))) + if err != nil { + log.WithField("error", err). + WithField("port", viper.GetInt("listenPort")). + Fatal("net.Listener failed to listen") + return + } + + log.WithField("port", viper.GetInt("listenPort")). + WithField("level", infologger.IL_Support). + Info("gRPC service started") + if err = s.Serve(lis); err != nil { + log.WithField("error", err).Fatal("gRPC server failed to serve") + } + return +} diff --git a/apricot/cacheproxy/service.go b/apricot/cacheproxy/service.go new file mode 100644 index 000000000..78c2c1d92 --- /dev/null +++ b/apricot/cacheproxy/service.go @@ -0,0 +1,160 @@ +// Package cacheproxy provides a caching proxy implementation for the +// configuration service, optimizing detector inventory lookups. +package cacheproxy + +import ( + "github.com/AliceO2Group/Control/configuration" + "github.com/AliceO2Group/Control/configuration/componentcfg" +) + +// Implements a cache proxy Service for the configuration system. +// Only DetectorForHost/DetectorsForHosts are cached, all other calls are passed through. +type Service struct { + base configuration.Service + cache svcCache +} + +type svcCache struct { + detectorsInventory map[string][]string + detectorForHost map[string]string +} + +func NewService(base configuration.Service) (*Service, error) { + svc := &Service{ + base: base, + cache: svcCache{ + detectorsInventory: make(map[string][]string), + detectorForHost: make(map[string]string), + }, + } + + var err error + svc.cache.detectorsInventory, err = svc.base.GetDetectorsInventory() + if err != nil { + return nil, err + } + + for det, hosts := range svc.cache.detectorsInventory { + for _, host := range hosts { + svc.cache.detectorForHost[host] = det + } + } + + return svc, nil +} + +func (s Service) GetRuntimeEntry(component string, key string) (string, error) { + return s.base.GetRuntimeEntry(component, key) +} + +func (s Service) SetRuntimeEntry(component string, key string, value string) error { + return s.base.SetRuntimeEntry(component, key, value) +} + +func (s Service) GetRuntimeEntries(component string) (map[string]string, error) { + return s.base.GetRuntimeEntries(component) +} + +func (s Service) ListRuntimeEntries(component string) ([]string, error) { + return s.base.ListRuntimeEntries(component) +} + +func (s Service) NewRunNumber() (runNumber uint32, err error) { + return s.base.NewRunNumber() +} + +func (s Service) GetDefaults() map[string]string { + return s.base.GetDefaults() +} + +func (s Service) GetVars() map[string]string { + return s.base.GetVars() +} + +func (s Service) GetComponentConfiguration(query *componentcfg.Query) (payload string, err error) { + return s.base.GetComponentConfiguration(query) +} + +func (s Service) GetComponentConfigurationWithLastIndex(query *componentcfg.Query) (payload string, lastIndex uint64, err error) { + return s.base.GetComponentConfigurationWithLastIndex(query) +} + +func (s Service) GetAndProcessComponentConfiguration(query *componentcfg.Query, varStack map[string]string) (payload string, err error) { + return s.base.GetAndProcessComponentConfiguration(query, varStack) +} + +func (s Service) ListDetectors(getAll bool) (detectors []string, err error) { + return s.base.ListDetectors(getAll) +} + +func (s Service) GetHostInventory(detector string) (hosts []string, err error) { + return s.base.GetHostInventory(detector) +} + +func (s Service) GetDetectorsInventory() (inventory map[string][]string, err error) { + return s.base.GetDetectorsInventory() +} + +func (s Service) ListComponents() (components []string, err error) { + return s.base.ListComponents() +} + +func (s Service) ListComponentEntries(query *componentcfg.EntriesQuery) (entries []string, err error) { + return s.base.ListComponentEntries(query) +} + +func (s Service) ResolveComponentQuery(query *componentcfg.Query) (resolved *componentcfg.Query, err error) { + return s.base.ResolveComponentQuery(query) +} + +func (s Service) ImportComponentConfiguration(query *componentcfg.Query, payload string, newComponent bool) (existingComponentUpdated bool, existingEntryUpdated bool, err error) { + return s.base.ImportComponentConfiguration(query, payload, newComponent) +} + +func (s Service) GetDetectorForHost(hostname string) (string, error) { + det, ok := s.cache.detectorForHost[hostname] + if !ok { + return s.base.GetDetectorForHost(hostname) + } + return det, nil +} + +func (s Service) GetDetectorsForHosts(hosts []string) ([]string, error) { + detectors := make(map[string]struct{}, 0) + for _, host := range hosts { + det, ok := s.cache.detectorForHost[host] + if !ok { + return s.base.GetDetectorsForHosts(hosts) + } + detectors[det] = struct{}{} + } + detList := make([]string, 0, len(detectors)) + for det := range detectors { + detList = append(detList, det) + } + return detList, nil +} + +func (s Service) GetCRUCardsForHost(hostname string) ([]string, error) { + return s.base.GetCRUCardsForHost(hostname) +} + +func (s Service) GetEndpointsForCRUCard(hostname, cardSerial string) ([]string, error) { + return s.base.GetEndpointsForCRUCard(hostname, cardSerial) +} + +func (s Service) GetLinkIDsForCRUEndpoint(host string, cruId string, endpoint string, onlyEnabled bool) (ids []string, err error) { + return s.base.GetLinkIDsForCRUEndpoint(host, cruId, endpoint, onlyEnabled) +} + +func (s Service) GetAliasedLinkIDsForDetector(detector string, onlyEnabled bool) ([]string, error) { + return s.base.GetAliasedLinkIDsForDetector(detector, onlyEnabled) +} + +func (s Service) RawGetRecursive(path string) (string, error) { + return s.base.RawGetRecursive(path) +} + +func (s Service) InvalidateComponentTemplateCache() { + s.base.InvalidateComponentTemplateCache() +} diff --git a/apricot/config.go b/apricot/config.go new file mode 100644 index 000000000..f6f3c9456 --- /dev/null +++ b/apricot/config.go @@ -0,0 +1,105 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2017-2018 CERN and copyright holders of ALICE O². + * Author: Teo Mrnjavac + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package apricot + +import ( + "errors" + "path/filepath" + + "github.com/sirupsen/logrus" + "github.com/spf13/pflag" + "github.com/spf13/viper" + "golang.org/x/sys/unix" +) + +func setDefaults() error { + viper.Set("component", "apricot") + + viper.SetDefault("listenPort", 32101) + viper.SetDefault("httpListenPort", 32188) + viper.SetDefault("backendUri", "consul://127.0.0.1:8500") + viper.SetDefault("runtimeBasePath", "o2/runtime") + viper.SetDefault("workingDir", "/var/lib/o2/apricot") + viper.SetDefault("verbose", false) + viper.SetDefault("trimSpaceInVarsFromConsulKV", true) + return nil +} + +func setFlags() error { + pflag.Int("listenPort", viper.GetInt("listenPort"), "Port of apricot server") + pflag.Int("httpListenPort", viper.GetInt("listenPort"), "Port of apricot http server") + pflag.String("backendUri", viper.GetString("backendUri"), "URI of the Consul server or YAML configuration file") + pflag.Bool("verbose", viper.GetBool("verbose"), "Verbose logging") + pflag.Bool("trimSpaceInVarsFromConsulKV", viper.GetBool("trimSpaceInVarsFromConsulKV"), "When true, the variables imported from the Consul KV are trimmed if the contain whitespaces") + pflag.String("workingDir", viper.GetString("workingDir"), "Working directory for apricot") + + pflag.Parse() + return viper.BindPFlags(pflag.CommandLine) +} + +func checkWorkingDirRights() error { + err := unix.Access(viper.GetString("workingDir"), unix.W_OK) + if err != nil { + return errors.New("No write access for core working path \"" + viper.GetString("workingDir") + "\": " + err.Error()) + } + return nil +} + +// Remove trailing '/' +func sanitizeWorkingPath() { + sanitizeWorkingPath := filepath.Clean(viper.GetString("workingDir")) + viper.Set("workingDir", sanitizeWorkingPath) +} + +// Bind environment variables with the prefix APRICOT +// e.g. APRICOT_LISTENPORT +func bindEnvironmentVariables() { + viper.SetEnvPrefix("APRICOT") + viper.AutomaticEnv() +} + +// NewConfig is the constructor for a new config. +func NewConfig() (err error) { + if err = setDefaults(); err != nil { + return + } + if err = setFlags(); err != nil { + return + } + bindEnvironmentVariables() + sanitizeWorkingPath() + if err = checkWorkingDirRights(); err != nil { + return + } + + if viper.GetBool("verbose") { + logrus.SetLevel(logrus.DebugLevel) + } + + // Trigger apricot backend setup + // this must happen after viper is ready + Instance() + return +} diff --git a/apricot/docs/apricot-apidocs-screenshot.png b/apricot/docs/apricot-apidocs-screenshot.png new file mode 100644 index 000000000..43194d572 Binary files /dev/null and b/apricot/docs/apricot-apidocs-screenshot.png differ diff --git a/apricot/docs/apricot.md b/apricot/docs/apricot.md new file mode 100644 index 000000000..43fd6027b --- /dev/null +++ b/apricot/docs/apricot.md @@ -0,0 +1,16 @@ +## apricot + +**A** **p**rocessor and **r**epos**i**tory for **co**nfiguration **t**emplates + +### Synopsis + +The `o2-apricot` binary implements a centralized configuration (micro)service for ALICE O². + +### Usage and options + +``` +Usage of bin/o2-apricot: + --backendUri string URI of the Consul server or YAML configuration file (default "consul://127.0.0.1:8500") + --listenPort int Port of apricot server (default 32101) + --verbose Verbose logging +``` \ No newline at end of file diff --git a/apricot/docs/apricot_http_service.md b/apricot/docs/apricot_http_service.md new file mode 100644 index 000000000..fd4609736 --- /dev/null +++ b/apricot/docs/apricot_http_service.md @@ -0,0 +1,49 @@ +## apricot HTTP service + +Web server component that implements the REST Configuration Endpoint (Apricot RCE). + +It serves JSON and/or plain text structures in order to make essential cluster information and component configuration available to scripts and other consumers for which the gRPC interface is impractical. + +It is strictly **read-only** and only ever responds to `GET` (except for the special purpose `POST` call `/components/_invalidate_cache`, called only by Consul). + +### Configuration + +To use this feature, the `o2-apricot` running machine has to communicate with Consul via the `--backendUri` option (see [here](apricot.md)). + +### Usage and options + +The default port is `32188`. + +To retrieve the information needed regarding FLPs, use the following urls in a web browser or with `curl` or `wget`. + +To retrieve as plain text: + +* `http://:/inventory/flps` or `http://:/inventory/flps/text` +* `http://:/inventory/detectors//flps` or `http://:/inventory/detectors//flps/text` + +To retrieve as JSON: + +* `http://:/inventory/flps/json` +* `http://:/inventory/detectors//flps/json` + +To retrieve a component configuration payload, use the following urls in a web browser or with `curl` or `wget`. + +* `http://:/components////?process=true` - with template processing +* `http://:/components////?process=false` - without template processing, returns the entry verbatim + +The full API documentation is available at `http://:/docs/` wherever your Apricot instance is running. It looks like this: + +![Apricot API documentation screenshot](apricot-apidocs-screenshot.png) + +This documentation interface also allows to perform API calls directly from the browser. +Besides configuration retrieval, the API also includes calls for browsing the configuration tree and resolving payload paths to actual entries according to the `ANY/any` mechanism. + +### Examples + +* With `curl`: `curl http://localhost:32188/inventory/flps` +* With `wget`: `wget http://localhost:32188/inventory/detectors/TST/flps/json -O ~/downloads/test` + +Getting a template-processed configuration payload for a component (entry `tpc-full-qcmn` for component `qc`, with `list_of_detectors` and `run_type` passed as template variables): + +* In a browser: `http://localhost:32188/components/qc/ANY/any/tpc-full-qcmn?process=true&list_of_detectors=tpc,its&run_type=PHYSICS` +* With `curl`: `curl http://127.0.0.1:32188/components/qc/ANY/any/tpc-full-qcmn\?process\=true\&list_of_detectors\=tpc,its\&run_type\=PHYSICS` diff --git a/apricot/docs/docs.go b/apricot/docs/docs.go new file mode 100644 index 000000000..66d99957e --- /dev/null +++ b/apricot/docs/docs.go @@ -0,0 +1,545 @@ +// Package docs Code generated by swaggo/swag. DO NOT EDIT +package docs + +import "github.com/swaggo/swag" + +const docTemplate = `{ + "schemes": {{ marshal .Schemes }}, + "swagger": "2.0", + "info": { + "description": "{{escape .Description}}", + "title": "{{.Title}}", + "contact": { + "name": "O² FLP support", + "url": "https://alice-flp.docs.cern.ch/", + "email": "alice-o2-flp-support@cern.ch" + }, + "version": "{{.Version}}" + }, + "host": "{{.Host}}", + "basePath": "{{.BasePath}}", + "paths": { + "/components": { + "get": { + "description": "Returns a list of all configuration components managed by Apricot", + "produces": [ + "application/json", + "text/plain" + ], + "tags": [ + "component configuration" + ], + "summary": "List Apricot-managed configuration components", + "parameters": [ + { + "enum": [ + "json", + "text" + ], + "type": "string", + "default": "text", + "description": "Output format, json or text", + "name": "format", + "in": "query" + } + ], + "responses": { + "200": { + "description": "List of components, either as JSON array or comma-separated plain text", + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "500": { + "description": "Internal server error", + "schema": { + "type": "string" + } + } + } + } + }, + "/components/_invalidate_cache": { + "post": { + "description": "Invalidates all cached templates for all configuration components managed by Apricot", + "produces": [ + "text/plain" + ], + "tags": [ + "component configuration" + ], + "summary": "Clear cache of Apricot-managed configuration component templates", + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/components/{component}": { + "get": { + "description": "Returns a list of all entries belonging to a given configuration component corresponding to the given query. The component must be provided. If the run type is not provided, entries for all run types are returned. If the role name is not provided, entries for all role names are returned. If the raw flag is true, AApricot will not attempt to include \"ANY\"/\"any\" run types or role names, and will return only the entries stored under the provided Consul path. If the raw flag is false, Apricot will include all entries for the given component, run type and role name, including entries stored under \"ANY\" and \"any\" paths corresponding to fallback values valid for all run types or role names.", + "produces": [ + "application/json", + "text/plain" + ], + "tags": [ + "component configuration" + ], + "summary": "Lists all entries for an Apricot-managed configuration component", + "parameters": [ + { + "enum": [ + "json", + "text" + ], + "type": "string", + "default": "text", + "description": "Output format, json or text", + "name": "format", + "in": "query" + }, + { + "type": "boolean", + "default": false, + "description": "If true, the query returns strictly Consul paths without catching all run types or role names with ANY/any", + "name": "raw", + "in": "query" + }, + { + "type": "string", + "description": "Configuration component", + "name": "component", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "List of entries, either as JSON array or comma-separated plain text", + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "400": { + "description": "Bad request, if the run type is invalid", + "schema": { + "type": "string" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "type": "string" + } + } + } + } + }, + "/components/{component}/{runtype}": { + "get": { + "description": "Returns a list of all entries belonging to a given configuration component corresponding to the given query. The component must be provided. If the run type is not provided, entries for all run types are returned. If the role name is not provided, entries for all role names are returned. If the raw flag is true, AApricot will not attempt to include \"ANY\"/\"any\" run types or role names, and will return only the entries stored under the provided Consul path. If the raw flag is false, Apricot will include all entries for the given component, run type and role name, including entries stored under \"ANY\" and \"any\" paths corresponding to fallback values valid for all run types or role names.", + "produces": [ + "application/json", + "text/plain" + ], + "tags": [ + "component configuration" + ], + "summary": "Lists all entries for an Apricot-managed configuration component", + "parameters": [ + { + "enum": [ + "json", + "text" + ], + "type": "string", + "default": "text", + "description": "Output format, json or text", + "name": "format", + "in": "query" + }, + { + "type": "boolean", + "default": false, + "description": "If true, the query returns strictly Consul paths without catching all run types or role names with ANY/any", + "name": "raw", + "in": "query" + }, + { + "type": "string", + "description": "Configuration component", + "name": "component", + "in": "path", + "required": true + }, + { + "type": "string", + "default": "ANY", + "description": "O² Run type, must be capitalized", + "name": "runtype", + "in": "path" + } + ], + "responses": { + "200": { + "description": "List of entries, either as JSON array or comma-separated plain text", + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "400": { + "description": "Bad request, if the run type is invalid", + "schema": { + "type": "string" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "type": "string" + } + } + } + } + }, + "/components/{component}/{runtype}/{rolename}": { + "get": { + "description": "Returns a list of all entries belonging to a given configuration component corresponding to the given query. The component must be provided. If the run type is not provided, entries for all run types are returned. If the role name is not provided, entries for all role names are returned. If the raw flag is true, AApricot will not attempt to include \"ANY\"/\"any\" run types or role names, and will return only the entries stored under the provided Consul path. If the raw flag is false, Apricot will include all entries for the given component, run type and role name, including entries stored under \"ANY\" and \"any\" paths corresponding to fallback values valid for all run types or role names.", + "produces": [ + "application/json", + "text/plain" + ], + "tags": [ + "component configuration" + ], + "summary": "Lists all entries for an Apricot-managed configuration component", + "parameters": [ + { + "enum": [ + "json", + "text" + ], + "type": "string", + "default": "text", + "description": "Output format, json or text", + "name": "format", + "in": "query" + }, + { + "type": "boolean", + "default": false, + "description": "If true, the query returns strictly Consul paths without catching all run types or role names with ANY/any", + "name": "raw", + "in": "query" + }, + { + "type": "string", + "description": "Configuration component", + "name": "component", + "in": "path", + "required": true + }, + { + "type": "string", + "default": "ANY", + "description": "O² Run type, must be capitalized", + "name": "runtype", + "in": "path" + }, + { + "type": "string", + "default": "any", + "description": "Role name", + "name": "rolename", + "in": "path" + } + ], + "responses": { + "200": { + "description": "List of entries, either as JSON array or comma-separated plain text", + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "400": { + "description": "Bad request, if the run type is invalid", + "schema": { + "type": "string" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "type": "string" + } + } + } + } + }, + "/components/{component}/{runtype}/{rolename}/{entry}": { + "get": { + "description": "The provided component, run type, role name and entry key are used to query the configuration service for a configuration entry, which is then processed in the O² Apricot template system to produce the final payload, and returned as string..", + "produces": [ + "text/plain" + ], + "tags": [ + "component configuration" + ], + "summary": "Returns a configuration payload for a given component, run type, role name and entry key", + "parameters": [ + { + "type": "boolean", + "default": false, + "description": "If true, template processing is performed to produce the final payload; if false, the entry is returned verbatim. In the true case, any number of additional string key-value pairs may be passed as query parameters (e.g. ?process=true\u0026mykey1=myvalue1\u0026mykey2=myvalue2), which are then fed into the template system as variables that affect configuration payload generation", + "name": "process", + "in": "query" + }, + { + "type": "string", + "description": "Configuration component", + "name": "component", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "O² Run type, must be capitalized", + "name": "runtype", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Role name", + "name": "rolename", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Entry key", + "name": "entry", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "Configuration payload for the queried entry, either processed or verbatim depending on the process flag", + "schema": { + "type": "string" + } + }, + "400": { + "description": "Bad request, if a parameter is invalid", + "schema": { + "type": "string" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "type": "string" + } + } + } + } + }, + "/components/{component}/{runtype}/{rolename}/{entry}/resolve": { + "get": { + "description": "Returns a resolved path for a given component, run type, role name and entry key. The path points to an actual existing entry in Consul, resolving ANY run type and any rolename wildcards.", + "produces": [ + "text/plain" + ], + "tags": [ + "component configuration" + ], + "summary": "Resolves a query for a given component, run type, role name and entry key", + "parameters": [ + { + "type": "string", + "description": "Configuration component", + "name": "component", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "O² Run type, must be capitalized", + "name": "runtype", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Role name", + "name": "rolename", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Entry key", + "name": "entry", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "Resolved path for the queried entry", + "schema": { + "type": "string" + } + }, + "400": { + "description": "Bad request, if a parameter is invalid", + "schema": { + "type": "string" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "type": "string" + } + } + } + } + }, + "/inventory/detectors/{detector}/flps/{format}": { + "get": { + "description": "Returns the list of all Apricot-managed hosts in the cluster that are known to be FLPs and serving the given detector, newline-separated or JSON depending on the format parameter", + "produces": [ + "text/plain" + ], + "tags": [ + "cluster inventory" + ], + "summary": "Returns the list of FLPs in the cluster that serve a given detector", + "parameters": [ + { + "enum": [ + "json", + "text" + ], + "type": "string", + "default": "text", + "description": "Output format, json or text", + "name": "format", + "in": "path" + } + ], + "responses": { + "200": { + "description": "List of FLPs in the cluster known to Apricot, newline-separated or JSON depending on the format parameter", + "schema": { + "type": "string" + } + } + } + } + }, + "/inventory/detectors/{format}": { + "get": { + "description": "Returns the list of all detectors known to Apricot that belong to the installed instance, newline-separated or JSON depending on the format parameter", + "produces": [ + "text/plain" + ], + "tags": [ + "cluster inventory" + ], + "summary": "Returns the list of detectors belonging to the installed instance known to Apricot", + "parameters": [ + { + "enum": [ + "json", + "text" + ], + "type": "string", + "default": "text", + "description": "Output format, json or text", + "name": "format", + "in": "path" + } + ], + "responses": { + "200": { + "description": "List of detectors in the instance known to Apricot, newline-separated or JSON depending on the format parameter", + "schema": { + "type": "string" + } + } + } + } + }, + "/inventory/flps/{format}": { + "get": { + "description": "Returns the list of all Apricot-managed hosts in the cluster that are known to be FLPs, newline-separated or JSON depending on the format parameter", + "produces": [ + "text/plain" + ], + "tags": [ + "cluster inventory" + ], + "summary": "Returns the list of FLPs in the cluster known to Apricot", + "parameters": [ + { + "enum": [ + "json", + "text" + ], + "type": "string", + "default": "text", + "description": "Output format, json or text", + "name": "format", + "in": "path" + } + ], + "responses": { + "200": { + "description": "List of FLPs in the cluster known to Apricot, newline-separated or JSON depending on the format parameter", + "schema": { + "type": "string" + } + } + } + } + } + }, + "externalDocs": { + "description": "AliECS handbook", + "url": "https://alice-flp.docs.cern.ch/aliecs/handbook/" + } +}` + +// SwaggerInfo holds exported Swagger Info so clients can modify it +var SwaggerInfo = &swag.Spec{ + Version: "1.0", + Host: "", + BasePath: "", + Schemes: []string{}, + Title: "O² Apricot REST API", + Description: "REST API for ALICE O² Apricot configuration service", + InfoInstanceName: "swagger", + SwaggerTemplate: docTemplate, + LeftDelim: "{{", + RightDelim: "}}", +} + +func init() { + swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo) +} diff --git a/apricot/docs/swagger.json b/apricot/docs/swagger.json new file mode 100644 index 000000000..df4d73e81 --- /dev/null +++ b/apricot/docs/swagger.json @@ -0,0 +1,519 @@ +{ + "swagger": "2.0", + "info": { + "description": "REST API for ALICE O² Apricot configuration service", + "title": "O² Apricot REST API", + "contact": { + "name": "O² FLP support", + "url": "https://alice-flp.docs.cern.ch/", + "email": "alice-o2-flp-support@cern.ch" + }, + "version": "1.0" + }, + "paths": { + "/components": { + "get": { + "description": "Returns a list of all configuration components managed by Apricot", + "produces": [ + "application/json", + "text/plain" + ], + "tags": [ + "component configuration" + ], + "summary": "List Apricot-managed configuration components", + "parameters": [ + { + "enum": [ + "json", + "text" + ], + "type": "string", + "default": "text", + "description": "Output format, json or text", + "name": "format", + "in": "query" + } + ], + "responses": { + "200": { + "description": "List of components, either as JSON array or comma-separated plain text", + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "500": { + "description": "Internal server error", + "schema": { + "type": "string" + } + } + } + } + }, + "/components/_invalidate_cache": { + "post": { + "description": "Invalidates all cached templates for all configuration components managed by Apricot", + "produces": [ + "text/plain" + ], + "tags": [ + "component configuration" + ], + "summary": "Clear cache of Apricot-managed configuration component templates", + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/components/{component}": { + "get": { + "description": "Returns a list of all entries belonging to a given configuration component corresponding to the given query. The component must be provided. If the run type is not provided, entries for all run types are returned. If the role name is not provided, entries for all role names are returned. If the raw flag is true, AApricot will not attempt to include \"ANY\"/\"any\" run types or role names, and will return only the entries stored under the provided Consul path. If the raw flag is false, Apricot will include all entries for the given component, run type and role name, including entries stored under \"ANY\" and \"any\" paths corresponding to fallback values valid for all run types or role names.", + "produces": [ + "application/json", + "text/plain" + ], + "tags": [ + "component configuration" + ], + "summary": "Lists all entries for an Apricot-managed configuration component", + "parameters": [ + { + "enum": [ + "json", + "text" + ], + "type": "string", + "default": "text", + "description": "Output format, json or text", + "name": "format", + "in": "query" + }, + { + "type": "boolean", + "default": false, + "description": "If true, the query returns strictly Consul paths without catching all run types or role names with ANY/any", + "name": "raw", + "in": "query" + }, + { + "type": "string", + "description": "Configuration component", + "name": "component", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "List of entries, either as JSON array or comma-separated plain text", + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "400": { + "description": "Bad request, if the run type is invalid", + "schema": { + "type": "string" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "type": "string" + } + } + } + } + }, + "/components/{component}/{runtype}": { + "get": { + "description": "Returns a list of all entries belonging to a given configuration component corresponding to the given query. The component must be provided. If the run type is not provided, entries for all run types are returned. If the role name is not provided, entries for all role names are returned. If the raw flag is true, AApricot will not attempt to include \"ANY\"/\"any\" run types or role names, and will return only the entries stored under the provided Consul path. If the raw flag is false, Apricot will include all entries for the given component, run type and role name, including entries stored under \"ANY\" and \"any\" paths corresponding to fallback values valid for all run types or role names.", + "produces": [ + "application/json", + "text/plain" + ], + "tags": [ + "component configuration" + ], + "summary": "Lists all entries for an Apricot-managed configuration component", + "parameters": [ + { + "enum": [ + "json", + "text" + ], + "type": "string", + "default": "text", + "description": "Output format, json or text", + "name": "format", + "in": "query" + }, + { + "type": "boolean", + "default": false, + "description": "If true, the query returns strictly Consul paths without catching all run types or role names with ANY/any", + "name": "raw", + "in": "query" + }, + { + "type": "string", + "description": "Configuration component", + "name": "component", + "in": "path", + "required": true + }, + { + "type": "string", + "default": "ANY", + "description": "O² Run type, must be capitalized", + "name": "runtype", + "in": "path" + } + ], + "responses": { + "200": { + "description": "List of entries, either as JSON array or comma-separated plain text", + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "400": { + "description": "Bad request, if the run type is invalid", + "schema": { + "type": "string" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "type": "string" + } + } + } + } + }, + "/components/{component}/{runtype}/{rolename}": { + "get": { + "description": "Returns a list of all entries belonging to a given configuration component corresponding to the given query. The component must be provided. If the run type is not provided, entries for all run types are returned. If the role name is not provided, entries for all role names are returned. If the raw flag is true, AApricot will not attempt to include \"ANY\"/\"any\" run types or role names, and will return only the entries stored under the provided Consul path. If the raw flag is false, Apricot will include all entries for the given component, run type and role name, including entries stored under \"ANY\" and \"any\" paths corresponding to fallback values valid for all run types or role names.", + "produces": [ + "application/json", + "text/plain" + ], + "tags": [ + "component configuration" + ], + "summary": "Lists all entries for an Apricot-managed configuration component", + "parameters": [ + { + "enum": [ + "json", + "text" + ], + "type": "string", + "default": "text", + "description": "Output format, json or text", + "name": "format", + "in": "query" + }, + { + "type": "boolean", + "default": false, + "description": "If true, the query returns strictly Consul paths without catching all run types or role names with ANY/any", + "name": "raw", + "in": "query" + }, + { + "type": "string", + "description": "Configuration component", + "name": "component", + "in": "path", + "required": true + }, + { + "type": "string", + "default": "ANY", + "description": "O² Run type, must be capitalized", + "name": "runtype", + "in": "path" + }, + { + "type": "string", + "default": "any", + "description": "Role name", + "name": "rolename", + "in": "path" + } + ], + "responses": { + "200": { + "description": "List of entries, either as JSON array or comma-separated plain text", + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "400": { + "description": "Bad request, if the run type is invalid", + "schema": { + "type": "string" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "type": "string" + } + } + } + } + }, + "/components/{component}/{runtype}/{rolename}/{entry}": { + "get": { + "description": "The provided component, run type, role name and entry key are used to query the configuration service for a configuration entry, which is then processed in the O² Apricot template system to produce the final payload, and returned as string..", + "produces": [ + "text/plain" + ], + "tags": [ + "component configuration" + ], + "summary": "Returns a configuration payload for a given component, run type, role name and entry key", + "parameters": [ + { + "type": "boolean", + "default": false, + "description": "If true, template processing is performed to produce the final payload; if false, the entry is returned verbatim. In the true case, any number of additional string key-value pairs may be passed as query parameters (e.g. ?process=true\u0026mykey1=myvalue1\u0026mykey2=myvalue2), which are then fed into the template system as variables that affect configuration payload generation", + "name": "process", + "in": "query" + }, + { + "type": "string", + "description": "Configuration component", + "name": "component", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "O² Run type, must be capitalized", + "name": "runtype", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Role name", + "name": "rolename", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Entry key", + "name": "entry", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "Configuration payload for the queried entry, either processed or verbatim depending on the process flag", + "schema": { + "type": "string" + } + }, + "400": { + "description": "Bad request, if a parameter is invalid", + "schema": { + "type": "string" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "type": "string" + } + } + } + } + }, + "/components/{component}/{runtype}/{rolename}/{entry}/resolve": { + "get": { + "description": "Returns a resolved path for a given component, run type, role name and entry key. The path points to an actual existing entry in Consul, resolving ANY run type and any rolename wildcards.", + "produces": [ + "text/plain" + ], + "tags": [ + "component configuration" + ], + "summary": "Resolves a query for a given component, run type, role name and entry key", + "parameters": [ + { + "type": "string", + "description": "Configuration component", + "name": "component", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "O² Run type, must be capitalized", + "name": "runtype", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Role name", + "name": "rolename", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Entry key", + "name": "entry", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "Resolved path for the queried entry", + "schema": { + "type": "string" + } + }, + "400": { + "description": "Bad request, if a parameter is invalid", + "schema": { + "type": "string" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "type": "string" + } + } + } + } + }, + "/inventory/detectors/{detector}/flps/{format}": { + "get": { + "description": "Returns the list of all Apricot-managed hosts in the cluster that are known to be FLPs and serving the given detector, newline-separated or JSON depending on the format parameter", + "produces": [ + "text/plain" + ], + "tags": [ + "cluster inventory" + ], + "summary": "Returns the list of FLPs in the cluster that serve a given detector", + "parameters": [ + { + "enum": [ + "json", + "text" + ], + "type": "string", + "default": "text", + "description": "Output format, json or text", + "name": "format", + "in": "path" + } + ], + "responses": { + "200": { + "description": "List of FLPs in the cluster known to Apricot, newline-separated or JSON depending on the format parameter", + "schema": { + "type": "string" + } + } + } + } + }, + "/inventory/detectors/{format}": { + "get": { + "description": "Returns the list of all detectors known to Apricot that belong to the installed instance, newline-separated or JSON depending on the format parameter", + "produces": [ + "text/plain" + ], + "tags": [ + "cluster inventory" + ], + "summary": "Returns the list of detectors belonging to the installed instance known to Apricot", + "parameters": [ + { + "enum": [ + "json", + "text" + ], + "type": "string", + "default": "text", + "description": "Output format, json or text", + "name": "format", + "in": "path" + } + ], + "responses": { + "200": { + "description": "List of detectors in the instance known to Apricot, newline-separated or JSON depending on the format parameter", + "schema": { + "type": "string" + } + } + } + } + }, + "/inventory/flps/{format}": { + "get": { + "description": "Returns the list of all Apricot-managed hosts in the cluster that are known to be FLPs, newline-separated or JSON depending on the format parameter", + "produces": [ + "text/plain" + ], + "tags": [ + "cluster inventory" + ], + "summary": "Returns the list of FLPs in the cluster known to Apricot", + "parameters": [ + { + "enum": [ + "json", + "text" + ], + "type": "string", + "default": "text", + "description": "Output format, json or text", + "name": "format", + "in": "path" + } + ], + "responses": { + "200": { + "description": "List of FLPs in the cluster known to Apricot, newline-separated or JSON depending on the format parameter", + "schema": { + "type": "string" + } + } + } + } + } + }, + "externalDocs": { + "description": "AliECS handbook", + "url": "https://alice-flp.docs.cern.ch/aliecs/handbook/" + } +} \ No newline at end of file diff --git a/apricot/docs/swagger.yaml b/apricot/docs/swagger.yaml new file mode 100644 index 000000000..1e353123e --- /dev/null +++ b/apricot/docs/swagger.yaml @@ -0,0 +1,409 @@ +externalDocs: + description: AliECS handbook + url: https://alice-flp.docs.cern.ch/aliecs/handbook/ +info: + contact: + email: alice-o2-flp-support@cern.ch + name: O² FLP support + url: https://alice-flp.docs.cern.ch/ + description: REST API for ALICE O² Apricot configuration service + title: O² Apricot REST API + version: "1.0" +paths: + /components: + get: + description: Returns a list of all configuration components managed by Apricot + parameters: + - default: text + description: Output format, json or text + enum: + - json + - text + in: query + name: format + type: string + produces: + - application/json + - text/plain + responses: + "200": + description: List of components, either as JSON array or comma-separated + plain text + schema: + items: + type: string + type: array + "500": + description: Internal server error + schema: + type: string + summary: List Apricot-managed configuration components + tags: + - component configuration + /components/_invalidate_cache: + post: + description: Invalidates all cached templates for all configuration components + managed by Apricot + produces: + - text/plain + responses: + "200": + description: OK + summary: Clear cache of Apricot-managed configuration component templates + tags: + - component configuration + /components/{component}: + get: + description: Returns a list of all entries belonging to a given configuration + component corresponding to the given query. The component must be provided. + If the run type is not provided, entries for all run types are returned. If + the role name is not provided, entries for all role names are returned. If + the raw flag is true, AApricot will not attempt to include "ANY"/"any" run + types or role names, and will return only the entries stored under the provided + Consul path. If the raw flag is false, Apricot will include all entries for + the given component, run type and role name, including entries stored under + "ANY" and "any" paths corresponding to fallback values valid for all run types + or role names. + parameters: + - default: text + description: Output format, json or text + enum: + - json + - text + in: query + name: format + type: string + - default: false + description: If true, the query returns strictly Consul paths without catching + all run types or role names with ANY/any + in: query + name: raw + type: boolean + - description: Configuration component + in: path + name: component + required: true + type: string + produces: + - application/json + - text/plain + responses: + "200": + description: List of entries, either as JSON array or comma-separated plain + text + schema: + items: + type: string + type: array + "400": + description: Bad request, if the run type is invalid + schema: + type: string + "500": + description: Internal server error + schema: + type: string + summary: Lists all entries for an Apricot-managed configuration component + tags: + - component configuration + /components/{component}/{runtype}: + get: + description: Returns a list of all entries belonging to a given configuration + component corresponding to the given query. The component must be provided. + If the run type is not provided, entries for all run types are returned. If + the role name is not provided, entries for all role names are returned. If + the raw flag is true, AApricot will not attempt to include "ANY"/"any" run + types or role names, and will return only the entries stored under the provided + Consul path. If the raw flag is false, Apricot will include all entries for + the given component, run type and role name, including entries stored under + "ANY" and "any" paths corresponding to fallback values valid for all run types + or role names. + parameters: + - default: text + description: Output format, json or text + enum: + - json + - text + in: query + name: format + type: string + - default: false + description: If true, the query returns strictly Consul paths without catching + all run types or role names with ANY/any + in: query + name: raw + type: boolean + - description: Configuration component + in: path + name: component + required: true + type: string + - default: ANY + description: O² Run type, must be capitalized + in: path + name: runtype + type: string + produces: + - application/json + - text/plain + responses: + "200": + description: List of entries, either as JSON array or comma-separated plain + text + schema: + items: + type: string + type: array + "400": + description: Bad request, if the run type is invalid + schema: + type: string + "500": + description: Internal server error + schema: + type: string + summary: Lists all entries for an Apricot-managed configuration component + tags: + - component configuration + /components/{component}/{runtype}/{rolename}: + get: + description: Returns a list of all entries belonging to a given configuration + component corresponding to the given query. The component must be provided. + If the run type is not provided, entries for all run types are returned. If + the role name is not provided, entries for all role names are returned. If + the raw flag is true, AApricot will not attempt to include "ANY"/"any" run + types or role names, and will return only the entries stored under the provided + Consul path. If the raw flag is false, Apricot will include all entries for + the given component, run type and role name, including entries stored under + "ANY" and "any" paths corresponding to fallback values valid for all run types + or role names. + parameters: + - default: text + description: Output format, json or text + enum: + - json + - text + in: query + name: format + type: string + - default: false + description: If true, the query returns strictly Consul paths without catching + all run types or role names with ANY/any + in: query + name: raw + type: boolean + - description: Configuration component + in: path + name: component + required: true + type: string + - default: ANY + description: O² Run type, must be capitalized + in: path + name: runtype + type: string + - default: any + description: Role name + in: path + name: rolename + type: string + produces: + - application/json + - text/plain + responses: + "200": + description: List of entries, either as JSON array or comma-separated plain + text + schema: + items: + type: string + type: array + "400": + description: Bad request, if the run type is invalid + schema: + type: string + "500": + description: Internal server error + schema: + type: string + summary: Lists all entries for an Apricot-managed configuration component + tags: + - component configuration + /components/{component}/{runtype}/{rolename}/{entry}: + get: + description: The provided component, run type, role name and entry key are used + to query the configuration service for a configuration entry, which is then + processed in the O² Apricot template system to produce the final payload, + and returned as string.. + parameters: + - default: false + description: If true, template processing is performed to produce the final + payload; if false, the entry is returned verbatim. In the true case, any + number of additional string key-value pairs may be passed as query parameters + (e.g. ?process=true&mykey1=myvalue1&mykey2=myvalue2), which are then fed + into the template system as variables that affect configuration payload + generation + in: query + name: process + type: boolean + - description: Configuration component + in: path + name: component + required: true + type: string + - description: O² Run type, must be capitalized + in: path + name: runtype + required: true + type: string + - description: Role name + in: path + name: rolename + required: true + type: string + - description: Entry key + in: path + name: entry + required: true + type: string + produces: + - text/plain + responses: + "200": + description: Configuration payload for the queried entry, either processed + or verbatim depending on the process flag + schema: + type: string + "400": + description: Bad request, if a parameter is invalid + schema: + type: string + "500": + description: Internal server error + schema: + type: string + summary: Returns a configuration payload for a given component, run type, role + name and entry key + tags: + - component configuration + /components/{component}/{runtype}/{rolename}/{entry}/resolve: + get: + description: Returns a resolved path for a given component, run type, role name + and entry key. The path points to an actual existing entry in Consul, resolving + ANY run type and any rolename wildcards. + parameters: + - description: Configuration component + in: path + name: component + required: true + type: string + - description: O² Run type, must be capitalized + in: path + name: runtype + required: true + type: string + - description: Role name + in: path + name: rolename + required: true + type: string + - description: Entry key + in: path + name: entry + required: true + type: string + produces: + - text/plain + responses: + "200": + description: Resolved path for the queried entry + schema: + type: string + "400": + description: Bad request, if a parameter is invalid + schema: + type: string + "500": + description: Internal server error + schema: + type: string + summary: Resolves a query for a given component, run type, role name and entry + key + tags: + - component configuration + /inventory/detectors/{detector}/flps/{format}: + get: + description: Returns the list of all Apricot-managed hosts in the cluster that + are known to be FLPs and serving the given detector, newline-separated or + JSON depending on the format parameter + parameters: + - default: text + description: Output format, json or text + enum: + - json + - text + in: path + name: format + type: string + produces: + - text/plain + responses: + "200": + description: List of FLPs in the cluster known to Apricot, newline-separated + or JSON depending on the format parameter + schema: + type: string + summary: Returns the list of FLPs in the cluster that serve a given detector + tags: + - cluster inventory + /inventory/detectors/{format}: + get: + description: Returns the list of all detectors known to Apricot that belong + to the installed instance, newline-separated or JSON depending on the format + parameter + parameters: + - default: text + description: Output format, json or text + enum: + - json + - text + in: path + name: format + type: string + produces: + - text/plain + responses: + "200": + description: List of detectors in the instance known to Apricot, newline-separated + or JSON depending on the format parameter + schema: + type: string + summary: Returns the list of detectors belonging to the installed instance known + to Apricot + tags: + - cluster inventory + /inventory/flps/{format}: + get: + description: Returns the list of all Apricot-managed hosts in the cluster that + are known to be FLPs, newline-separated or JSON depending on the format parameter + parameters: + - default: text + description: Output format, json or text + enum: + - json + - text + in: path + name: format + type: string + produces: + - text/plain + responses: + "200": + description: List of FLPs in the cluster known to Apricot, newline-separated + or JSON depending on the format parameter + schema: + type: string + summary: Returns the list of FLPs in the cluster known to Apricot + tags: + - cluster inventory +swagger: "2.0" diff --git a/apricot/instance.go b/apricot/instance.go new file mode 100644 index 000000000..b143a0251 --- /dev/null +++ b/apricot/instance.go @@ -0,0 +1,126 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2020-2021 CERN and copyright holders of ALICE O². + * Author: Teo Mrnjavac + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package apricot + +import ( + "fmt" + "net/url" + "sync" + + "github.com/AliceO2Group/Control/apricot/cacheproxy" + "github.com/AliceO2Group/Control/apricot/local" + "github.com/AliceO2Group/Control/apricot/remote" + "github.com/AliceO2Group/Control/common/logger/infologger" + "github.com/AliceO2Group/Control/configuration" + "github.com/spf13/viper" +) + +var ( + once sync.Once + instance configuration.Service +) + +func newService(configUri string) (configuration.Service, error) { + parsedUri, err := url.Parse(configUri) + if err != nil { + return nil, err + } + + var svc configuration.Service + + switch parsedUri.Scheme { + case "consul": + fallthrough + case "file": + if viper.GetString("component") == "apricot" { + log.WithField("configUri", configUri). + Debug("new embedded apricot instance") + } else if viper.GetString("component") == "coconut" { + log.WithField("config_endpoint", configUri). + Debug("new embedded apricot instance") + } else { + log.WithField("configUri", configUri). + WithField("level", infologger.IL_Support). + Info("new embedded apricot instance") + } + svc, err = local.NewService(configUri) + if err != nil { + return svc, err + } + if viper.GetBool("configCache") { + svc, err = cacheproxy.NewService(svc) + } + return svc, err + case "apricot": + if viper.GetString("component") == "apricot" { + log.WithField("configUri", configUri). + Warn("apricot proxy mode") + } else if viper.GetString("component") == "coconut" { + log.WithField("config_endpoint", configUri). + Debug("new apricot client") + } else { + log.WithField("configUri", configUri). + WithField("level", infologger.IL_Support). + Info("new apricot client") + } + svc, err = remote.NewService(configUri) + if err != nil { + return svc, err + } + if viper.GetBool("configCache") { + svc, err = cacheproxy.NewService(svc) + } + return svc, err + case "mock": + svc, err = local.NewService(configUri) + if err != nil { + return svc, err + } + return svc, err + default: + return nil, fmt.Errorf("invalid configuration URI scheme %s", parsedUri.Scheme) + } +} + +func Instance() configuration.Service { + once.Do(func() { + var ( + err error + configUri string + ) + if viper.IsSet("config_endpoint") { //coconut + configUri = viper.GetString("config_endpoint") + } else if viper.IsSet("configServiceUri") { //core + configUri = viper.GetString("configServiceUri") + } else { //apricot + configUri = viper.GetString("backendUri") + } + instance, err = newService(configUri) + if err != nil { + log.WithField("level", infologger.IL_Support).WithField("configServiceUri", configUri).Fatal("bad configuration URI") + } + }) + return instance +} diff --git a/apricot/local/jsontypes.go b/apricot/local/jsontypes.go new file mode 100644 index 000000000..7133f6a99 --- /dev/null +++ b/apricot/local/jsontypes.go @@ -0,0 +1,35 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2019-2021 CERN and copyright holders of ALICE O². + * Author: Miltiadis Alexis + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package local + +type Card struct { + Type string `json:"type"` + PciAddress string `json:"pciAddress"` + Serial string `json:"serial"` + Endpoint string `json:"endpoint"` + Numa string `json:"numa"` + Firware string `json:"firmware"` + UserLogicVersion string `json:"userLogicVersion"` +} diff --git a/apricot/local/local_test.go b/apricot/local/local_test.go new file mode 100644 index 000000000..49fc11245 --- /dev/null +++ b/apricot/local/local_test.go @@ -0,0 +1,60 @@ +package local + +import ( + "github.com/spf13/viper" + "io" + "os" + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +var tmpDir *string + +const serviceConfigFile = "service_test.yaml" +const serviceHTTPConfigFile = "servicehttp_test.yaml" + +func TestLocal(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Local Apricot Service Suite") +} + +var _ = BeforeSuite(func() { + var err error + tmpDir = new(string) + *tmpDir, err = os.MkdirTemp("", "o2control-local-service") + Expect(err).NotTo(HaveOccurred()) + + // copy config files + configFiles := []string{serviceConfigFile, serviceHTTPConfigFile} + for _, configFile := range configFiles { + from, err := os.Open("./" + configFile) + Expect(err).NotTo(HaveOccurred()) + defer func() { + err := from.Close() + if err != nil { + Expect(err).NotTo(HaveOccurred()) + } + }() + + to, err := os.OpenFile(*tmpDir+"/"+configFile, os.O_RDWR|os.O_CREATE, 0666) + Expect(err).NotTo(HaveOccurred()) + defer func() { + err := to.Close() + if err != nil { + Expect(err).NotTo(HaveOccurred()) + } + }() + + _, err = io.Copy(to, from) + Expect(err).NotTo(HaveOccurred()) + } + + viper.Set("coreWorkingDir", tmpDir) // used by NewRunNumber with YAML backend +}) + +var _ = AfterSuite(func() { + err := os.RemoveAll(*tmpDir) + Expect(err).NotTo(HaveOccurred()) +}) diff --git a/apricot/local/service.go b/apricot/local/service.go new file mode 100644 index 000000000..827cb931b --- /dev/null +++ b/apricot/local/service.go @@ -0,0 +1,807 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2019-2021 CERN and copyright holders of ALICE O². + * Author: Teo Mrnjavac + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +// Package local implements a local configuration backend for the configuration +// service, providing direct access to the backend storage without gRPC. +package local + +import ( + "encoding/json" + "errors" + "fmt" + "github.com/AliceO2Group/Control/common/logger" + "github.com/AliceO2Group/Control/common/logger/infologger" + "github.com/AliceO2Group/Control/configuration/cfgbackend" + "github.com/AliceO2Group/Control/configuration/componentcfg" + "github.com/AliceO2Group/Control/configuration/template" + "github.com/flosch/pongo2/v6" + "github.com/hashicorp/go-multierror" + "github.com/sirupsen/logrus" + "github.com/spf13/viper" + "io/ioutil" + "net/url" + "os" + "path/filepath" + "regexp" + "runtime" + "sort" + "strconv" + "strings" + "sync" +) + +var log = logger.New(logrus.StandardLogger(), "confsys") + +const inventoryKeyPrefix = "o2/hardware/" +const readoutCardKeyPrefix = "o2/components/readoutcard/" + +type Service struct { + src cfgbackend.Source + + templateSets map[string]*pongo2.TemplateSet + templateSetsMu sync.Mutex +} + +func NewService(uri string) (svc *Service, err error) { + var src cfgbackend.Source + src, err = cfgbackend.NewSource(uri) + return &Service{ + src: src, + }, err +} + +func (s *Service) NewRunNumber() (runNumber uint32, err error) { + if cSrc, ok := s.src.(*cfgbackend.ConsulSource); ok { + return cSrc.GetNextUInt32(filepath.Join(getConsulRuntimePrefix(), "run_number")) + } else { + // Unsafe check-and-set, only for file backend + var rnf string + rnf = filepath.Join(viper.GetString("coreWorkingDir"), "runcounter.txt") + if _, err = os.Stat(rnf); os.IsNotExist(err) { + err = ioutil.WriteFile(rnf, []byte("0"), 0644) + if err != nil { + return + } + } + var raw []byte + raw, err = ioutil.ReadFile(rnf) + if err != nil { + return + } + var rn64 uint64 + rn64, err = strconv.ParseUint(string(raw[:]), 10, 32) + if err != nil { + return + } + runNumber = uint32(rn64) + runNumber++ + raw = []byte(strconv.FormatUint(uint64(runNumber), 10)) + err = ioutil.WriteFile(rnf, raw, 0) + return + } +} + +// maybe this one shouldn't exist at all, because vars should get inserted +// response: but not all of them! some vars will likely only get parsed at deployment time i.e. right +// before pushing TaskInfos +func (s *Service) GetDefaults() map[string]string { + s.logMethod() + + smap := s.getStringMap(filepath.Join(getAliECSRuntimePrefix(), "defaults")) + + // Fill in some global constants we want to make available everywhere + var configUri string + if viper.IsSet("config_endpoint") { //coconut + configUri = viper.GetString("config_endpoint") + } else if viper.IsSet("globalConfigurationUri") { //core + configUri = viper.GetString("globalConfigurationUri") + } else { //apricot + configUri = viper.GetString("backendUri") + } + + smap["consul_base_uri"] = configUri + consulUrl, err := url.ParseRequestURI(configUri) + if err == nil { + smap["consul_hostname"] = consulUrl.Hostname() + smap["consul_port"] = consulUrl.Port() + smap["consul_endpoint"] = consulUrl.Host + } else { + log.WithField("globalConfigurationUri", configUri). + Warn("cannot parse global configuration endpoint") + } + smap["framework_id"], _ = s.GetRuntimeEntry("aliecs", "mesos_fid") + smap["core_hostname"], _ = os.Hostname() // fixme: this might not be correct if apricot sits elsewhere than the core + return smap +} + +func (s *Service) ListDetectors(getAll bool) (detectors []string, err error) { + s.logMethod() + + keyPrefix := inventoryKeyPrefix + "detectors/" + var keys []string + keys, err = s.src.GetKeysByPrefix(keyPrefix) + if err != nil { + log.WithError(err).Error("could not retrieve detectors") + return []string{}, err + } + detectorSet := make(map[string]bool, 0) + detectors = make([]string, 0) + for _, key := range keys { + detTrimmed := strings.TrimPrefix(key, keyPrefix) + detname := strings.Split(detTrimmed, "/") + if !getAll && detname[0] == "TRG" { + continue + } + if _, ok := detectorSet[detname[0]]; !ok { // the detector name we found in the path isn't already accounted for + detectorSet[detname[0]] = true + detectors = append(detectors, detname[0]) + } + } + return detectors, err +} + +func (s *Service) GetHostInventory(detector string) (hosts []string, err error) { + s.logMethod() + + var keyPrefix string + if detector != "" { + keyPrefix = inventoryKeyPrefix + "detectors/" + detector + "/flps/" + } else { + keyPrefix = inventoryKeyPrefix + "flps/" + } + var keys []string + keys, err = s.src.GetKeysByPrefix(keyPrefix) + if err != nil { + log.WithError(err).Error("could not retrieve host list") + return []string{}, err + } + hostSet := make(map[string]bool, 0) + hosts = make([]string, 0) + for _, key := range keys { + hostTrimmed := strings.TrimPrefix(key, keyPrefix) + hostname := strings.Split(hostTrimmed, "/") + if _, ok := hostSet[hostname[0]]; !ok { + hostSet[hostname[0]] = true + hosts = append(hosts, hostname[0]) + } + } + return hosts, err +} + +func (s *Service) GetLinkIDsForCRUEndpoint(host string, cruId string, endpoint string, onlyEnabled bool) (ids []string, err error) { + + cfgPath := readoutCardKeyPrefix + host + "/cru/" + cruId + "/" + endpoint + + readoutCardConfig, err := s.src.Get(cfgPath) + if err != nil { + return nil, err + } + + var data map[string]map[string]interface{} + if err := json.Unmarshal([]byte(readoutCardConfig), &data); err != nil { + return nil, err + } + + linkPattern := regexp.MustCompile(`^link(\d+)$`) + + var linkIDs []string + for key, value := range data { + matches := linkPattern.FindStringSubmatch(key) + if matches == nil { + continue + } + linkEnabled, ok := value["enabled"].(string) + if !ok { + // this implies that "enabled" key should be always there, even if we want both enabled and disabled links + continue + } + if linkEnabled == "true" || onlyEnabled == false { + linkIDs = append(linkIDs, matches[1]) + } + } + + return linkIDs, nil +} + +type Aliases struct { + Flp struct { + Alias string `json:"alias"` + } `json:"flp"` + Cards map[string]struct { + Alias string `json:"alias"` + Links map[string]struct { + Alias string `json:"alias"` + } `json:"links"` + } `json:"cards"` +} + +func (s *Service) getAliasesForHost(detector, host string) (Aliases, error) { + aliasesPath := inventoryKeyPrefix + "detectors/" + detector + "/flps/" + host + "/aliases" + aliasesFile, err := s.src.Get(aliasesPath) + if err != nil { + return Aliases{}, err + } + + var aliases Aliases + if err := json.Unmarshal([]byte(aliasesFile), &aliases); err != nil { + return Aliases{}, err + } + return aliases, nil +} + +func (s *Service) GetAliasedLinkIDsForDetector(detector string, onlyEnabled bool) (aliasedLinkIds []string, err error) { + s.logMethod() + + hosts, err := s.GetHostInventory(detector) + if err != nil { + return nil, err + } + + for _, host := range hosts { + aliases, err := s.getAliasesForHost(detector, host) + if err != nil { + return nil, err + } + + crus, err := s.GetCRUCardsForHost(host) + if err != nil { + return nil, err + } + for _, cru := range crus { + endpoints, err := s.GetEndpointsForCRUCard(host, cru) + if err != nil { + return nil, err + } + for _, endpoint := range endpoints { + linkIDs, err := s.GetLinkIDsForCRUEndpoint(host, cru, endpoint, onlyEnabled) + if err != nil { + return nil, err + } + + aliasesForCruEndpoint, ok := aliases.Cards[cru+":"+endpoint] + if !ok { + return nil, fmt.Errorf("aliases for cru endpoint %s:%s not found", cru, endpoint) + } + for _, linkID := range linkIDs { + linkIdAlias, ok := aliasesForCruEndpoint.Links[linkID] + if !ok { + return nil, fmt.Errorf("alias for link %s in cru endpoint %s:%s not found", linkID, cru, endpoint) + } + aliasedLinkIds = append(aliasedLinkIds, linkIdAlias.Alias) + } + } + } + } + sort.Strings(aliasedLinkIds) + return aliasedLinkIds, nil +} + +func (s *Service) GetDetectorsInventory() (inventory map[string][]string, err error) { + s.logMethod() + + inventory = map[string][]string{} + detectors, err := s.ListDetectors(true) + if err != nil { + log.WithError(err).Error("could not retrieve detectors list") + return nil, err + } + for _, detector := range detectors { + hosts, err := s.GetHostInventory(detector) + if err != nil { + log.WithError(err).Error("could not retrieve hosts list") + return nil, err + } + inventory[detector] = hosts + } + return inventory, err +} + +func (s *Service) GetVars() map[string]string { + s.logMethod() + + return s.getStringMap(filepath.Join(getAliECSRuntimePrefix(), "vars")) +} + +// Returns a YAML file OR even a structure made of Roles or Nodes with: +// import() functions already computed and resolved +// vars inserted +func (s *Service) GenerateWorkflowDescriptor(wfPath string, vars map[string]string /*vars from cli/gui*/) string { + panic("not implemented yet") +} + +// TODO: remove these, replaced by 2 calls to Get/SetRuntimeEntry in task/manager.go +// +//// Persist Mesos Framework ID by saving to Consul, or to a local file. +//func (s *Service) SetMesosFID(fidValue string) error { +// if cSrc, ok := s.src.(*cfgbackend.ConsulSource); ok { +// return cSrc.Put(filepath.Join(getConsulRuntimePrefix(),"mesos_fid"), fidValue) +// } else { +// data := []byte(fidValue) +// return ioutil.WriteFile(filepath.Join(viper.GetString("workingDir"), "mesos_fid.txt"), data, 0644) +// } +//} +// +//// Retrieve Mesos Framework ID from Consul, or local file. +//func (s *Service) GetMesosFID() (fidValue string, err error) { +// if cSrc, ok := s.src.(*cfgbackend.ConsulSource); ok { +// return cSrc.Get(filepath.Join(getConsulRuntimePrefix(),"mesos_fid")) +// } else { +// var byteFidValue []byte +// byteFidValue, err = ioutil.ReadFile(filepath.Join(viper.GetString("workingDir"), "mesos_fid.txt")) +// if err != nil { +// return +// } +// fidValue = strings.TrimSuffix(string(byteFidValue), "/n") +// return +// } +//} + +func (s *Service) GetComponentConfiguration(query *componentcfg.Query) (payload string, err error) { + s.logMethod() + + var absolutePath string + absolutePath, err = s.queryToAbsPath(query) + if err != nil { + return + } + payload, err = s.src.Get(absolutePath) + return +} + +func (s *Service) GetComponentConfigurationWithLastIndex(query *componentcfg.Query) (payload string, lastIndex uint64, err error) { + s.logMethod() + + if cSrc, ok := s.src.(*cfgbackend.ConsulSource); ok { + var absolutePath string + absolutePath, err = s.queryToAbsPath(query) + if err != nil { + return + } + payload, lastIndex, err = cSrc.GetWithLastIndex(absolutePath) + } else { + err = errors.New("component with last index not supported with file backend") + } + return +} + +func (s *Service) GetAndProcessComponentConfiguration(query *componentcfg.Query, varStack map[string]string) (payload string, err error) { + s.logMethod() + + path := query.Path() + + // We need to decompose the requested GetConfig path into prefix and suffix, + // with the last / as separator. + // This is done to allow internal references between template snippets + // within the same Consul directory. + indexOfLastSeparator := strings.LastIndex(path, "/") + var basePath string + shortPath := path + if indexOfLastSeparator != -1 { + basePath = path[:indexOfLastSeparator] + shortPath = path[indexOfLastSeparator+1:] + } + + // We get a TemplateSet, with a custom TemplateLoader. Depending on past events, a template set for this base path + // might already exist in the service's template set cache map. We will then use the cache of this template set to + // speed up future requests. + // In order for resolution of short paths to work (i.e. entry name within a component/runtype/rolename directory), + // we need to build one templateSet+templateLoader per base path. + // Our ConsulTemplateLoader takes control of the FromFile code path in pongo2, effectively adding support for + // Consul as file-like backend. + tplSet := s.templateSetForBasePath(basePath) + var tpl *pongo2.Template + tpl, err = tplSet.FromCache(shortPath) + + if err != nil { + return fmt.Sprintf("{\"error\":\"%s\"}", err.Error()), err + } + + bindings := make(map[string]interface{}) + for k, v := range varStack { + bindings[strings.TrimSpace(k)] = v + } + + // Add custom functions to bindings: + funcMap := template.MakeUtilFuncMap(varStack) + for k, v := range funcMap { + bindings[k] = v + } + + payload, err = tpl.Execute(bindings) + return +} + +func (s *Service) ResolveComponentQuery(query *componentcfg.Query) (resolved *componentcfg.Query, err error) { + s.logMethod() + + resolved = &componentcfg.Query{} + if query == nil { + *resolved = *query + return + } + resolved, err = s.resolveComponentQuery(query) + return +} + +func (s *Service) RawGetRecursive(path string) (string, error) { + s.logMethod() + + cfgDump, err := s.src.GetRecursive(path) + if err != nil { + log.WithError(err).Error("cannot retrieve configuration") + return "", err + } + cfgBytes, err := json.MarshalIndent(cfgDump, "", "\t") + if err != nil { + log.WithError(err).Error("cannot marshal configuration dump") + return "", err + } + return string(cfgBytes[:]), nil +} + +func (s *Service) GetDetectorForHost(hostname string) (string, error) { + s.logMethod() + + keys, err := s.src.GetKeysByPrefix(filepath.Join("o2/hardware", "detectors")) + if err != nil { + return "", err + } + for _, key := range keys { + // key example: o2/hardware/detectors/TST/flps/some-hostname/ + splitKey := strings.Split(key, "/") + if len(splitKey) == 7 { + if splitKey[5] == hostname { + return splitKey[3], nil + } + } + } + return "", fmt.Errorf("detector not found for host %s", hostname) +} + +func (s *Service) GetDetectorsForHosts(hosts []string) ([]string, error) { + s.logMethod() + + detectorMap := make(map[string]struct{}) + for _, host := range hosts { + det, err := s.GetDetectorForHost(host) + if err != nil { + return []string{}, err + } + detectorMap[det] = struct{}{} + } + + detectorSlice := make([]string, len(detectorMap)) + i := 0 + for k := range detectorMap { + detectorSlice[i] = k + i++ + } + sort.Strings(detectorSlice) + return detectorSlice, nil +} + +func (s *Service) GetCRUCardsForHost(hostname string) ([]string, error) { + s.logMethod() + + var cards map[string]Card + var serials []string + cfgCards, err := s.src.Get(filepath.Join("o2/hardware", "flps", hostname, "cards")) + if err != nil { + return nil, err + } + err = json.Unmarshal([]byte(cfgCards), &cards) + if err != nil { + return nil, err + } + unique := make(map[string]bool) + for _, card := range cards { + if _, value := unique[card.Serial]; !value { + unique[card.Serial] = true + serials = append(serials, card.Serial) + } + } + return serials, nil +} + +func (s *Service) GetEndpointsForCRUCard(hostname, cardSerial string) ([]string, error) { + s.logMethod() + + log.WithPrefix("rpcserver"). + WithField("method", "GetEndpointsForCRUCard"). + WithField("level", infologger.IL_Devel). + WithField("hostname", hostname). + WithField("cardSerial", cardSerial). + Debug("getting endpoints") + + var cards map[string]Card + var endpoints []string + cfgCards, err := s.src.Get(filepath.Join("o2/hardware", "flps", hostname, "cards")) + if err != nil { + return nil, err + } + err = json.Unmarshal([]byte(cfgCards), &cards) + if err != nil { + return nil, err + } + for _, card := range cards { + if card.Serial == cardSerial { + endpoints = append(endpoints, card.Endpoint) + } + } + return endpoints, nil +} + +func (s *Service) GetRuntimeEntry(component string, key string) (string, error) { + s.logMethod() + + if cSrc, ok := s.src.(*cfgbackend.ConsulSource); ok { + return cSrc.Get(filepath.Join(getConsulRuntimePrefix(), component, key)) + } else { + return "", errors.New("runtime KV not supported with file backend") + } +} + +func (s *Service) SetRuntimeEntry(component string, key string, value string) error { + s.logMethod() + + if cSrc, ok := s.src.(*cfgbackend.ConsulSource); ok { + return cSrc.Put(filepath.Join(getConsulRuntimePrefix(), component, key), value) + } else { + return errors.New("runtime KV not supported with file backend") + } +} + +func (s *Service) GetRuntimeEntries(component string) (map[string]string, error) { + s.logMethod() + + if keys, err := s.ListRuntimeEntries(component); err == nil { + var keysErrors *multierror.Error + entries := make(map[string]string) + for _, key := range keys { + if entry, err := s.GetRuntimeEntry(component, key); err == nil { + entries[key] = entry + } else { + keysErrors = multierror.Append(keysErrors, err) + } + } + return entries, keysErrors.ErrorOrNil() + } else { + return nil, err + } + +} + +func (s *Service) ListRuntimeEntries(component string) ([]string, error) { + s.logMethod() + + if cSrc, ok := s.src.(*cfgbackend.ConsulSource); ok { + path := filepath.Join(getConsulRuntimePrefix(), component) + keys, err := cSrc.GetKeysByPrefix(path) + if err != nil { + return nil, err + } + + payload := make([]string, 0) + for _, k := range keys { + keySuffix := strings.TrimPrefix(k, path+"/") + if keySuffix == "" { + continue + } + split := strings.Split(keySuffix, componentcfg.SEPARATOR) + var last string + last = split[len(split)-1] + if last == "" { + continue + } else { + payload = append(payload, keySuffix) + } + } + return payload, nil + } else { + return nil, errors.New("runtime KV not supported with file backend") + } +} + +func (s *Service) ListComponents() (components []string, err error) { + s.logMethod() + + keyPrefix := componentcfg.ConfigComponentsPath + var keys []string + keys, err = s.src.GetKeysByPrefix(keyPrefix) + if err != nil { + return + } + componentSet := make(map[string]struct{}) + for _, key := range keys { + componentsFullName := strings.TrimPrefix(key, keyPrefix) + componentParts := strings.Split(componentsFullName, componentcfg.SEPARATOR) + // Criterion for being a component: + // length of parts == 2 because of trailing slash in Consul output for folders + // part[1] must be len=0, otherwise it's an actual entry within the component and not a trailing slash + if len(componentParts) != 2 || len(componentParts[1]) != 0 { + continue + } + component := componentParts[0] + componentSet[component] = struct{}{} + } + components = make([]string, len(componentSet)) + i := 0 + for component := range componentSet { + components[i] = component + i++ + } + return +} + +func formatComponentEntriesList(keys []string, keyPrefix string) ([]string, error) { + if len(keys) == 0 { + return []string{}, errors.New("no keys found") + } + + var components sort.StringSlice + + for _, key := range keys { + // The input is assumed to be absolute paths, so we must trim the prefix. + // The prefix includes the component name, e.g. o2/components/readout + componentsFullName := strings.TrimPrefix(key, keyPrefix) + componentParts := strings.Split(componentsFullName, "/") + + // The component name is already stripped as part of the keyPrefix. + // len(ANY/any/entry) is least 3, therefore ↓ + if len(componentParts) >= 3 { + // 1st acceptable case: single entry + if len(componentParts[len(componentParts)-1]) == 0 { // means this is a folder key with trailing slash e.g. "ANY/any/" + continue + } + components = append(components, componentsFullName) + } else { + continue + } + } + + sort.Sort(components) + return components, nil +} + +func (s *Service) ListComponentEntries(query *componentcfg.EntriesQuery) (entries []string, err error) { + s.logMethod() + + keyPrefix := componentcfg.ConfigComponentsPath + if query == nil { + err = errors.New("bad query for ListComponentEntries") + return + } + + keyPrefix += query.Component + "/" + + var keys []string + keys, err = s.src.GetKeysByPrefix(keyPrefix) + if err != nil { + return + } + + entries, err = formatComponentEntriesList(keys, keyPrefix) + if err != nil { + return + } + + return +} + +func (s *Service) ImportComponentConfiguration(query *componentcfg.Query, payload string, newComponent bool) (existingComponentUpdated bool, existingEntryUpdated bool, err error) { + s.logMethod() + + if query == nil { + return + } + + var keys []string + // fixme: it looks like an overkill to get all the keys in config tree just to obtain a list of components in o2/components + keys, err = s.src.GetKeysByPrefix("") + if err != nil { + return + } + + components := componentcfg.GetComponentsMapFromKeysList(keys) + + componentExist := components[query.Component] + if !componentExist && !newComponent { + componentMsg := "" + for key := range components { + componentMsg += "\n- " + key + } + err = errors.New("component " + query.Component + " does not exist. " + + "Available components in configuration database:" + componentMsg + + "\nTo create a new component, use the new component parameter") + return + } + if componentExist && newComponent { + err = errors.New("invalid use of new component parameter: component " + query.Component + " already exists") + return + } + + entryExists := false + if !newComponent { + entriesMap := componentcfg.GetEntriesMapOfComponentFromKeysList(query.Component, query.RunType, query.RoleName, keys) + entryExists = entriesMap[query.EntryKey] + } + + fullKey := query.AbsoluteRaw() + + err = s.src.Put(fullKey, payload) + if err != nil { + return + } + + existingComponentUpdated = componentExist + existingEntryUpdated = entryExists + return +} + +func getConsulRuntimePrefix() string { + // FIXME: this should not be hardcoded + return "o2/runtime" +} + +func getAliECSRuntimePrefix() string { + return getConsulRuntimePrefix() + "/aliecs" +} + +func (s *Service) logMethod() { + if !viper.GetBool("verbose") { + return + } + pc, _, _, ok := runtime.Caller(1) + if !ok { + return + } + fun := runtime.FuncForPC(pc) + if fun == nil { + return + } + log.WithPrefix("rpcserver"). + WithField("method", fun.Name()). + WithField("level", infologger.IL_Devel). + Debug("handling RPC request") +} + +func (s *Service) templateSetForBasePath(basePath string) *pongo2.TemplateSet { + s.templateSetsMu.Lock() + defer s.templateSetsMu.Unlock() + if s.templateSets == nil { + s.templateSets = make(map[string]*pongo2.TemplateSet) + } + if _, ok := s.templateSets[basePath]; !ok { + s.templateSets[basePath] = pongo2.NewSet(basePath, template.NewConsulTemplateLoader(s, basePath)) + } + return s.templateSets[basePath] +} + +func (s *Service) InvalidateComponentTemplateCache() { + s.templateSetsMu.Lock() + defer s.templateSetsMu.Unlock() + + // In principle we could also foreach templateSet call ClearCache(), but this is quicker and has the same effect + s.templateSets = make(map[string]*pongo2.TemplateSet) +} diff --git a/apricot/local/service_test.go b/apricot/local/service_test.go new file mode 100644 index 000000000..b3d8e1ede --- /dev/null +++ b/apricot/local/service_test.go @@ -0,0 +1,659 @@ +package local + +import ( + apricotpb "github.com/AliceO2Group/Control/apricot/protos" + "github.com/AliceO2Group/Control/configuration/cfgbackend" + "github.com/AliceO2Group/Control/configuration/componentcfg" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +// TODO: +// - with little effort this could be generalized to test also cacheproxy.service +// - with some more effort, we should also test the same with consul backend, since we break the abstraction layer +// in a few functions in service.go + +var _ = Describe("local service", func() { + + var ( + svc *Service + err error + ) + + Context("with YAML file backend", func() { + BeforeEach(func() { + svc, err = NewService("file://" + *tmpDir + "/" + serviceConfigFile) + Expect(err).NotTo(HaveOccurred()) + }) + + It("should be of type *YamlSource", func() { + _, ok := svc.src.(*cfgbackend.YamlSource) + Expect(ok).To(Equal(true)) + }) + + Describe("creating a new run number", func() { + When("the run number does not exist yet", func() { + It("should return number 1", func() { + Expect(svc.NewRunNumber()).To(Equal(uint32(1))) + }) + It("should return number 2 after number 1", func() { + Expect(svc.NewRunNumber()).To(Equal(uint32(2))) + }) + }) + }) + + Describe("getting defaults", func() { + var defaults map[string]string + BeforeEach(func() { + defaults = svc.GetDefaults() + }) + When("the defaults are retrieved", func() { + It("should contain the elements from the test YAML file at o2/runtime/aliecs/defaults", func() { + Expect(defaults).To(HaveKeyWithValue("key1", "value1")) + }) + It("should contain some prefilled key-values regardless of the information in the configuration backend", func() { + Expect(defaults).To(HaveKey("core_hostname")) + }) + }) + }) + + Describe("getting vars", func() { + var vars map[string]string + BeforeEach(func() { + vars = svc.GetVars() + }) + When("the vars are retrieved", func() { + It("should contain the elements from the test YAML file at o2/runtime/aliecs/vars", func() { + Expect(vars).To(HaveKeyWithValue("key2", "value2")) + }) + }) + }) + + Describe("getting the list of detectors", func() { + var detectors []string + When("the full detector list is retrieved (incl. detector-like subsystems)", func() { + BeforeEach(func() { + detectors, err = svc.ListDetectors(true) + }) + It("should contain the detectors in the test YAML file at o2/hardware/detectors", func() { + Expect(err).NotTo(HaveOccurred()) + Expect(detectors).To(ContainElements("ABC", "DEF", "TRG", "XYZ")) + }) + }) + When("the pure detector list is retrieved", func() { + BeforeEach(func() { + detectors, err = svc.ListDetectors(false) + }) + It("should contain the detectors in the test YAML file at o2/hardware/detectors", func() { + Expect(err).NotTo(HaveOccurred()) + Expect(detectors).To(ContainElements("ABC", "DEF", "XYZ")) + }) + }) + }) + + Describe("getting the list of hosts", func() { + var hosts []string + When("the list of hosts is retrieved for all detectors", func() { + BeforeEach(func() { + hosts, err = svc.GetHostInventory("") + }) + It("should contain all the hosts for all the detectors in the test YAML file at o2/hardware/detectors", func() { + Expect(err).NotTo(HaveOccurred()) + Expect(hosts).To(ContainElements("flp001", "flp002", "flp003", "flp100")) + }) + }) + When("the list of hosts is retrieved for a valid detector", func() { + BeforeEach(func() { + hosts, err = svc.GetHostInventory("ABC") + }) + It("should contain all the hosts for the ABC detector in the test YAML file at o2/hardware/detectors", func() { + Expect(err).NotTo(HaveOccurred()) + Expect(hosts).To(ContainElements("flp001")) + }) + }) + When("the list of hosts is retrieved for a non-existent detector", func() { + BeforeEach(func() { + hosts, err = svc.GetHostInventory("NOPE") + }) + It("should produce an error", func() { + Expect(err).To(HaveOccurred()) + }) + }) + When("the list of hosts is retrieved for an existing detector with no FLPs assigned", func() { + BeforeEach(func() { + hosts, err = svc.GetHostInventory("XYZ") + }) + It("should return an empty list", func() { + Expect(err).NotTo(HaveOccurred()) + Expect(hosts).To(BeEmpty()) + }) + }) + }) + + Describe("getting the detectors FLP inventory", func() { + var inventory map[string][]string + When("the inventory is retrieved", func() { + BeforeEach(func() { + inventory, err = svc.GetDetectorsInventory() + }) + It("should contain all the hosts for all the detectors in the test YAML file at o2/hardware/detectors", func() { + Expect(err).NotTo(HaveOccurred()) + Expect(inventory).To(HaveKey("ABC")) + Expect(inventory["ABC"]).To(ContainElements("flp001")) + Expect(inventory).To(HaveKey("DEF")) + Expect(inventory["DEF"]).To(ContainElements("flp002", "flp003")) + Expect(inventory).To(HaveKey("TRG")) + Expect(inventory["TRG"]).To(ContainElements("flp100")) + Expect(inventory).To(HaveKey("XYZ")) + Expect(inventory["XYZ"]).To(BeEmpty()) + }) + }) + }) + + Describe("getting component configuration", func() { + var ( + payload string + query *componentcfg.Query + err error + ) + When("requesting an entry for a concrete run type and a concrete role", func() { + It("should return the payload for the concrete run type and role", func() { + query, err = componentcfg.NewQuery("qc/PHYSICS/role1/entry1") + Expect(err).NotTo(HaveOccurred()) + payload, err = svc.GetComponentConfiguration(query) + Expect(err).NotTo(HaveOccurred()) + Expect(payload).To(Equal("entry1 config PHYSICS role1")) + }) + }) + When("requesting an entry for ANY run type and any role", func() { + It("should return the payload for ANY/any", func() { + query, err = componentcfg.NewQuery("qc/ANY/any/entry1") + Expect(err).NotTo(HaveOccurred()) + payload, err = svc.GetComponentConfiguration(query) + Expect(err).NotTo(HaveOccurred()) + Expect(payload).To(Equal("entry1 config ANY any")) + }) + }) + When("requesting an entry in a subfolder", func() { + It("should return the expected payload", func() { + query, err = componentcfg.NewQuery("qc/ANY/any/sub/entry12") + Expect(err).NotTo(HaveOccurred()) + payload, err = svc.GetComponentConfiguration(query) + Expect(err).NotTo(HaveOccurred()) + Expect(payload).To(Equal("world")) + }) + }) + }) + + Describe("getting component configuration with last index", func() { + var ( + query *componentcfg.Query + err error + ) + When("a payload is retrieved", func() { + It("should return an error, because file backend does not support last index", func() { + query, err = componentcfg.NewQuery("qc/ANY/any/entry1") + Expect(err).NotTo(HaveOccurred()) + _, _, err = svc.GetComponentConfigurationWithLastIndex(query) + Expect(err).To(HaveOccurred()) + }) + }) + }) + + Describe("getting and processing component configuration", func() { + var ( + payload string + query *componentcfg.Query + err error + ) + When("requesting an entry which requires including another entry and inserting a variable", func() { + It("should return the processed payload", func() { + query, err = componentcfg.NewQuery("qc/ANY/any/entry10") + Expect(err).NotTo(HaveOccurred()) + varStack := make(map[string]string) + varStack["var1"] = "hello" + payload, err = svc.GetAndProcessComponentConfiguration(query, varStack) + Expect(err).NotTo(HaveOccurred()) + Expect(payload).To(Equal("hello world")) + }) + }) + When("requesting an entry which requires including another entry in a different node", func() { + It("should return the processed payload", func() { + query, err = componentcfg.NewQuery("qc/ANY/any/entry12") + Expect(err).NotTo(HaveOccurred()) + varStack := make(map[string]string) + payload, err = svc.GetAndProcessComponentConfiguration(query, varStack) + Expect(err).NotTo(HaveOccurred()) + Expect(payload).To(Equal("hello world")) + }) + }) + When("requesting an entry which is in a subfolder", func() { + It("should return the processed payload", func() { + query, err = componentcfg.NewQuery("qc/ANY/any/sub/entry12") + Expect(err).NotTo(HaveOccurred()) + varStack := make(map[string]string) + payload, err = svc.GetAndProcessComponentConfiguration(query, varStack) + Expect(err).NotTo(HaveOccurred()) + Expect(payload).To(Equal("world")) + }) + }) + }) + + Describe("resolving a query", func() { + var ( + query *componentcfg.Query + resolved *componentcfg.Query + err error + ) + + When("resolving an ANY/any query which may match an ANY/any and a concrete one in the configuration tree", func() { + It("should resolve to ANY/any query", func() { + query, err = componentcfg.NewQuery("qc/ANY/any/entry1") + Expect(err).NotTo(HaveOccurred()) + resolved, err = svc.ResolveComponentQuery(query) + Expect(err).NotTo(HaveOccurred()) + Expect(resolved.RunType).To(Equal(apricotpb.RunType_ANY)) + Expect(resolved.RoleName).To(Equal("any")) + }) + }) + When("resolving an ANY/any query while there is only PHYSICS/role1 entry in the configuration tree", func() { + It("should fail to resolve", func() { + query, err = componentcfg.NewQuery("qc/ANY/any/entry2") + Expect(err).NotTo(HaveOccurred()) + _, err = svc.ResolveComponentQuery(query) + Expect(err).To(HaveOccurred()) + }) + }) + When("resolving a concrete PHYSICS/role1 query which may match an ANY/any and that concrete one in the configuration tree", func() { + It("should resolve to PHYSICS/role1 query", func() { + query, err = componentcfg.NewQuery("qc/PHYSICS/role1/entry1") + Expect(err).NotTo(HaveOccurred()) + resolved, err = svc.ResolveComponentQuery(query) + Expect(err).NotTo(HaveOccurred()) + Expect(resolved.RunType).To(Equal(apricotpb.RunType_PHYSICS)) + Expect(resolved.RoleName).To(Equal("role1")) + }) + }) + When("resolving a concrete PHYSICS/role1 query while there is ANY/any entry in the configuration tree", func() { + It("should resolve to ANY/any query", func() { + query, err = componentcfg.NewQuery("qc/PHYSICS/role1/entry11") + Expect(err).NotTo(HaveOccurred()) + resolved, err = svc.ResolveComponentQuery(query) + Expect(err).NotTo(HaveOccurred()) + Expect(resolved.RunType).To(Equal(apricotpb.RunType_ANY)) + Expect(resolved.RoleName).To(Equal("any")) + }) + }) + When("resolving a query to an entry in a subfolder", func() { + It("should resolve it correctly", func() { + query, err = componentcfg.NewQuery("qc/ANY/any/sub/entry12") + Expect(err).NotTo(HaveOccurred()) + resolved, err = svc.ResolveComponentQuery(query) + Expect(err).NotTo(HaveOccurred()) + Expect(resolved.RunType).To(Equal(apricotpb.RunType_ANY)) + Expect(resolved.RoleName).To(Equal("any")) + Expect(resolved.EntryKey).To(Equal("sub/entry12")) + }) + }) + }) + + Describe("getting raw json payload of everything under a node", func() { + var ( + payload string + err error + ) + When("retrieving a raw json payload", func() { + It("should return a json with correct content and formatting", func() { + payload, err = svc.RawGetRecursive("o2/components/qc/TECHNICAL") + Expect(err).NotTo(HaveOccurred()) + Expect(payload).To(Equal("{\n\t\"any\": {\n\t\t\"entry\": \"config\"\n\t}\n}")) + }) + }) + }) + + Describe("importing component configuration", func() { + var ( + importedPayload string + retrievedPayload string + existingComponentUpdated bool + existingEntryUpdated bool + query *componentcfg.Query + err error + ) + When("we import a new configuration payload for a new component", func() { + It("should be correctly stored", func() { + query, err = componentcfg.NewQuery("reco/ANY/any/entry2") + Expect(err).NotTo(HaveOccurred()) + importedPayload = "hello" + existingComponentUpdated, existingEntryUpdated, err = svc.ImportComponentConfiguration(query, importedPayload, true) + Expect(err).NotTo(HaveOccurred()) + Expect(existingComponentUpdated).To(BeFalse()) + Expect(existingEntryUpdated).To(BeFalse()) + retrievedPayload, err = svc.GetComponentConfiguration(query) + Expect(err).NotTo(HaveOccurred()) + Expect(retrievedPayload).To(Equal(importedPayload)) + }) + }) + When("we import a new configuration payload for a new component, but we do not set the newComponent flag", func() { + It("should produce an error", func() { + query, err = componentcfg.NewQuery("gpu/ANY/any/entry2") + Expect(err).NotTo(HaveOccurred()) + importedPayload = "hello" + existingComponentUpdated, existingEntryUpdated, err = svc.ImportComponentConfiguration(query, importedPayload, false) + Expect(err).To(HaveOccurred()) + }) + }) + When("we import a new configuration entry for an existing component", func() { + It("should be correctly stored", func() { + query, err = componentcfg.NewQuery("qc/ANY/any/entry3") + Expect(err).NotTo(HaveOccurred()) + importedPayload = "hello" + existingComponentUpdated, existingEntryUpdated, err = svc.ImportComponentConfiguration(query, importedPayload, false) + Expect(err).NotTo(HaveOccurred()) + Expect(existingComponentUpdated).To(BeTrue()) + Expect(existingEntryUpdated).To(BeFalse()) + retrievedPayload, err = svc.GetComponentConfiguration(query) + Expect(err).NotTo(HaveOccurred()) + Expect(retrievedPayload).To(Equal(importedPayload)) + }) + }) + When("we import a new payload to an existing entry for an existing component", func() { + It("should be correctly stored", func() { + query, err = componentcfg.NewQuery("qc/ANY/any/entry4") + Expect(err).NotTo(HaveOccurred()) + importedPayload = "hello" + existingComponentUpdated, existingEntryUpdated, err = svc.ImportComponentConfiguration(query, importedPayload, false) + Expect(err).NotTo(HaveOccurred()) + + importedPayload = "hello2" + existingComponentUpdated, existingEntryUpdated, err = svc.ImportComponentConfiguration(query, importedPayload, false) + Expect(err).NotTo(HaveOccurred()) + Expect(existingComponentUpdated).To(BeTrue()) + Expect(existingEntryUpdated).To(BeTrue()) + retrievedPayload, err = svc.GetComponentConfiguration(query) + Expect(err).NotTo(HaveOccurred()) + Expect(retrievedPayload).To(Equal(importedPayload)) + }) + }) + When("we import a new payload to a new entry which should go to a non-existing subfolder", func() { + It("should be correctly stored", func() { + query, err = componentcfg.NewQuery("qc/PHYSICS/role1/sub/entry2") + Expect(err).NotTo(HaveOccurred()) + importedPayload = "sub hello" + existingComponentUpdated, existingEntryUpdated, err = svc.ImportComponentConfiguration(query, importedPayload, false) + Expect(err).NotTo(HaveOccurred()) + Expect(svc.src.Exists("o2/components/qc/PHYSICS/role1/sub/entry2")).To(BeTrue()) + + retrievedPayload, err = svc.GetComponentConfiguration(query) + Expect(err).NotTo(HaveOccurred()) + Expect(retrievedPayload).To(Equal(importedPayload)) + }) + }) + + }) + + Describe("getting detector for host", func() { + var ( + detector string + err error + ) + When("retrieving the detector for a host", func() { + It("should return the correct detector", func() { + detector, err = svc.GetDetectorForHost("flp001") + Expect(err).NotTo(HaveOccurred()) + Expect(detector).To(Equal("ABC")) + }) + }) + When("retrieving the detector for a non-existing host", func() { + It("should produce an error", func() { + detector, err = svc.GetDetectorForHost("NOPE") + Expect(err).To(HaveOccurred()) + }) + }) + When("retrieving the detector spelled lower-case for a host", func() { + // not supported + It("should produce an error", func() { + detector, err = svc.GetDetectorForHost("abc") + Expect(err).To(HaveOccurred()) + }) + }) + }) + Describe("getting detectors for hosts", func() { + var ( + detectors []string + err error + ) + When("retrieving the detectors for a list of hosts", func() { + It("should return the correct detectors", func() { + detectors, err = svc.GetDetectorsForHosts([]string{"flp001", "flp002"}) + Expect(err).NotTo(HaveOccurred()) + Expect(detectors).To(ContainElements("ABC", "DEF")) + }) + }) + When("retrieving the detectors for a non-existing host", func() { + It("should produce an error", func() { + detectors, err = svc.GetDetectorsForHosts([]string{"NOPE"}) + Expect(err).To(HaveOccurred()) + }) + }) + When("retrieving the detectors for a list of hosts with a non-existing host", func() { + It("should produce an error", func() { + detectors, err = svc.GetDetectorsForHosts([]string{"flp001", "NOPE"}) + Expect(err).To(HaveOccurred()) + }) + }) + }) + Describe("getting CRU cards for a host", func() { + var ( + cards []string + err error + ) + When("retrieving the CRU cards for a host", func() { + It("should return the correct CRU cards", func() { + cards, err = svc.GetCRUCardsForHost("flp001") + Expect(err).NotTo(HaveOccurred()) + Expect(cards).To(ContainElements("0228", "0229")) + }) + }) + When("retrieving the CRU cards for a non-existing host", func() { + It("should produce an error", func() { + cards, err = svc.GetCRUCardsForHost("NOPE") + Expect(err).To(HaveOccurred()) + }) + }) + When("retrieving the CRU cards for an FLP with invalid \"cards\" JSON", func() { + It("should produce an error", func() { + cards, err = svc.GetCRUCardsForHost("flp500") + Expect(err).To(HaveOccurred()) + }) + }) + }) + Describe("getting endpoints for a CRU card", func() { + var ( + endpoints []string + err error + ) + When("retrieving the endpoints for a CRU card", func() { + It("should return the correct endpoints", func() { + endpoints, err = svc.GetEndpointsForCRUCard("flp001", "0228") + Expect(err).NotTo(HaveOccurred()) + Expect(endpoints).To(ContainElements("0", "1")) + }) + }) + When("retrieving the endpoints for a non-existing host", func() { + It("should produce an error", func() { + endpoints, err = svc.GetEndpointsForCRUCard("NOPE", "0228") + Expect(err).To(HaveOccurred()) + }) + }) + When("retrieving the endpoints for a non-existing CRU card", func() { + // fixme: probably incorrect behaviour, but I don't want to risk breaking something + It("should not return an empty slice", func() { + endpoints, err = svc.GetEndpointsForCRUCard("flp001", "NOPE") + Expect(endpoints).To(BeEmpty()) + Expect(err).NotTo(HaveOccurred()) + }) + }) + }) + Describe("getting link IDs for a CRU card endpoint", func() { + var ( + linkIDs []string + err error + ) + When("retrieving the link IDs for a CRU card endpoint", func() { + It("should return the correct link IDs", func() { + linkIDs, err = svc.GetLinkIDsForCRUEndpoint("flp001", "0228", "0", false) + Expect(err).NotTo(HaveOccurred()) + Expect(linkIDs).To(ContainElements("0", "1", "2", "10")) + }) + It("should return the correct enabled link IDs", func() { + linkIDs, err = svc.GetLinkIDsForCRUEndpoint("flp001", "0228", "0", true) + Expect(err).NotTo(HaveOccurred()) + Expect(linkIDs).To(ContainElements("0", "2")) + }) + }) + When("retrieving the link IDs for a non-existing host", func() { + It("should produce an error", func() { + linkIDs, err = svc.GetLinkIDsForCRUEndpoint("NOPE", "0228", "0", true) + Expect(err).To(HaveOccurred()) + }) + }) + When("retrieving the link IDs for a non-existing CRU card", func() { + It("should produce an error", func() { + linkIDs, err = svc.GetLinkIDsForCRUEndpoint("flp001", "NOPE", "0", true) + Expect(err).To(HaveOccurred()) + }) + }) + When("retrieving the link IDs for a non-existing endpoint", func() { + It("should produce an error", func() { + linkIDs, err = svc.GetLinkIDsForCRUEndpoint("flp001", "0228", "NOPE", true) + Expect(err).To(HaveOccurred()) + }) + }) + When("trying to retrieve the link IDs for an FLP belonging to a CRORC detector", func() { + It("should produce an error", func() { + linkIDs, err = svc.GetLinkIDsForCRUEndpoint("flp146", "0110", "0", true) + Expect(err).To(HaveOccurred()) + }) + }) + }) + + Describe("getting aliased link IDs for a detector", func() { + var ( + linkIDs []string + err error + ) + When("retrieving the link IDs for a detector", func() { + It("should return the correct link IDs", func() { + linkIDs, err = svc.GetAliasedLinkIDsForDetector("ABC", false) + Expect(err).NotTo(HaveOccurred()) + Expect(linkIDs).To(Equal([]string{"01", "123", "400", "58", "59", "600", "62", "63", "a-b_c=d", "string"})) + }) + }) + When("retrieving the active link IDs for a detector", func() { + It("should return the correct link IDs", func() { + linkIDs, err = svc.GetAliasedLinkIDsForDetector("ABC", true) + Expect(err).NotTo(HaveOccurred()) + Expect(linkIDs).To(Equal([]string{"01", "400", "58", "600", "string"})) + }) + }) + When("retrieving the link IDs for a non-existing detector", func() { + It("should produce an error", func() { + linkIDs, err = svc.GetAliasedLinkIDsForDetector("NOPE", false) + Expect(err).To(HaveOccurred()) + }) + }) + When("retrieving the link IDs for a detector spelled lower-case", func() { + // not supported + It("should produce an error", func() { + linkIDs, err = svc.GetAliasedLinkIDsForDetector("abc", false) + Expect(err).To(HaveOccurred()) + }) + }) + When("retrieving the link IDs for a detector without readoutcard config", func() { + It("should produce an error", func() { + linkIDs, err = svc.GetAliasedLinkIDsForDetector("DEF", false) + Expect(err).To(HaveOccurred()) + }) + }) + }) + + // TODO: + // GetRuntimeEntry (currently not supporting yaml backend) + // SetRuntimeEntry (currently not supporting yaml backend) + // GetRuntimeEntries (currently not supporting yaml backend) + // ListRuntimeEntries (currently not supporting yaml backend) + // as far as i can tell, all of those could rather easily support yaml backend and share the code paths with + // the consul backend, thus allow us to test them well without requiring a consul instance + + Describe("listing components", func() { + var ( + components []string + err error + ) + When("the list of components is retrieved", func() { + It("should have the expected components", func() { + components, err = svc.ListComponents() + Expect(err).NotTo(HaveOccurred()) + Expect(components).To(ContainElements("qc")) + // there might be also 'readout' in components, but it is put by another Describe node, + // so i would not treat it as guaranteed to be there. + }) + }) + }) + + Describe("listing component entries", func() { + var ( + query *componentcfg.EntriesQuery + entries []string + err error + ) + When("the list of entries for a component is retrieved", func() { + // fixme: the tested function does not perform run type and role name matching, it just takes + // everything under the provided component. i'm not sure if this is correct. + It("should have the expected entries listed in the test yaml file", func() { + query, err = componentcfg.NewEntriesQuery("qc/ANY/any") + entries, err = svc.ListComponentEntries(query) + Expect(err).NotTo(HaveOccurred()) + Expect(entries).To(ContainElements( + "ANY/any/entry1", + "ANY/any/entry10", + "ANY/any/entry11", + "ANY/any/entry12", + "ANY/any/sub/entry12", + "ANY/role1/entry1", + "PHYSICS/role1/entry1", + "PHYSICS/role1/entry2", + "TECHNICAL/any/entry", + )) + // there might be also a few other elements which are added in different Describe nodes, + // which we do not look for, as they are not guaranteed to be there. + }) + }) + }) + + Describe("invalidating template cache", func() { + var ( + payload string + query *componentcfg.Query + err error + ) + When("requesting an entry after having invalidated cache", func() { + It("should provide a valid entry", func() { + query, err = componentcfg.NewQuery("qc/ANY/any/entry10") + Expect(err).NotTo(HaveOccurred()) + varStack := make(map[string]string) + varStack["var1"] = "hello" + payload, err = svc.GetAndProcessComponentConfiguration(query, varStack) + Expect(err).NotTo(HaveOccurred()) + Expect(payload).To(Equal("hello world")) + + svc.InvalidateComponentTemplateCache() + payload, err = svc.GetAndProcessComponentConfiguration(query, varStack) + Expect(err).NotTo(HaveOccurred()) + Expect(payload).To(Equal("hello world")) + }) + }) + }) + }) +}) diff --git a/apricot/local/service_test.yaml b/apricot/local/service_test.yaml new file mode 100644 index 000000000..4d15acac4 --- /dev/null +++ b/apricot/local/service_test.yaml @@ -0,0 +1,197 @@ +o2: + components: + qc: + TECHNICAL: + any: + entry: "config" + PHYSICS: + role1: + entry1: "entry1 config PHYSICS role1" + entry2: "entry2 config PHYSICS role1" + ANY: + role1: + entry1: "entry1 config ANY role1" + any: + entry1: "entry1 config ANY any" + entry10: "{{ var1 }} {% include \"entry11\" %}" + entry11: "world" + entry12: "hello {% include \"sub/entry12\" %}" + sub: + entry12: "world" + readoutcard: + flp001: + cru: + "0228": + "0": '{ + "cru": { + "key" : "value" + }, + "links": { + "enabled": "false", + "gbtMux": "TTC", + "feeId": "0x2" + }, + "link0": { + "enabled": "true", + "gbtMux": "ttc", + "feeId": "0x680" + }, + "link1": { + "enabled": "false", + "gbtMux": "ttc", + "feeId": "0x681" + }, + "link2": { + "enabled": "true", + "gbtMux": "ttc", + "feeId": "0x682" + }, + "link10": { + "enabled": "false", + "gbtMux": "ttc", + "feeId": "0x683" + } + }' + "1": '{ + "cru": { + "key" : "value" + }, + "links": { + "enabled": "false", + "gbtMux": "TTC", + "feeId": "0x2" + }, + "link0": { + "enabled": "true", + "gbtMux": "ttc", + "feeId": "0x6c0" + }, + "link1": { + "enabled": "false", + "gbtMux": "ttc", + "feeId": "0x6c1" + }, + "link2": { + "enabled": "false", + "gbtMux": "ttc", + "feeId": "0x6c2" + }, + "link3": { + "enabled": "false", + "gbtMux": "ttc", + "feeId": "0x6c3" + } + }' + "0229": + "0": '{ + "cru": { + "key" : "value" + }, + "links": { + "enabled": "true", + "gbtMux": "TTC", + "feeId": "0x2" + }, + "link0": { + "enabled": "true", + "gbtMux": "ttc", + "feeId": "0x680" + } + }' + "1": '{ + "cru": { + "key" : "value" + }, + "links": { + "enabled": "true", + "gbtMux": "TTC", + "feeId": "0x2" + }, + "link0": { + "enabled": "true", + "gbtMux": "ttc", + "feeId": "0x6c0" + } + }' + flp146: + crorc: + "0110": + "0": "{}" + runtime: + aliecs: + defaults: + key1: value1 + vars: + key2: value2 + hardware: + detectors: + ABC: + flps: + flp001: + cards: "{ \"key\" : \"value\" }" + aliases: ' + { + "flp": { + "alias": "SM 0-1-2-3-4-14-15-16-17" + }, + "cards": { + "0228:0": { + "alias": "SM 14-15-16-17 A-Side", + "links": { + "10": {"alias": "a-b_c=d"}, + "2": {"alias": "string"}, + "1": {"alias": "123"}, + "0": {"alias": "01"} + } + }, + "0228:1": { + "alias": "SM 14-15-16-17 C-Side", + "links": { + "0": {"alias": "58"}, + "1": {"alias": "59"}, + "2": {"alias": "62"}, + "3": {"alias": "63"} + } + }, + "0229:0": { + "alias": "SM 0-1-2-3-4 A-Side", + "links": { + "0": {"alias": "400"} + } + }, + "0229:1": { + "alias": "SM 0-1-2-3-4 C-Side", + "links": { + "0": {"alias": "600"} + } + } + } + }' + DEF: + flps: + flp002: + cards: "{ \"key\" : \"value\" }" + flp003: + cards: "{ \"key\" : \"value\" }" + TRG: + flps: + flp100: + cards: "{ \"key\" : \"value\" }" + XYZ: + flps: {} + flps: + flp001: + cards: "{ + \"0\": {\"serial\": \"0228\", \"endpoint\": \"0\"}, + \"1\": {\"serial\": \"0229\", \"endpoint\": \"0\"}, + \"2\": {\"serial\": \"0228\", \"endpoint\": \"1\"}, + \"3\": {\"serial\": \"0229\", \"endpoint\": \"1\"} + }" + flp002: + cards: "{ \"key\" : \"value\" }" + flp003: + cards: "{ \"key\" : \"value\" }" + flp100: + cards: "{ \"key\" : \"value\" }" + flp500: + cards: "invalid json{" \ No newline at end of file diff --git a/apricot/local/servicehttp.go b/apricot/local/servicehttp.go new file mode 100644 index 000000000..c7f3c9fe4 --- /dev/null +++ b/apricot/local/servicehttp.go @@ -0,0 +1,658 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2019-2023 CERN and copyright holders of ALICE O². + * Author: Claire Guyot + * Author: Teo Mrnjavac + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package local + +import ( + "encoding/json" + "fmt" + "net/http" + "sort" + "strconv" + "strings" + "time" + + _ "github.com/AliceO2Group/Control/apricot/docs" + apricotpb "github.com/AliceO2Group/Control/apricot/protos" + "github.com/AliceO2Group/Control/common/logger/infologger" + "github.com/AliceO2Group/Control/common/system" + "github.com/AliceO2Group/Control/configuration" + "github.com/AliceO2Group/Control/configuration/componentcfg" + "github.com/gorilla/mux" + "github.com/spf13/viper" + httpSwagger "github.com/swaggo/http-swagger/v2" +) + +type HttpService struct { + svc configuration.Service +} + +// @title O² Apricot REST API +// @version 1.0 +// @description REST API for ALICE O² Apricot configuration service + +// @contact.name O² FLP support +// @contact.url https://alice-flp.docs.cern.ch/ +// @contact.email alice-o2-flp-support@cern.ch + +// @externalDocs.description AliECS handbook +// @externalDocs.url https://alice-flp.docs.cern.ch/aliecs/handbook/ +func newHandlerForHttpService(httpsvc *HttpService) *mux.Router { + router := mux.NewRouter() + // documentation endpoint + _ = router.PathPrefix("/docs/").Handler(httpSwagger.WrapHandler) + + // component configuration API + + // GET /components + apiComponents := router.PathPrefix("/components").Subrouter() + apiComponents.HandleFunc("", httpsvc.ApiListComponents).Methods(http.MethodGet) + apiComponents.HandleFunc("/", httpsvc.ApiListComponents).Methods(http.MethodGet) + // POST /components/_invalidate_cache + apiComponents.HandleFunc("/_invalidate_cache", httpsvc.ApiInvalidateCache).Methods(http.MethodPost) + + // GET /components/{component} + apiComponentsEntries := router.PathPrefix("/components/{component}").Subrouter() + // GET /components/{component} returns all, raw is ignored + apiComponentsEntries.HandleFunc("", httpsvc.ApiListComponentEntries).Methods(http.MethodGet) + apiComponentsEntries.HandleFunc("/", httpsvc.ApiListComponentEntries).Methods(http.MethodGet) + // runtype = {runtype} rolename = any, raw excludes ANY runtype, if false returns all + apiComponentsEntries.HandleFunc("/{runtype}", httpsvc.ApiListComponentEntries).Methods(http.MethodGet) + apiComponentsEntries.HandleFunc("/{runtype}/", httpsvc.ApiListComponentEntries).Methods(http.MethodGet) + // runtype = {runtype} rolename = {rolename}, raw excludes ANY runtype and any rolename, if false returns all + apiComponentsEntries.HandleFunc("/{runtype}/{rolename}", httpsvc.ApiListComponentEntries).Methods(http.MethodGet) + apiComponentsEntries.HandleFunc("/{runtype}/{rolename}/", httpsvc.ApiListComponentEntries).Methods(http.MethodGet) + + apiComponentQuery := router.PathPrefix("/components/{component}/{runtype}/{rolename}/{remainder:.*}").Subrouter() + // GET /components/{component}/{runtype}/{rolename}/{remainder:.*} with '/resolve' within the remainder, + // assumes this is not a raw path, returns a raw path like {component}/{runtype}/{rolename}/{entry} + apiComponentQuery.HandleFunc("/resolve", httpsvc.ApiResolveComponentQuery).Methods(http.MethodGet) + // GET /components/{component}/{runtype}/{rolename}/{remainder:.*}, accepts raw or non-raw path, returns payload + // that may be processed or not depending on process=true or false + apiComponentQuery.HandleFunc("", httpsvc.ApiGetComponentConfiguration).Methods(http.MethodGet) + apiComponentQuery.HandleFunc("/", httpsvc.ApiGetComponentConfiguration).Methods(http.MethodGet) + + // inventory API + + apiInventoryFlps := router.PathPrefix("/inventory/flps").Subrouter() + apiInventoryFlps.HandleFunc("", httpsvc.ApiGetFlps).Methods(http.MethodGet) + apiInventoryFlps.HandleFunc("/", httpsvc.ApiGetFlps).Methods(http.MethodGet) + apiInventoryFlps.HandleFunc("/{format}", httpsvc.ApiGetFlps).Methods(http.MethodGet) + + apiInventoryDetectors := router.PathPrefix("/inventory/detectors").Subrouter() + apiInventoryDetectors.HandleFunc("", httpsvc.ApiGetDetectorsInventory).Methods(http.MethodGet) + apiInventoryDetectors.HandleFunc("/", httpsvc.ApiGetDetectorsInventory).Methods(http.MethodGet) + apiInventoryDetectors.HandleFunc("/{format}", httpsvc.ApiGetDetectorsInventory).Methods(http.MethodGet) + + apiInventoryDetectorFlps := router.PathPrefix("/inventory/detectors/{detector}/flps").Subrouter() + apiInventoryDetectorFlps.HandleFunc("", httpsvc.ApiGetDetectorFlps).Methods(http.MethodGet) + apiInventoryDetectorFlps.HandleFunc("/", httpsvc.ApiGetDetectorFlps).Methods(http.MethodGet) + apiInventoryDetectorFlps.HandleFunc("/{format}", httpsvc.ApiGetDetectorFlps).Methods(http.MethodGet) + + return router +} + +func NewHttpService(service configuration.Service) (svr *http.Server) { + httpsvc := &HttpService{ + svc: service, + } + handler := newHandlerForHttpService(httpsvc) + + httpsvr := &http.Server{ + Handler: handler, + Addr: ":" + strconv.Itoa(viper.GetInt("httpListenPort")), + WriteTimeout: 15 * time.Second, + ReadTimeout: 15 * time.Second, + } + + // async-start of http Service and capture error + go func() { + log.WithField("port", viper.GetInt("httpListenPort")). + WithField("level", infologger.IL_Support). + Info("HTTP service started") + + err := httpsvr.ListenAndServe() + if err != nil { + log.WithError(err).Error("HTTP service returned error") + } + }() + return httpsvr +} + +// ApiListComponents lists configuration components +// +// @Summary List Apricot-managed configuration components +// @Description Returns a list of all configuration components managed by Apricot +// @Tags component configuration +// @Produce json +// @Produce plain +// @Param format query string false "Output format, json or text" Enums(json, text) Default(text) +// @Success 200 {array} string "List of components, either as JSON array or comma-separated plain text" +// @Failure 500 {string} string "Internal server error" +// @Router /components [get] +func (httpsvc *HttpService) ApiListComponents(w http.ResponseWriter, r *http.Request) { + queryArgs := r.URL.Query() + format := queryArgs.Get("format") + switch format { + case "json": + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + components, err := httpsvc.svc.ListComponents() + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + out, _ := json.MarshalIndent(err, "", "\t") + _, _ = fmt.Fprintln(w, string(out)) + return + } + + sort.Strings(components) + + response, err := json.MarshalIndent(components, "", "\t") + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + out, _ := json.MarshalIndent(err, "", "\t") + _, _ = fmt.Fprintln(w, string(out)) + return + } + _, _ = fmt.Fprintln(w, string(response)) + return + + case "text": + fallthrough + default: + w.Header().Set("Content-Type", "text/plain") + w.WriteHeader(http.StatusOK) + + components, err := httpsvc.svc.ListComponents() + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + _, _ = fmt.Fprintln(w, err) + return + } + + sort.Strings(components) + + response := strings.Join(components, "\n") + _, _ = fmt.Fprintln(w, string(response)) + } +} + +// ApiInvalidateCache clears the component template cache +// +// @Summary Clear cache of Apricot-managed configuration component templates +// @Description Invalidates all cached templates for all configuration components managed by Apricot +// @Tags component configuration +// @Produce plain +// @Success 200 +// @Router /components/_invalidate_cache [post] +func (httpsvc *HttpService) ApiInvalidateCache(w http.ResponseWriter, r *http.Request) { + log.WithField("level", infologger.IL_Support). + Debug("invalidating component template cache") + httpsvc.svc.InvalidateComponentTemplateCache() + w.WriteHeader(http.StatusOK) + _, _ = fmt.Fprintln(w, "OK") +} + +// ApiListComponentEntries lists all entries for a given configuration component +// +// @Summary Lists all entries for an Apricot-managed configuration component +// @Description Returns a list of all entries belonging to a given configuration component corresponding to the given query. The component must be provided. If the run type is not provided, entries for all run types are returned. If the role name is not provided, entries for all role names are returned. If the raw flag is true, AApricot will not attempt to include "ANY"/"any" run types or role names, and will return only the entries stored under the provided Consul path. If the raw flag is false, Apricot will include all entries for the given component, run type and role name, including entries stored under "ANY" and "any" paths corresponding to fallback values valid for all run types or role names. +// @Tags component configuration +// @Produce json +// @Produce plain +// @Param format query string false "Output format, json or text" Enums(json, text) Default(text) +// @Param raw query boolean false "If true, the query returns strictly Consul paths without catching all run types or role names with ANY/any" Default(false) +// @Param component path string true "Configuration component" +// @Param runtype path string false "O² Run type, must be capitalized" Default(ANY) +// @Param rolename path string false "Role name" Default(any) +// @Success 200 {array} string "List of entries, either as JSON array or comma-separated plain text" +// @Failure 400 {string} string "Bad request, if the run type is invalid" +// @Failure 500 {string} string "Internal server error" +// @Router /components/{component} [get] +// @Router /components/{component}/{runtype} [get] +// @Router /components/{component}/{runtype}/{rolename} [get] +func (httpsvc *HttpService) ApiListComponentEntries(w http.ResponseWriter, r *http.Request) { + // GET /components/{component} returns all, raw is ignored + // runtype = {runtype} rolename = any, raw excludes ANY runtype, if false returns all + // runtype = {runtype} rolename = {rolename}, raw excludes ANY runtype and any rolename, if false returns all + + queryParams := mux.Vars(r) + component, hasComponent := queryParams["component"] + if !hasComponent { + w.WriteHeader(http.StatusBadRequest) + _, _ = fmt.Fprintln(w, "component name not provided") + return + } + + runtypeS, hasRuntype := queryParams["runtype"] + + runType := apricotpb.RunType_NULL + if hasRuntype { + runtypeS = strings.ToUpper(runtypeS) + runTypeInt, isRunTypeValid := apricotpb.RunType_value[runtypeS] + if !isRunTypeValid { + w.WriteHeader(http.StatusBadRequest) + _, _ = fmt.Fprintln(w, "runtype not valid") + return + } else { + runType = apricotpb.RunType(runTypeInt) + } + } + + rolename, hasRolename := queryParams["rolename"] + + queryArgs := r.URL.Query() + rawS := queryArgs.Get("raw") + raw, err := strconv.ParseBool(rawS) + if err != nil { + raw = false + } + + entries, err := httpsvc.svc.ListComponentEntries(&componentcfg.EntriesQuery{ + Component: component, + RunType: runType, + RoleName: rolename, + }) + + filterPrefixes := make(map[string]struct{}) + if hasRuntype { + if hasRolename { // we filter for runtype and rolename, and if !raw, also combos with ANY and any + filterPrefixes[runtypeS+"/"+rolename] = struct{}{} + if !raw { + filterPrefixes["ANY"+"/"+rolename] = struct{}{} + filterPrefixes[runtypeS+"/any"] = struct{}{} + filterPrefixes["ANY/any"] = struct{}{} + } + } else { // no rolename provided, we only filter for runtype and ANY if !raw + filterPrefixes[runtypeS] = struct{}{} + if !raw { + filterPrefixes["ANY"] = struct{}{} + } + } + } + + filteredEntries := make([]string, 0) + if hasRuntype { // if there's any filtering to do + for _, entry := range entries { + for filterPrefix := range filterPrefixes { + if strings.HasPrefix(entry, filterPrefix) { + filteredEntries = append(filteredEntries, entry) + } + } + } + } else { // no filtering, return all entries + filteredEntries = entries + } + + format := queryArgs.Get("format") + + switch format { + case "json": + w.Header().Set("Content-Type", "application/json") + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + out, _ := json.MarshalIndent(err, "", "\t") + _, _ = fmt.Fprintln(w, string(out)) + return + } + + w.WriteHeader(http.StatusOK) + + sort.Strings(filteredEntries) + + response, err := json.MarshalIndent(filteredEntries, "", "\t") + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + out, _ := json.MarshalIndent(err, "", "\t") + _, _ = fmt.Fprintln(w, string(out)) + return + } + _, _ = fmt.Fprintln(w, string(response)) + return + + case "text": + fallthrough + default: + w.Header().Set("Content-Type", "text/plain") + + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + _, _ = fmt.Fprintln(w, err) + return + } + + w.WriteHeader(http.StatusOK) + + sort.Strings(filteredEntries) + + response := strings.Join(filteredEntries, "\n") + _, _ = fmt.Fprintln(w, string(response)) + } +} + +// ApiResolveComponentQuery resolves a query for a given component, run type, role name and entry key +// +// @Summary Resolves a query for a given component, run type, role name and entry key +// @Description Returns a resolved path for a given component, run type, role name and entry key. The path points to an actual existing entry in Consul, resolving ANY run type and any rolename wildcards. +// @Tags component configuration +// @Produce plain +// @Param component path string true "Configuration component" +// @Param runtype path string true "O² Run type, must be capitalized" +// @Param rolename path string true "Role name" +// @Param entry path string true "Entry key" +// @Success 200 {string} string "Resolved path for the queried entry" +// @Failure 400 {string} string "Bad request, if a parameter is invalid" +// @Failure 500 {string} string "Internal server error" +// @Router /components/{component}/{runtype}/{rolename}/{entry}/resolve [get] +func (httpsvc *HttpService) ApiResolveComponentQuery(w http.ResponseWriter, r *http.Request) { + // GET /components/{component}/{runtype}/{rolename}/{entry}/resolve, assumes this is not a raw path, returns a raw path + // like {component}/{runtype}/{rolename}/{entry} + + queryParams := mux.Vars(r) + component, hasComponent := queryParams["component"] + if !hasComponent { + w.WriteHeader(http.StatusBadRequest) + _, _ = fmt.Fprintln(w, "component name not provided") + return + } + + runtypeS, hasRuntype := queryParams["runtype"] + runType := apricotpb.RunType_NULL + if hasRuntype { + runtypeS = strings.ToUpper(runtypeS) + runTypeInt, isRunTypeValid := apricotpb.RunType_value[runtypeS] + if !isRunTypeValid { + w.WriteHeader(http.StatusBadRequest) + _, _ = fmt.Fprintln(w, "runtype not valid") + return + } else { + runType = apricotpb.RunType(runTypeInt) + } + } else { + w.WriteHeader(http.StatusBadRequest) + _, _ = fmt.Fprintln(w, "runtype not provided") + return + } + + rolename, hasRolename := queryParams["rolename"] + if !hasRolename { + w.WriteHeader(http.StatusBadRequest) + _, _ = fmt.Fprintln(w, "rolename not provided") + return + } + + entry, hasEntry := queryParams["remainder"] + if !hasEntry { + w.WriteHeader(http.StatusBadRequest) + _, _ = fmt.Fprintln(w, "entry not provided") + return + } + entry = strings.TrimSuffix(entry, "/resolve") + + resolved, err := httpsvc.svc.ResolveComponentQuery(&componentcfg.Query{ + Component: component, + RunType: runType, + RoleName: rolename, + EntryKey: entry, + }) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + _, _ = fmt.Fprintln(w, err) + return + } + + resolvedStr := resolved.Path() + + w.WriteHeader(http.StatusOK) + _, _ = fmt.Fprintln(w, resolvedStr) +} + +// ApiGetComponentConfiguration returns the processed configuration payload for a given component, run type, role name and entry key +// +// @Summary Returns a configuration payload for a given component, run type, role name and entry key +// @Description The provided component, run type, role name and entry key are used to query the configuration service for a configuration entry, which is then processed in the O² Apricot template system to produce the final payload, and returned as string.. +// @Tags component configuration +// @Produce plain +// @Param process query boolean false "If true, template processing is performed to produce the final payload; if false, the entry is returned verbatim. In the true case, any number of additional string key-value pairs may be passed as query parameters (e.g. ?process=true&mykey1=myvalue1&mykey2=myvalue2), which are then fed into the template system as variables that affect configuration payload generation" Default(false) +// @Param component path string true "Configuration component" +// @Param runtype path string true "O² Run type, must be capitalized" +// @Param rolename path string true "Role name" +// @Param entry path string true "Entry key" +// @Success 200 {string} string "Configuration payload for the queried entry, either processed or verbatim depending on the process flag" +// @Failure 400 {string} string "Bad request, if a parameter is invalid" +// @Failure 500 {string} string "Internal server error" +// @Router /components/{component}/{runtype}/{rolename}/{entry} [get] +func (httpsvc *HttpService) ApiGetComponentConfiguration(w http.ResponseWriter, r *http.Request) { + // GET /components/{component}/{runtype}/{rolename}/{entry}, accepts raw or non-raw path, returns payload + // that may be processed or not depending on process=true or false + + queryParams := mux.Vars(r) + component, hasComponent := queryParams["component"] + if !hasComponent { + w.WriteHeader(http.StatusBadRequest) + _, _ = fmt.Fprintln(w, "component name not provided") + return + } + + runtypeS, hasRuntype := queryParams["runtype"] + runType := apricotpb.RunType_NULL + if hasRuntype { + runtypeS = strings.ToUpper(runtypeS) + runTypeInt, isRunTypeValid := apricotpb.RunType_value[runtypeS] + if !isRunTypeValid { + w.WriteHeader(http.StatusBadRequest) + _, _ = fmt.Fprintln(w, "runtype not valid") + return + } else { + runType = apricotpb.RunType(runTypeInt) + } + } else { + w.WriteHeader(http.StatusBadRequest) + _, _ = fmt.Fprintln(w, "runtype not provided") + return + } + + rolename, hasRolename := queryParams["rolename"] + if !hasRolename { + w.WriteHeader(http.StatusBadRequest) + _, _ = fmt.Fprintln(w, "rolename not provided") + return + } + + entry, hasEntry := queryParams["remainder"] + if !hasEntry { + w.WriteHeader(http.StatusBadRequest) + _, _ = fmt.Fprintln(w, "entry not provided") + return + } + entry = strings.TrimSuffix(entry, "/") + + queryArgs := r.URL.Query() + processS := queryArgs.Get("process") + process, err := strconv.ParseBool(processS) + if err != nil { + process = false + } + if queryArgs.Has("process") { + queryArgs.Del("process") + } + + varStack := make(map[string]string) + for k, v := range queryArgs { + if len(v) > 0 { + varStack[k] = v[0] + } + } + + var payload string + if process { + payload, err = httpsvc.svc.GetAndProcessComponentConfiguration(&componentcfg.Query{ + Component: component, + RunType: runType, + RoleName: rolename, + EntryKey: entry, + }, varStack) + } else { + payload, err = httpsvc.svc.GetComponentConfiguration(&componentcfg.Query{ + Component: component, + RunType: runType, + RoleName: rolename, + EntryKey: entry, + }) + } + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + _, _ = fmt.Fprintln(w, err) + return + } + + w.WriteHeader(http.StatusOK) + _, _ = fmt.Fprintln(w, payload) +} + +// ApiGetFlps returns the list of FLPs in the cluster known to Apricot +// +// @Summary Returns the list of FLPs in the cluster known to Apricot +// @Description Returns the list of all Apricot-managed hosts in the cluster that are known to be FLPs, newline-separated or JSON depending on the format parameter +// @Tags cluster inventory +// @Param format path string false "Output format, json or text" Enums(json, text) Default(text) +// @Produce plain +// @Success 200 {string} string "List of FLPs in the cluster known to Apricot, newline-separated or JSON depending on the format parameter" +// @Router /inventory/flps/{format} [get] +func (httpsvc *HttpService) ApiGetFlps(w http.ResponseWriter, r *http.Request) { + httpsvc.ApiGetHostInventory(w, r, "") +} + +// ApiGetDetectorFlps returns the list of FLPs in the cluster that serve a given detector +// +// @Summary Returns the list of FLPs in the cluster that serve a given detector +// @Description Returns the list of all Apricot-managed hosts in the cluster that are known to be FLPs and serving the given detector, newline-separated or JSON depending on the format parameter +// @Tags cluster inventory +// @Param format path string false "Output format, json or text" Enums(json, text) Default(text) +// @Produce plain +// @Success 200 {string} string "List of FLPs in the cluster known to Apricot, newline-separated or JSON depending on the format parameter" +// @Router /inventory/detectors/{detector}/flps/{format} [get] +func (httpsvc *HttpService) ApiGetDetectorFlps(w http.ResponseWriter, r *http.Request) { + queryParam := mux.Vars(r) + detector := queryParam["detector"] + _, err := system.IDString(detector) + if err != nil { + log.WithError(err).Warn("Error, the detector name provided is not valid.") + } else { + httpsvc.ApiGetHostInventory(w, r, detector) + } +} + +func (httpsvc *HttpService) ApiGetHostInventory(w http.ResponseWriter, r *http.Request, detector string) { + hosts, err := httpsvc.svc.GetHostInventory(detector) + if err != nil { + log.WithError(err).Warn("Error, could not retrieve host list.") + } + httpsvc.ApiPrintClusterInformation(w, r, hosts, nil) +} + +// ApiGetDetectorsInventory returns the list of detectors belonging to the installed instance known to Apricot +// +// @Summary Returns the list of detectors belonging to the installed instance known to Apricot +// @Description Returns the list of all detectors known to Apricot that belong to the installed instance, newline-separated or JSON depending on the format parameter +// @Tags cluster inventory +// @Param format path string false "Output format, json or text" Enums(json, text) Default(text) +// @Produce plain +// @Success 200 {string} string "List of detectors in the instance known to Apricot, newline-separated or JSON depending on the format parameter" +// @Router /inventory/detectors/{format} [get] +func (httpsvc *HttpService) ApiGetDetectorsInventory(w http.ResponseWriter, r *http.Request) { + inventory, err := httpsvc.svc.GetDetectorsInventory() + if err != nil { + log.WithError(err).Warn("Error, could not retrieve detectors inventory list.") + } + httpsvc.ApiPrintClusterInformation(w, r, nil, inventory) +} + +func (httpsvc *HttpService) ApiPrintClusterInformation(w http.ResponseWriter, r *http.Request, hosts []string, inventory map[string][]string) { + queryParam := mux.Vars(r) + format := "" + format = queryParam["format"] + if format == "" { + format = "text" + } + switch format { + case "json": + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + var result []byte + var err error + if hosts != nil { + result, err = json.MarshalIndent(hosts, "", "\t") + if err != nil { + log.WithError(err).Warn("Error, could not marshal hosts.") + } + } else if inventory != nil { + result, err = json.MarshalIndent(inventory, "", "\t") + if err != nil { + log.WithError(err).Warn("Error, could not marshal inventory.") + } + } + _, err = fmt.Fprintln(w, string(result)) + if err != nil { + log.WithField("result", string(result)). + WithField(infologger.Level, infologger.IL_Support). + WithError(err). + Warn("Error, could not write a part of HTTP response to response writer.") + } + case "text": + fallthrough + default: + w.Header().Set("Content-Type", "text/plain") + w.WriteHeader(http.StatusOK) + if hosts != nil { + for _, hostname := range hosts { + _, err := fmt.Fprintf(w, "%s\n", hostname) + if err != nil { + log.WithField(infologger.Level, infologger.IL_Support). + WithError(err). + Warn("Error, could not write a part of HTTP response to response writer.") + } + } + } else if inventory != nil { + for detector, flps := range inventory { + _, err := fmt.Fprintf(w, "%s\n", detector) + if err != nil { + log.WithField(infologger.Level, infologger.IL_Support). + WithError(err). + Warn("Error, could not write a part of HTTP response to response writer.") + } + for _, hostname := range flps { + _, err = fmt.Fprintf(w, "\t%s\n", hostname) + if err != nil { + log.WithField(infologger.Level, infologger.IL_Support). + WithError(err). + Warn("Error, could not write a part of HTTP response to response writer.") + } + } + } + } + } +} diff --git a/apricot/local/servicehttp_test.go b/apricot/local/servicehttp_test.go new file mode 100644 index 000000000..14a6533b3 --- /dev/null +++ b/apricot/local/servicehttp_test.go @@ -0,0 +1,402 @@ +package local + +import ( + "encoding/json" + "github.com/gorilla/mux" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "net/http" + "net/http/httptest" +) + +var _ = Describe("HTTP apricot service", func() { + + // We test the handler directly to avoid any issues with finding a free port, waiting until the server responds, + // freeing the socket/port, concurrency of the executed tests. This way we can test just the API. + var ( + httpSvc *HttpService + handler *mux.Router + recorder *httptest.ResponseRecorder + err error + ) + + Context("with YAML file backend", func() { + BeforeEach(func() { + svc, err := NewService("file://" + *tmpDir + "/" + serviceHTTPConfigFile) + Expect(err).NotTo(HaveOccurred()) + httpSvc = &HttpService{svc} + handler = newHandlerForHttpService(httpSvc) + recorder = httptest.NewRecorder() + }) + + Describe("getting the list of hosts", func() { + Context("expecting JSON output", func() { + When("the list of hosts is retrieved for all detectors", func() { + BeforeEach(func() { + req, err := http.NewRequest("GET", "/inventory/flps/json", nil) + Expect(err).NotTo(HaveOccurred()) + handler.ServeHTTP(recorder, req) + }) + It("should contain all the hosts for all the detectors listed in the test YAML file at o2/hardware/detectors", func() { + Expect(recorder.Code).To(Equal(http.StatusOK)) + + var hosts []string + err = json.NewDecoder(recorder.Body).Decode(&hosts) + Expect(err).NotTo(HaveOccurred()) + + Expect(hosts).To(HaveLen(4)) + Expect(hosts).To(ContainElements("flp001", "flp002", "flp003", "flp100")) + }) + }) + When("the list of hosts is retrieved for a valid detector", func() { + BeforeEach(func() { + req, err := http.NewRequest("GET", "/inventory/detectors/ITS/flps/json", nil) + Expect(err).NotTo(HaveOccurred()) + handler.ServeHTTP(recorder, req) + }) + It("should contain all the hosts for the ITS detector in the test YAML file at o2/hardware/detectors", func() { + Expect(recorder.Code).To(Equal(http.StatusOK)) + + var hosts []string + err = json.NewDecoder(recorder.Body).Decode(&hosts) + Expect(err).NotTo(HaveOccurred()) + Expect(hosts).To(ContainElements("flp001")) + }) + }) + When("the list of hosts is retrieved for a detector which is not present in the config store", func() { + BeforeEach(func() { + req, err := http.NewRequest("GET", "/inventory/detectors/TPC/flps/json", nil) + Expect(err).NotTo(HaveOccurred()) + handler.ServeHTTP(recorder, req) + }) + // Perhaps that's OK, but this behaviour is different from the local/service, which returns an error + // in such case. + It("should return an empty list", func() { + Expect(recorder.Code).To(Equal(http.StatusOK)) + + var hosts []string + err = json.NewDecoder(recorder.Body).Decode(&hosts) + Expect(hosts).To(BeEmpty()) + }) + }) + When("the list of hosts is retrieved for an existing detector with no FLPs assigned", func() { + BeforeEach(func() { + req, err := http.NewRequest("GET", "/inventory/detectors/MID/flps/json", nil) + Expect(err).NotTo(HaveOccurred()) + handler.ServeHTTP(recorder, req) + }) + It("should return an empty list", func() { + Expect(recorder.Code).To(Equal(http.StatusOK)) + + var hosts []string + err = json.NewDecoder(recorder.Body).Decode(&hosts) + Expect(hosts).To(BeEmpty()) + }) + }) + }) + Context("expecting plain text output", func() { + When("the list of hosts is retrieved for all detectors", func() { + BeforeEach(func() { + req, err := http.NewRequest("GET", "/inventory/flps", nil) + Expect(err).NotTo(HaveOccurred()) + handler.ServeHTTP(recorder, req) + }) + It("should contain all the hosts for all the detectors listed in the test YAML file at o2/hardware/detectors", func() { + Expect(recorder.Code).To(Equal(http.StatusOK)) + // order is not guaranteed + Expect(recorder.Body.String()).To(ContainSubstring("flp100")) + Expect(recorder.Body.String()).To(ContainSubstring("flp001")) + Expect(recorder.Body.String()).To(ContainSubstring("flp002")) + Expect(recorder.Body.String()).To(ContainSubstring("flp003")) + }) + }) + When("the list of hosts is retrieved for a valid detector", func() { + BeforeEach(func() { + req, err := http.NewRequest("GET", "/inventory/detectors/ITS/flps", nil) + Expect(err).NotTo(HaveOccurred()) + handler.ServeHTTP(recorder, req) + }) + It("should contain all the hosts for the ITS detector in the test YAML file at o2/hardware/detectors", func() { + Expect(recorder.Code).To(Equal(http.StatusOK)) + Expect(recorder.Body.String()).To(Equal("flp001\n")) + }) + }) + }) + }) + Describe("getting the detectors FLP inventory", func() { + Context("expecting JSON output", func() { + var inventory map[string][]string + When("the inventory is retrieved", func() { + BeforeEach(func() { + req, err := http.NewRequest("GET", "/inventory/detectors/json", nil) + Expect(err).NotTo(HaveOccurred()) + handler.ServeHTTP(recorder, req) + }) + It("should contain all the hosts for all the detectors in the test YAML file at o2/hardware/detectors", func() { + Expect(recorder.Code).To(Equal(http.StatusOK)) + + err = json.NewDecoder(recorder.Body).Decode(&inventory) + + Expect(err).NotTo(HaveOccurred()) + Expect(inventory).To(HaveKey("ITS")) + Expect(inventory["ITS"]).To(ContainElements("flp001")) + Expect(inventory).To(HaveKey("HMP")) + Expect(inventory["HMP"]).To(ContainElements("flp002", "flp003")) + Expect(inventory).To(HaveKey("TRG")) + Expect(inventory["TRG"]).To(ContainElements("flp100")) + Expect(inventory).To(HaveKey("MID")) + Expect(inventory["MID"]).To(BeEmpty()) + }) + }) + }) + Context("expecting plain text output", func() { + When("the inventory is retrieved", func() { + BeforeEach(func() { + req, err := http.NewRequest("GET", "/inventory/detectors/text", nil) + Expect(err).NotTo(HaveOccurred()) + handler.ServeHTTP(recorder, req) + }) + It("should contain all the hosts for all the detectors in the test YAML file at o2/hardware/detectors", func() { + Expect(recorder.Code).To(Equal(http.StatusOK)) + Expect(recorder.Body.String()).To(ContainSubstring("ITS\n\tflp001")) + Expect(recorder.Body.String()).To(ContainSubstring("TRG\n\tflp100")) + Expect(recorder.Body.String()).To(ContainSubstring("MID\n")) + Expect(recorder.Body.String()).To(ContainSubstring("HMP\n")) // flps can be in any order, we won't test it + }) + }) + }) + }) + + Describe("getting component configuration", func() { + // fixme: compared to the raw local/service, the HTTP service adds a newline character. + // probably not an issue, but still an inconsistency to be aware of. + When("requesting an entry for a concrete run type and a concrete role", func() { + BeforeEach(func() { + req, err := http.NewRequest("GET", "/components/qc/PHYSICS/role1/entry1", nil) + Expect(err).NotTo(HaveOccurred()) + handler.ServeHTTP(recorder, req) + }) + It("should return the payload for the concrete run type and role", func() { + Expect(recorder.Code).To(Equal(http.StatusOK)) + Expect(recorder.Body.String()).To(Equal("entry1 config PHYSICS role1\n")) + }) + }) + When("requesting an entry for ANY run type and any role", func() { + BeforeEach(func() { + req, err := http.NewRequest("GET", "/components/qc/ANY/any/entry1", nil) + Expect(err).NotTo(HaveOccurred()) + handler.ServeHTTP(recorder, req) + }) + It("should return the payload for ANY/any", func() { + Expect(recorder.Code).To(Equal(http.StatusOK)) + Expect(recorder.Body.String()).To(Equal("entry1 config ANY any\n")) + }) + }) + When("requesting an entry for ANY run type and any role with '/' at the end of the query", func() { + BeforeEach(func() { + req, err := http.NewRequest("GET", "/components/qc/ANY/any/entry1/", nil) + Expect(err).NotTo(HaveOccurred()) + handler.ServeHTTP(recorder, req) + }) + It("should return the payload for ANY/any", func() { + Expect(recorder.Code).To(Equal(http.StatusOK)) + Expect(recorder.Body.String()).To(Equal("entry1 config ANY any\n")) + }) + }) + When("requesting an entry in a subfolder", func() { + BeforeEach(func() { + req, err := http.NewRequest("GET", "/components/qc/ANY/any/sub/entry12", nil) + Expect(err).NotTo(HaveOccurred()) + handler.ServeHTTP(recorder, req) + }) + It("should return the expected payload", func() { + Expect(recorder.Code).To(Equal(http.StatusOK)) + Expect(recorder.Body.String()).To(Equal("world\n")) + }) + }) + }) + + Describe("getting and processing component configuration", func() { + When("requesting an entry which requires including another entry and inserting a variable", func() { + BeforeEach(func() { + req, err := http.NewRequest("GET", "/components/qc/ANY/any/entry10?process=true&var1=hello", nil) + Expect(err).NotTo(HaveOccurred()) + handler.ServeHTTP(recorder, req) + }) + It("should return the processed payload", func() { + Expect(recorder.Code).To(Equal(http.StatusOK)) + Expect(recorder.Body.String()).To(Equal("hello world\n")) + }) + }) + When("requesting an entry which requires including another entry in a different node", func() { + BeforeEach(func() { + req, err := http.NewRequest("GET", "/components/qc/ANY/any/entry12?process=true", nil) + Expect(err).NotTo(HaveOccurred()) + handler.ServeHTTP(recorder, req) + }) + It("should return the processed payload", func() { + Expect(recorder.Code).To(Equal(http.StatusOK)) + Expect(recorder.Body.String()).To(Equal("hello world\n")) + }) + }) + When("requesting an entry which is in a subfolder", func() { + BeforeEach(func() { + req, err := http.NewRequest("GET", "/components/qc/ANY/any/sub/entry12?process=true", nil) + Expect(err).NotTo(HaveOccurred()) + handler.ServeHTTP(recorder, req) + }) + It("should return the processed payload", func() { + Expect(recorder.Code).To(Equal(http.StatusOK)) + Expect(recorder.Body.String()).To(Equal("world\n")) + }) + }) + }) + Describe("resolving a query", func() { + // fixme: compared to the raw local/service, the HTTP service adds a newline character. + // probably not an issue, but still an inconsistency to be aware of. + When("resolving an ANY/any query", func() { + BeforeEach(func() { + req, err := http.NewRequest("GET", "/components/qc/ANY/any/entry1/resolve", nil) + Expect(err).NotTo(HaveOccurred()) + handler.ServeHTTP(recorder, req) + }) + It("should resolve to ANY/any query", func() { + Expect(recorder.Code).To(Equal(http.StatusOK)) + Expect(recorder.Body.String()).To(Equal("qc/ANY/any/entry1\n")) + }) + }) + When("resolving a concrete PHYSICS/role1 query while there is ANY/any entry in the configuration tree", func() { + BeforeEach(func() { + req, err := http.NewRequest("GET", "/components/qc/PHYSICS/role1/entry11/resolve", nil) + Expect(err).NotTo(HaveOccurred()) + handler.ServeHTTP(recorder, req) + }) + It("should resolve to ANY/any query", func() { + Expect(recorder.Code).To(Equal(http.StatusOK)) + Expect(recorder.Body.String()).To(Equal("qc/ANY/any/entry11\n")) + }) + }) + When("resolving a query to an entry in a subfolder", func() { + BeforeEach(func() { + req, err := http.NewRequest("GET", "/components/qc/ANY/any/sub/entry12/resolve", nil) + Expect(err).NotTo(HaveOccurred()) + handler.ServeHTTP(recorder, req) + }) + It("should resolve it correctly", func() { + Expect(recorder.Code).To(Equal(http.StatusOK)) + Expect(recorder.Body.String()).To(Equal("qc/ANY/any/sub/entry12\n")) + }) + }) + }) + + Describe("listing all entries for a component/runtype/rolename", func() { + Context("expecting plain text output", func() { + When("listing all entries (without '/' at the end)", func() { + BeforeEach(func() { + req, err := http.NewRequest("GET", "/components/qc/ANY/any", nil) + Expect(err).NotTo(HaveOccurred()) + handler.ServeHTTP(recorder, req) + }) + It("should return text list with the entries, one per line", func() { + Expect(recorder.Code).To(Equal(http.StatusOK)) + Expect(recorder.Body.String()).To(Equal( + "ANY/any/entry1\n" + + "ANY/any/entry10\n" + + "ANY/any/entry11\n" + + "ANY/any/entry12\n" + + "ANY/any/sub/entry12\n")) + }) + }) + When("listing all entries (with '/' at the end)", func() { + BeforeEach(func() { + req, err := http.NewRequest("GET", "/components/qc/ANY/any/", nil) + Expect(err).NotTo(HaveOccurred()) + handler.ServeHTTP(recorder, req) + }) + It("should return text list with the entries, one per line", func() { + Expect(recorder.Code).To(Equal(http.StatusOK)) + Expect(recorder.Body.String()).To(Equal( + "ANY/any/entry1\n" + + "ANY/any/entry10\n" + + "ANY/any/entry11\n" + + "ANY/any/entry12\n" + + "ANY/any/sub/entry12\n")) + }) + }) + }) + Context("expecting json output", func() { + When("listing all entries", func() { + BeforeEach(func() { + req, err := http.NewRequest("GET", "/components/qc/ANY/any?format=json", nil) + Expect(err).NotTo(HaveOccurred()) + handler.ServeHTTP(recorder, req) + }) + It("should return text list with the entries, one per line", func() { + Expect(recorder.Code).To(Equal(http.StatusOK)) + var hosts []string + err = json.NewDecoder(recorder.Body).Decode(&hosts) + Expect(hosts).To(ContainElements("ANY/any/entry1", "ANY/any/entry10", "ANY/any/entry11", "ANY/any/entry12", "ANY/any/sub/entry12")) + }) + }) + }) + }) + + Describe("listing components", func() { + Context("with text/plain format", func() { + When("the list of components is retrieved", func() { + BeforeEach(func() { + req, err := http.NewRequest("GET", "/components?format=text/plain", nil) + Expect(err).NotTo(HaveOccurred()) + handler.ServeHTTP(recorder, req) + }) + It("should have the expected components", func() { + Expect(recorder.Code).To(Equal(http.StatusOK)) + Expect(recorder.Body.String()).To(Equal("qc\n")) + }) + }) + }) + Context("with json format", func() { + When("the list of components is retrieved", func() { + BeforeEach(func() { + req, err := http.NewRequest("GET", "/components?format=json", nil) + Expect(err).NotTo(HaveOccurred()) + handler.ServeHTTP(recorder, req) + }) + It("should have the expected components", func() { + Expect(recorder.Code).To(Equal(http.StatusOK)) + + var components []string + err = json.NewDecoder(recorder.Body).Decode(&components) + Expect(err).NotTo(HaveOccurred()) + Expect(components).To(HaveLen(1)) + Expect(components).To(ContainElements("qc")) + }) + }) + }) + }) + + Describe("invalidating template cache", func() { + When("requesting an entry after having invalidated cache", func() { + It("should provide a valid entry", func() { + req, err := http.NewRequest("GET", "/components/qc/PHYSICS/role1/entry1", nil) + Expect(err).NotTo(HaveOccurred()) + handler.ServeHTTP(recorder, req) + Expect(recorder.Code).To(Equal(http.StatusOK)) + Expect(recorder.Body.String()).To(Equal("entry1 config PHYSICS role1\n")) + + recorder = httptest.NewRecorder() + req, err = http.NewRequest("POST", "/components/_invalidate_cache", nil) + handler.ServeHTTP(recorder, req) + Expect(recorder.Code).To(Equal(http.StatusOK)) + + recorder = httptest.NewRecorder() + req, err = http.NewRequest("GET", "/components/qc/PHYSICS/role1/entry1", nil) + Expect(err).NotTo(HaveOccurred()) + handler.ServeHTTP(recorder, req) + Expect(recorder.Code).To(Equal(http.StatusOK)) + Expect(recorder.Body.String()).To(Equal("entry1 config PHYSICS role1\n")) + }) + }) + }) + }) +}) diff --git a/apricot/local/servicehttp_test.yaml b/apricot/local/servicehttp_test.yaml new file mode 100644 index 000000000..f84eb30a3 --- /dev/null +++ b/apricot/local/servicehttp_test.yaml @@ -0,0 +1,54 @@ +o2: + components: + qc: + TECHNICAL: + any: + entryA: "config" + entryB: "config" + PHYSICS: + role1: + entry1: "entry1 config PHYSICS role1" + entry2: "entry2 config PHYSICS role1" + ANY: + role1: + entry1: "entry1 config ANY role1" + any: + entry1: "entry1 config ANY any" + entry10: "{{ var1 }} {% include \"entry11\" %}" + entry11: "world" + entry12: "hello {% include \"sub/entry12\" %}" + sub: + entry12: "world" + runtime: + aliecs: + defaults: + key1: value1 + vars: + key2: value2 + hardware: + detectors: + ITS: + flps: + flp001: + cards: "{ \"key\" : \"value\" }" + HMP: + flps: + flp002: + cards: "{ \"key\" : \"value\" }" + flp003: + cards: "{ \"key\" : \"value\" }" + TRG: + flps: + flp100: + cards: "{ \"key\" : \"value\" }" + MID: + flps: {} + flps: + flp001: + cards: "{ \"key\" : \"value\" }" + flp002: + cards: "{ \"key\" : \"value\" }" + flp003: + cards: "{ \"key\" : \"value\" }" + flp100: + cards: "{ \"key\" : \"value\" }" \ No newline at end of file diff --git a/apricot/local/serviceutil.go b/apricot/local/serviceutil.go new file mode 100644 index 000000000..2a743e22c --- /dev/null +++ b/apricot/local/serviceutil.go @@ -0,0 +1,121 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2019-2020 CERN and copyright holders of ALICE O². + * Author: Teo Mrnjavac + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package local + +import ( + "errors" + "fmt" + "strings" + + "github.com/spf13/viper" + + "github.com/AliceO2Group/Control/configuration/cfgbackend" + "github.com/AliceO2Group/Control/configuration/componentcfg" +) + +func (s *Service) queryToAbsPath(query *componentcfg.Query) (absolutePath string, err error) { + if query == nil { + return + } + + absolutePath = query.AbsoluteRaw() + if exists, _ := s.src.Exists(absolutePath); exists { + err = nil + return + } + + err = fmt.Errorf("no payload at configuration path %s", absolutePath) + + return +} + +func (s *Service) getStringMap(path string) map[string]string { + tree, err := s.src.GetRecursive(path) + if err != nil { + log.WithError(err). + WithField("path", path). + Warning("getStringMap from configuration backend failed, possibly rate limited") + return map[string]string{} + } + if tree.Type() == cfgbackend.IT_Map { + responseMap := tree.Map() + theMap := make(map[string]string, len(responseMap)) + trimSpaces := viper.GetBool("trimSpaceInVarsFromConsulKV") + for k, v := range responseMap { + if v.Type() != cfgbackend.IT_Value { + continue + } + if trimmedValue := strings.TrimSpace(v.Value()); v.Value() != trimmedValue { + valueToStore := v.Value() + if trimSpaces { + log.WithError(errors.New("leading or trailing space in value")). + WithField("path", path). + WithField("key", k). + Warning("found unsanitized string value in configuration backend, returning whitespace-trimmed string") + valueToStore = trimmedValue + } else { + log.WithError(errors.New("leading or trailing space in value")). + WithField("path", path). + WithField("key", k). + Warning("found unsanitized string value in configuration backend, returning anyway") + } + theMap[k] = valueToStore + } else { + theMap[k] = v.Value() + } + } + return theMap + } + return map[string]string{} +} + +func (s *Service) resolveComponentQuery(query *componentcfg.Query) (resolved *componentcfg.Query, err error) { + resolved = &componentcfg.Query{} + *resolved = *query + if _, err = s.queryToAbsPath(resolved); err == nil { + // requested path exists, return it + return + } + + resolved = query.WithFallbackRunType() + if _, err = s.queryToAbsPath(resolved); err == nil { + // path with run type ANY exists, return it + return + } + + resolved = query.WithFallbackRoleName() + if _, err = s.queryToAbsPath(resolved); err == nil { + // path with role name "any" exists, return it + return + } + + resolved = resolved.WithFallbackRunType() + if _, err = s.queryToAbsPath(resolved); err == nil { + // path with run type ANY and role name "any" exists, return it + return + } + + return nil, fmt.Errorf("could not resolve configuration path %s", query.AbsoluteRaw()) +} diff --git a/apricot/protos/apricot.pb.go b/apricot/protos/apricot.pb.go new file mode 100644 index 000000000..c24df2de3 --- /dev/null +++ b/apricot/protos/apricot.pb.go @@ -0,0 +1,2855 @@ +// +// === This file is part of ALICE O² === +// +// Copyright 2020-2021 CERN and copyright holders of ALICE O². +// Author: Teo Mrnjavac +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// In applying this license CERN does not waive the privileges and +// immunities granted to it by virtue of its status as an +// Intergovernmental Organization or submit itself to any jurisdiction. + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.34.1 +// protoc v5.29.3 +// source: protos/apricot.proto + +package apricotpb + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type RunType int32 + +const ( + RunType_NULL RunType = 0 + RunType_PHYSICS RunType = 1 + RunType_TECHNICAL RunType = 2 + RunType_PEDESTAL RunType = 3 + RunType_PULSER RunType = 4 + RunType_LASER RunType = 5 + RunType_CALIBRATION_ITHR_TUNING RunType = 6 + RunType_CALIBRATION_VCASN_TUNING RunType = 7 + RunType_CALIBRATION_THR_SCAN RunType = 8 + RunType_CALIBRATION_DIGITAL_SCAN RunType = 9 + RunType_CALIBRATION_ANALOG_SCAN RunType = 10 + RunType_CALIBRATION_FHR RunType = 11 + RunType_CALIBRATION_ALPIDE_SCAN RunType = 12 + RunType_CALIBRATION RunType = 13 + RunType_COSMICS RunType = 14 + RunType_SYNTHETIC RunType = 15 + RunType_NOISE RunType = 16 + RunType_CALIBRATION_PULSE_LENGTH RunType = 17 + RunType_CALIBRATION_VRESETD RunType = 18 + RunType_ANY RunType = 300 +) + +// Enum value maps for RunType. +var ( + RunType_name = map[int32]string{ + 0: "NULL", + 1: "PHYSICS", + 2: "TECHNICAL", + 3: "PEDESTAL", + 4: "PULSER", + 5: "LASER", + 6: "CALIBRATION_ITHR_TUNING", + 7: "CALIBRATION_VCASN_TUNING", + 8: "CALIBRATION_THR_SCAN", + 9: "CALIBRATION_DIGITAL_SCAN", + 10: "CALIBRATION_ANALOG_SCAN", + 11: "CALIBRATION_FHR", + 12: "CALIBRATION_ALPIDE_SCAN", + 13: "CALIBRATION", + 14: "COSMICS", + 15: "SYNTHETIC", + 16: "NOISE", + 17: "CALIBRATION_PULSE_LENGTH", + 18: "CALIBRATION_VRESETD", + 300: "ANY", + } + RunType_value = map[string]int32{ + "NULL": 0, + "PHYSICS": 1, + "TECHNICAL": 2, + "PEDESTAL": 3, + "PULSER": 4, + "LASER": 5, + "CALIBRATION_ITHR_TUNING": 6, + "CALIBRATION_VCASN_TUNING": 7, + "CALIBRATION_THR_SCAN": 8, + "CALIBRATION_DIGITAL_SCAN": 9, + "CALIBRATION_ANALOG_SCAN": 10, + "CALIBRATION_FHR": 11, + "CALIBRATION_ALPIDE_SCAN": 12, + "CALIBRATION": 13, + "COSMICS": 14, + "SYNTHETIC": 15, + "NOISE": 16, + "CALIBRATION_PULSE_LENGTH": 17, + "CALIBRATION_VRESETD": 18, + "ANY": 300, + } +) + +func (x RunType) Enum() *RunType { + p := new(RunType) + *p = x + return p +} + +func (x RunType) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (RunType) Descriptor() protoreflect.EnumDescriptor { + return file_protos_apricot_proto_enumTypes[0].Descriptor() +} + +func (RunType) Type() protoreflect.EnumType { + return &file_protos_apricot_proto_enumTypes[0] +} + +func (x RunType) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use RunType.Descriptor instead. +func (RunType) EnumDescriptor() ([]byte, []int) { + return file_protos_apricot_proto_rawDescGZIP(), []int{0} +} + +type Empty struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *Empty) Reset() { + *x = Empty{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_apricot_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Empty) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Empty) ProtoMessage() {} + +func (x *Empty) ProtoReflect() protoreflect.Message { + mi := &file_protos_apricot_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Empty.ProtoReflect.Descriptor instead. +func (*Empty) Descriptor() ([]byte, []int) { + return file_protos_apricot_proto_rawDescGZIP(), []int{0} +} + +type ComponentQuery struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Component string `protobuf:"bytes,1,opt,name=component,proto3" json:"component,omitempty"` + RunType RunType `protobuf:"varint,2,opt,name=runType,proto3,enum=apricot.RunType" json:"runType,omitempty"` + MachineRole string `protobuf:"bytes,3,opt,name=machineRole,proto3" json:"machineRole,omitempty"` + Entry string `protobuf:"bytes,4,opt,name=entry,proto3" json:"entry,omitempty"` +} + +func (x *ComponentQuery) Reset() { + *x = ComponentQuery{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_apricot_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ComponentQuery) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ComponentQuery) ProtoMessage() {} + +func (x *ComponentQuery) ProtoReflect() protoreflect.Message { + mi := &file_protos_apricot_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ComponentQuery.ProtoReflect.Descriptor instead. +func (*ComponentQuery) Descriptor() ([]byte, []int) { + return file_protos_apricot_proto_rawDescGZIP(), []int{1} +} + +func (x *ComponentQuery) GetComponent() string { + if x != nil { + return x.Component + } + return "" +} + +func (x *ComponentQuery) GetRunType() RunType { + if x != nil { + return x.RunType + } + return RunType_NULL +} + +func (x *ComponentQuery) GetMachineRole() string { + if x != nil { + return x.MachineRole + } + return "" +} + +func (x *ComponentQuery) GetEntry() string { + if x != nil { + return x.Entry + } + return "" +} + +type ComponentRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Types that are assignable to QueryPath: + // + // *ComponentRequest_Path + // *ComponentRequest_Query + QueryPath isComponentRequest_QueryPath `protobuf_oneof:"queryPath"` + ProcessTemplate bool `protobuf:"varint,3,opt,name=processTemplate,proto3" json:"processTemplate,omitempty"` + VarStack map[string]string `protobuf:"bytes,4,rep,name=varStack,proto3" json:"varStack,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (x *ComponentRequest) Reset() { + *x = ComponentRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_apricot_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ComponentRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ComponentRequest) ProtoMessage() {} + +func (x *ComponentRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_apricot_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ComponentRequest.ProtoReflect.Descriptor instead. +func (*ComponentRequest) Descriptor() ([]byte, []int) { + return file_protos_apricot_proto_rawDescGZIP(), []int{2} +} + +func (m *ComponentRequest) GetQueryPath() isComponentRequest_QueryPath { + if m != nil { + return m.QueryPath + } + return nil +} + +func (x *ComponentRequest) GetPath() string { + if x, ok := x.GetQueryPath().(*ComponentRequest_Path); ok { + return x.Path + } + return "" +} + +func (x *ComponentRequest) GetQuery() *ComponentQuery { + if x, ok := x.GetQueryPath().(*ComponentRequest_Query); ok { + return x.Query + } + return nil +} + +func (x *ComponentRequest) GetProcessTemplate() bool { + if x != nil { + return x.ProcessTemplate + } + return false +} + +func (x *ComponentRequest) GetVarStack() map[string]string { + if x != nil { + return x.VarStack + } + return nil +} + +type isComponentRequest_QueryPath interface { + isComponentRequest_QueryPath() +} + +type ComponentRequest_Path struct { + Path string `protobuf:"bytes,1,opt,name=path,proto3,oneof"` +} + +type ComponentRequest_Query struct { + Query *ComponentQuery `protobuf:"bytes,2,opt,name=query,proto3,oneof"` +} + +func (*ComponentRequest_Path) isComponentRequest_QueryPath() {} + +func (*ComponentRequest_Query) isComponentRequest_QueryPath() {} + +type ComponentResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Payload string `protobuf:"bytes,1,opt,name=payload,proto3" json:"payload,omitempty"` +} + +func (x *ComponentResponse) Reset() { + *x = ComponentResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_apricot_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ComponentResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ComponentResponse) ProtoMessage() {} + +func (x *ComponentResponse) ProtoReflect() protoreflect.Message { + mi := &file_protos_apricot_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ComponentResponse.ProtoReflect.Descriptor instead. +func (*ComponentResponse) Descriptor() ([]byte, []int) { + return file_protos_apricot_proto_rawDescGZIP(), []int{3} +} + +func (x *ComponentResponse) GetPayload() string { + if x != nil { + return x.Payload + } + return "" +} + +type ComponentResponseWithLastIndex struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Payload string `protobuf:"bytes,1,opt,name=payload,proto3" json:"payload,omitempty"` + LastIndex uint64 `protobuf:"varint,2,opt,name=lastIndex,proto3" json:"lastIndex,omitempty"` +} + +func (x *ComponentResponseWithLastIndex) Reset() { + *x = ComponentResponseWithLastIndex{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_apricot_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ComponentResponseWithLastIndex) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ComponentResponseWithLastIndex) ProtoMessage() {} + +func (x *ComponentResponseWithLastIndex) ProtoReflect() protoreflect.Message { + mi := &file_protos_apricot_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ComponentResponseWithLastIndex.ProtoReflect.Descriptor instead. +func (*ComponentResponseWithLastIndex) Descriptor() ([]byte, []int) { + return file_protos_apricot_proto_rawDescGZIP(), []int{4} +} + +func (x *ComponentResponseWithLastIndex) GetPayload() string { + if x != nil { + return x.Payload + } + return "" +} + +func (x *ComponentResponseWithLastIndex) GetLastIndex() uint64 { + if x != nil { + return x.LastIndex + } + return 0 +} + +type HostRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Hostname string `protobuf:"bytes,1,opt,name=hostname,proto3" json:"hostname,omitempty"` +} + +func (x *HostRequest) Reset() { + *x = HostRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_apricot_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *HostRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*HostRequest) ProtoMessage() {} + +func (x *HostRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_apricot_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use HostRequest.ProtoReflect.Descriptor instead. +func (*HostRequest) Descriptor() ([]byte, []int) { + return file_protos_apricot_proto_rawDescGZIP(), []int{5} +} + +func (x *HostRequest) GetHostname() string { + if x != nil { + return x.Hostname + } + return "" +} + +type HostsRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Hosts []string `protobuf:"bytes,1,rep,name=hosts,proto3" json:"hosts,omitempty"` +} + +func (x *HostsRequest) Reset() { + *x = HostsRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_apricot_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *HostsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*HostsRequest) ProtoMessage() {} + +func (x *HostsRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_apricot_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use HostsRequest.ProtoReflect.Descriptor instead. +func (*HostsRequest) Descriptor() ([]byte, []int) { + return file_protos_apricot_proto_rawDescGZIP(), []int{6} +} + +func (x *HostsRequest) GetHosts() []string { + if x != nil { + return x.Hosts + } + return nil +} + +type DetectorResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Payload string `protobuf:"bytes,1,opt,name=payload,proto3" json:"payload,omitempty"` +} + +func (x *DetectorResponse) Reset() { + *x = DetectorResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_apricot_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DetectorResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DetectorResponse) ProtoMessage() {} + +func (x *DetectorResponse) ProtoReflect() protoreflect.Message { + mi := &file_protos_apricot_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DetectorResponse.ProtoReflect.Descriptor instead. +func (*DetectorResponse) Descriptor() ([]byte, []int) { + return file_protos_apricot_proto_rawDescGZIP(), []int{7} +} + +func (x *DetectorResponse) GetPayload() string { + if x != nil { + return x.Payload + } + return "" +} + +type DetectorInventoryResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Flps []string `protobuf:"bytes,1,rep,name=flps,proto3" json:"flps,omitempty"` +} + +func (x *DetectorInventoryResponse) Reset() { + *x = DetectorInventoryResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_apricot_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DetectorInventoryResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DetectorInventoryResponse) ProtoMessage() {} + +func (x *DetectorInventoryResponse) ProtoReflect() protoreflect.Message { + mi := &file_protos_apricot_proto_msgTypes[8] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DetectorInventoryResponse.ProtoReflect.Descriptor instead. +func (*DetectorInventoryResponse) Descriptor() ([]byte, []int) { + return file_protos_apricot_proto_rawDescGZIP(), []int{8} +} + +func (x *DetectorInventoryResponse) GetFlps() []string { + if x != nil { + return x.Flps + } + return nil +} + +type DetectorEntriesResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + DetectorEntries map[string]*DetectorInventoryResponse `protobuf:"bytes,1,rep,name=detectorEntries,proto3" json:"detectorEntries,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (x *DetectorEntriesResponse) Reset() { + *x = DetectorEntriesResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_apricot_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DetectorEntriesResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DetectorEntriesResponse) ProtoMessage() {} + +func (x *DetectorEntriesResponse) ProtoReflect() protoreflect.Message { + mi := &file_protos_apricot_proto_msgTypes[9] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DetectorEntriesResponse.ProtoReflect.Descriptor instead. +func (*DetectorEntriesResponse) Descriptor() ([]byte, []int) { + return file_protos_apricot_proto_rawDescGZIP(), []int{9} +} + +func (x *DetectorEntriesResponse) GetDetectorEntries() map[string]*DetectorInventoryResponse { + if x != nil { + return x.DetectorEntries + } + return nil +} + +type RunNumberResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + RunNumber uint32 `protobuf:"varint,1,opt,name=runNumber,proto3" json:"runNumber,omitempty"` +} + +func (x *RunNumberResponse) Reset() { + *x = RunNumberResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_apricot_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RunNumberResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RunNumberResponse) ProtoMessage() {} + +func (x *RunNumberResponse) ProtoReflect() protoreflect.Message { + mi := &file_protos_apricot_proto_msgTypes[10] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RunNumberResponse.ProtoReflect.Descriptor instead. +func (*RunNumberResponse) Descriptor() ([]byte, []int) { + return file_protos_apricot_proto_rawDescGZIP(), []int{10} +} + +func (x *RunNumberResponse) GetRunNumber() uint32 { + if x != nil { + return x.RunNumber + } + return 0 +} + +type StringMap struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + StringMap map[string]string `protobuf:"bytes,1,rep,name=stringMap,proto3" json:"stringMap,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (x *StringMap) Reset() { + *x = StringMap{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_apricot_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StringMap) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StringMap) ProtoMessage() {} + +func (x *StringMap) ProtoReflect() protoreflect.Message { + mi := &file_protos_apricot_proto_msgTypes[11] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StringMap.ProtoReflect.Descriptor instead. +func (*StringMap) Descriptor() ([]byte, []int) { + return file_protos_apricot_proto_rawDescGZIP(), []int{11} +} + +func (x *StringMap) GetStringMap() map[string]string { + if x != nil { + return x.StringMap + } + return nil +} + +type RawGetRecursiveRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + RawPath string `protobuf:"bytes,1,opt,name=rawPath,proto3" json:"rawPath,omitempty"` +} + +func (x *RawGetRecursiveRequest) Reset() { + *x = RawGetRecursiveRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_apricot_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RawGetRecursiveRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RawGetRecursiveRequest) ProtoMessage() {} + +func (x *RawGetRecursiveRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_apricot_proto_msgTypes[12] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RawGetRecursiveRequest.ProtoReflect.Descriptor instead. +func (*RawGetRecursiveRequest) Descriptor() ([]byte, []int) { + return file_protos_apricot_proto_rawDescGZIP(), []int{12} +} + +func (x *RawGetRecursiveRequest) GetRawPath() string { + if x != nil { + return x.RawPath + } + return "" +} + +type GetRuntimeEntryRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Component string `protobuf:"bytes,1,opt,name=component,proto3" json:"component,omitempty"` + Key string `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"` +} + +func (x *GetRuntimeEntryRequest) Reset() { + *x = GetRuntimeEntryRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_apricot_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetRuntimeEntryRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetRuntimeEntryRequest) ProtoMessage() {} + +func (x *GetRuntimeEntryRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_apricot_proto_msgTypes[13] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetRuntimeEntryRequest.ProtoReflect.Descriptor instead. +func (*GetRuntimeEntryRequest) Descriptor() ([]byte, []int) { + return file_protos_apricot_proto_rawDescGZIP(), []int{13} +} + +func (x *GetRuntimeEntryRequest) GetComponent() string { + if x != nil { + return x.Component + } + return "" +} + +func (x *GetRuntimeEntryRequest) GetKey() string { + if x != nil { + return x.Key + } + return "" +} + +type SetRuntimeEntryRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Component string `protobuf:"bytes,1,opt,name=component,proto3" json:"component,omitempty"` + Key string `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"` + Value string `protobuf:"bytes,3,opt,name=value,proto3" json:"value,omitempty"` +} + +func (x *SetRuntimeEntryRequest) Reset() { + *x = SetRuntimeEntryRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_apricot_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SetRuntimeEntryRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SetRuntimeEntryRequest) ProtoMessage() {} + +func (x *SetRuntimeEntryRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_apricot_proto_msgTypes[14] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SetRuntimeEntryRequest.ProtoReflect.Descriptor instead. +func (*SetRuntimeEntryRequest) Descriptor() ([]byte, []int) { + return file_protos_apricot_proto_rawDescGZIP(), []int{14} +} + +func (x *SetRuntimeEntryRequest) GetComponent() string { + if x != nil { + return x.Component + } + return "" +} + +func (x *SetRuntimeEntryRequest) GetKey() string { + if x != nil { + return x.Key + } + return "" +} + +func (x *SetRuntimeEntryRequest) GetValue() string { + if x != nil { + return x.Value + } + return "" +} + +type GetEntryRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` +} + +func (x *GetEntryRequest) Reset() { + *x = GetEntryRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_apricot_proto_msgTypes[15] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetEntryRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetEntryRequest) ProtoMessage() {} + +func (x *GetEntryRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_apricot_proto_msgTypes[15] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetEntryRequest.ProtoReflect.Descriptor instead. +func (*GetEntryRequest) Descriptor() ([]byte, []int) { + return file_protos_apricot_proto_rawDescGZIP(), []int{15} +} + +func (x *GetEntryRequest) GetKey() string { + if x != nil { + return x.Key + } + return "" +} + +type GetRuntimeEntriesRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Component string `protobuf:"bytes,1,opt,name=component,proto3" json:"component,omitempty"` +} + +func (x *GetRuntimeEntriesRequest) Reset() { + *x = GetRuntimeEntriesRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_apricot_proto_msgTypes[16] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetRuntimeEntriesRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetRuntimeEntriesRequest) ProtoMessage() {} + +func (x *GetRuntimeEntriesRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_apricot_proto_msgTypes[16] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetRuntimeEntriesRequest.ProtoReflect.Descriptor instead. +func (*GetRuntimeEntriesRequest) Descriptor() ([]byte, []int) { + return file_protos_apricot_proto_rawDescGZIP(), []int{16} +} + +func (x *GetRuntimeEntriesRequest) GetComponent() string { + if x != nil { + return x.Component + } + return "" +} + +type ListRuntimeEntriesRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Component string `protobuf:"bytes,1,opt,name=component,proto3" json:"component,omitempty"` +} + +func (x *ListRuntimeEntriesRequest) Reset() { + *x = ListRuntimeEntriesRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_apricot_proto_msgTypes[17] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ListRuntimeEntriesRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListRuntimeEntriesRequest) ProtoMessage() {} + +func (x *ListRuntimeEntriesRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_apricot_proto_msgTypes[17] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListRuntimeEntriesRequest.ProtoReflect.Descriptor instead. +func (*ListRuntimeEntriesRequest) Descriptor() ([]byte, []int) { + return file_protos_apricot_proto_rawDescGZIP(), []int{17} +} + +func (x *ListRuntimeEntriesRequest) GetComponent() string { + if x != nil { + return x.Component + } + return "" +} + +type ComponentEntriesQuery struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Component string `protobuf:"bytes,1,opt,name=component,proto3" json:"component,omitempty"` + RunType RunType `protobuf:"varint,2,opt,name=runType,proto3,enum=apricot.RunType" json:"runType,omitempty"` + MachineRole string `protobuf:"bytes,3,opt,name=machineRole,proto3" json:"machineRole,omitempty"` +} + +func (x *ComponentEntriesQuery) Reset() { + *x = ComponentEntriesQuery{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_apricot_proto_msgTypes[18] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ComponentEntriesQuery) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ComponentEntriesQuery) ProtoMessage() {} + +func (x *ComponentEntriesQuery) ProtoReflect() protoreflect.Message { + mi := &file_protos_apricot_proto_msgTypes[18] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ComponentEntriesQuery.ProtoReflect.Descriptor instead. +func (*ComponentEntriesQuery) Descriptor() ([]byte, []int) { + return file_protos_apricot_proto_rawDescGZIP(), []int{18} +} + +func (x *ComponentEntriesQuery) GetComponent() string { + if x != nil { + return x.Component + } + return "" +} + +func (x *ComponentEntriesQuery) GetRunType() RunType { + if x != nil { + return x.RunType + } + return RunType_NULL +} + +func (x *ComponentEntriesQuery) GetMachineRole() string { + if x != nil { + return x.MachineRole + } + return "" +} + +type ListComponentEntriesRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Types that are assignable to QueryPath: + // + // *ListComponentEntriesRequest_Path + // *ListComponentEntriesRequest_Query + QueryPath isListComponentEntriesRequest_QueryPath `protobuf_oneof:"queryPath"` +} + +func (x *ListComponentEntriesRequest) Reset() { + *x = ListComponentEntriesRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_apricot_proto_msgTypes[19] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ListComponentEntriesRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListComponentEntriesRequest) ProtoMessage() {} + +func (x *ListComponentEntriesRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_apricot_proto_msgTypes[19] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListComponentEntriesRequest.ProtoReflect.Descriptor instead. +func (*ListComponentEntriesRequest) Descriptor() ([]byte, []int) { + return file_protos_apricot_proto_rawDescGZIP(), []int{19} +} + +func (m *ListComponentEntriesRequest) GetQueryPath() isListComponentEntriesRequest_QueryPath { + if m != nil { + return m.QueryPath + } + return nil +} + +func (x *ListComponentEntriesRequest) GetPath() string { + if x, ok := x.GetQueryPath().(*ListComponentEntriesRequest_Path); ok { + return x.Path + } + return "" +} + +func (x *ListComponentEntriesRequest) GetQuery() *ComponentEntriesQuery { + if x, ok := x.GetQueryPath().(*ListComponentEntriesRequest_Query); ok { + return x.Query + } + return nil +} + +type isListComponentEntriesRequest_QueryPath interface { + isListComponentEntriesRequest_QueryPath() +} + +type ListComponentEntriesRequest_Path struct { + Path string `protobuf:"bytes,1,opt,name=path,proto3,oneof"` +} + +type ListComponentEntriesRequest_Query struct { + Query *ComponentEntriesQuery `protobuf:"bytes,2,opt,name=query,proto3,oneof"` +} + +func (*ListComponentEntriesRequest_Path) isListComponentEntriesRequest_QueryPath() {} + +func (*ListComponentEntriesRequest_Query) isListComponentEntriesRequest_QueryPath() {} + +type ComponentEntriesResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Payload []string `protobuf:"bytes,1,rep,name=payload,proto3" json:"payload,omitempty"` +} + +func (x *ComponentEntriesResponse) Reset() { + *x = ComponentEntriesResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_apricot_proto_msgTypes[20] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ComponentEntriesResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ComponentEntriesResponse) ProtoMessage() {} + +func (x *ComponentEntriesResponse) ProtoReflect() protoreflect.Message { + mi := &file_protos_apricot_proto_msgTypes[20] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ComponentEntriesResponse.ProtoReflect.Descriptor instead. +func (*ComponentEntriesResponse) Descriptor() ([]byte, []int) { + return file_protos_apricot_proto_rawDescGZIP(), []int{20} +} + +func (x *ComponentEntriesResponse) GetPayload() []string { + if x != nil { + return x.Payload + } + return nil +} + +type DetectorsRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + GetAll bool `protobuf:"varint,1,opt,name=getAll,proto3" json:"getAll,omitempty"` // if false(default) restricts "private" detectors (e.g. TRG) +} + +func (x *DetectorsRequest) Reset() { + *x = DetectorsRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_apricot_proto_msgTypes[21] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DetectorsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DetectorsRequest) ProtoMessage() {} + +func (x *DetectorsRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_apricot_proto_msgTypes[21] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DetectorsRequest.ProtoReflect.Descriptor instead. +func (*DetectorsRequest) Descriptor() ([]byte, []int) { + return file_protos_apricot_proto_rawDescGZIP(), []int{21} +} + +func (x *DetectorsRequest) GetGetAll() bool { + if x != nil { + return x.GetAll + } + return false +} + +type DetectorsResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Detectors []string `protobuf:"bytes,1,rep,name=detectors,proto3" json:"detectors,omitempty"` +} + +func (x *DetectorsResponse) Reset() { + *x = DetectorsResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_apricot_proto_msgTypes[22] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DetectorsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DetectorsResponse) ProtoMessage() {} + +func (x *DetectorsResponse) ProtoReflect() protoreflect.Message { + mi := &file_protos_apricot_proto_msgTypes[22] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DetectorsResponse.ProtoReflect.Descriptor instead. +func (*DetectorsResponse) Descriptor() ([]byte, []int) { + return file_protos_apricot_proto_rawDescGZIP(), []int{22} +} + +func (x *DetectorsResponse) GetDetectors() []string { + if x != nil { + return x.Detectors + } + return nil +} + +type HostGetRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Detector string `protobuf:"bytes,1,opt,name=detector,proto3" json:"detector,omitempty"` +} + +func (x *HostGetRequest) Reset() { + *x = HostGetRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_apricot_proto_msgTypes[23] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *HostGetRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*HostGetRequest) ProtoMessage() {} + +func (x *HostGetRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_apricot_proto_msgTypes[23] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use HostGetRequest.ProtoReflect.Descriptor instead. +func (*HostGetRequest) Descriptor() ([]byte, []int) { + return file_protos_apricot_proto_rawDescGZIP(), []int{23} +} + +func (x *HostGetRequest) GetDetector() string { + if x != nil { + return x.Detector + } + return "" +} + +type HostEntriesResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Hosts []string `protobuf:"bytes,1,rep,name=hosts,proto3" json:"hosts,omitempty"` +} + +func (x *HostEntriesResponse) Reset() { + *x = HostEntriesResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_apricot_proto_msgTypes[24] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *HostEntriesResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*HostEntriesResponse) ProtoMessage() {} + +func (x *HostEntriesResponse) ProtoReflect() protoreflect.Message { + mi := &file_protos_apricot_proto_msgTypes[24] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use HostEntriesResponse.ProtoReflect.Descriptor instead. +func (*HostEntriesResponse) Descriptor() ([]byte, []int) { + return file_protos_apricot_proto_rawDescGZIP(), []int{24} +} + +func (x *HostEntriesResponse) GetHosts() []string { + if x != nil { + return x.Hosts + } + return nil +} + +type ImportComponentConfigurationRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Query *ComponentQuery `protobuf:"bytes,1,opt,name=query,proto3" json:"query,omitempty"` + Payload string `protobuf:"bytes,2,opt,name=payload,proto3" json:"payload,omitempty"` + NewComponent bool `protobuf:"varint,3,opt,name=newComponent,proto3" json:"newComponent,omitempty"` +} + +func (x *ImportComponentConfigurationRequest) Reset() { + *x = ImportComponentConfigurationRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_apricot_proto_msgTypes[25] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ImportComponentConfigurationRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ImportComponentConfigurationRequest) ProtoMessage() {} + +func (x *ImportComponentConfigurationRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_apricot_proto_msgTypes[25] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ImportComponentConfigurationRequest.ProtoReflect.Descriptor instead. +func (*ImportComponentConfigurationRequest) Descriptor() ([]byte, []int) { + return file_protos_apricot_proto_rawDescGZIP(), []int{25} +} + +func (x *ImportComponentConfigurationRequest) GetQuery() *ComponentQuery { + if x != nil { + return x.Query + } + return nil +} + +func (x *ImportComponentConfigurationRequest) GetPayload() string { + if x != nil { + return x.Payload + } + return "" +} + +func (x *ImportComponentConfigurationRequest) GetNewComponent() bool { + if x != nil { + return x.NewComponent + } + return false +} + +type ImportComponentConfigurationResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ExistingComponentUpdated bool `protobuf:"varint,1,opt,name=existingComponentUpdated,proto3" json:"existingComponentUpdated,omitempty"` + ExistingEntryUpdated bool `protobuf:"varint,2,opt,name=existingEntryUpdated,proto3" json:"existingEntryUpdated,omitempty"` +} + +func (x *ImportComponentConfigurationResponse) Reset() { + *x = ImportComponentConfigurationResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_apricot_proto_msgTypes[26] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ImportComponentConfigurationResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ImportComponentConfigurationResponse) ProtoMessage() {} + +func (x *ImportComponentConfigurationResponse) ProtoReflect() protoreflect.Message { + mi := &file_protos_apricot_proto_msgTypes[26] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ImportComponentConfigurationResponse.ProtoReflect.Descriptor instead. +func (*ImportComponentConfigurationResponse) Descriptor() ([]byte, []int) { + return file_protos_apricot_proto_rawDescGZIP(), []int{26} +} + +func (x *ImportComponentConfigurationResponse) GetExistingComponentUpdated() bool { + if x != nil { + return x.ExistingComponentUpdated + } + return false +} + +func (x *ImportComponentConfigurationResponse) GetExistingEntryUpdated() bool { + if x != nil { + return x.ExistingEntryUpdated + } + return false +} + +type CRUCardsResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Cards string `protobuf:"bytes,1,opt,name=cards,proto3" json:"cards,omitempty"` +} + +func (x *CRUCardsResponse) Reset() { + *x = CRUCardsResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_apricot_proto_msgTypes[27] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CRUCardsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CRUCardsResponse) ProtoMessage() {} + +func (x *CRUCardsResponse) ProtoReflect() protoreflect.Message { + mi := &file_protos_apricot_proto_msgTypes[27] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CRUCardsResponse.ProtoReflect.Descriptor instead. +func (*CRUCardsResponse) Descriptor() ([]byte, []int) { + return file_protos_apricot_proto_rawDescGZIP(), []int{27} +} + +func (x *CRUCardsResponse) GetCards() string { + if x != nil { + return x.Cards + } + return "" +} + +type CardRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Hostname string `protobuf:"bytes,1,opt,name=hostname,proto3" json:"hostname,omitempty"` + CardSerial string `protobuf:"bytes,2,opt,name=cardSerial,proto3" json:"cardSerial,omitempty"` +} + +func (x *CardRequest) Reset() { + *x = CardRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_apricot_proto_msgTypes[28] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CardRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CardRequest) ProtoMessage() {} + +func (x *CardRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_apricot_proto_msgTypes[28] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CardRequest.ProtoReflect.Descriptor instead. +func (*CardRequest) Descriptor() ([]byte, []int) { + return file_protos_apricot_proto_rawDescGZIP(), []int{28} +} + +func (x *CardRequest) GetHostname() string { + if x != nil { + return x.Hostname + } + return "" +} + +func (x *CardRequest) GetCardSerial() string { + if x != nil { + return x.CardSerial + } + return "" +} + +type CRUCardEndpointResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Endpoints string `protobuf:"bytes,1,opt,name=endpoints,proto3" json:"endpoints,omitempty"` +} + +func (x *CRUCardEndpointResponse) Reset() { + *x = CRUCardEndpointResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_apricot_proto_msgTypes[29] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CRUCardEndpointResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CRUCardEndpointResponse) ProtoMessage() {} + +func (x *CRUCardEndpointResponse) ProtoReflect() protoreflect.Message { + mi := &file_protos_apricot_proto_msgTypes[29] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CRUCardEndpointResponse.ProtoReflect.Descriptor instead. +func (*CRUCardEndpointResponse) Descriptor() ([]byte, []int) { + return file_protos_apricot_proto_rawDescGZIP(), []int{29} +} + +func (x *CRUCardEndpointResponse) GetEndpoints() string { + if x != nil { + return x.Endpoints + } + return "" +} + +type LinkIDsRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Hostname string `protobuf:"bytes,1,opt,name=hostname,proto3" json:"hostname,omitempty"` + CardSerial string `protobuf:"bytes,2,opt,name=cardSerial,proto3" json:"cardSerial,omitempty"` + Endpoint string `protobuf:"bytes,3,opt,name=endpoint,proto3" json:"endpoint,omitempty"` + OnlyEnabled bool `protobuf:"varint,4,opt,name=onlyEnabled,proto3" json:"onlyEnabled,omitempty"` +} + +func (x *LinkIDsRequest) Reset() { + *x = LinkIDsRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_apricot_proto_msgTypes[30] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *LinkIDsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*LinkIDsRequest) ProtoMessage() {} + +func (x *LinkIDsRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_apricot_proto_msgTypes[30] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use LinkIDsRequest.ProtoReflect.Descriptor instead. +func (*LinkIDsRequest) Descriptor() ([]byte, []int) { + return file_protos_apricot_proto_rawDescGZIP(), []int{30} +} + +func (x *LinkIDsRequest) GetHostname() string { + if x != nil { + return x.Hostname + } + return "" +} + +func (x *LinkIDsRequest) GetCardSerial() string { + if x != nil { + return x.CardSerial + } + return "" +} + +func (x *LinkIDsRequest) GetEndpoint() string { + if x != nil { + return x.Endpoint + } + return "" +} + +func (x *LinkIDsRequest) GetOnlyEnabled() bool { + if x != nil { + return x.OnlyEnabled + } + return false +} + +type LinkIDsResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + LinkIDs []string `protobuf:"bytes,1,rep,name=linkIDs,proto3" json:"linkIDs,omitempty"` +} + +func (x *LinkIDsResponse) Reset() { + *x = LinkIDsResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_apricot_proto_msgTypes[31] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *LinkIDsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*LinkIDsResponse) ProtoMessage() {} + +func (x *LinkIDsResponse) ProtoReflect() protoreflect.Message { + mi := &file_protos_apricot_proto_msgTypes[31] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use LinkIDsResponse.ProtoReflect.Descriptor instead. +func (*LinkIDsResponse) Descriptor() ([]byte, []int) { + return file_protos_apricot_proto_rawDescGZIP(), []int{31} +} + +func (x *LinkIDsResponse) GetLinkIDs() []string { + if x != nil { + return x.LinkIDs + } + return nil +} + +type AliasedLinkIDsRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Detector string `protobuf:"bytes,1,opt,name=detector,proto3" json:"detector,omitempty"` + OnlyEnabled bool `protobuf:"varint,2,opt,name=onlyEnabled,proto3" json:"onlyEnabled,omitempty"` +} + +func (x *AliasedLinkIDsRequest) Reset() { + *x = AliasedLinkIDsRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_apricot_proto_msgTypes[32] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AliasedLinkIDsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AliasedLinkIDsRequest) ProtoMessage() {} + +func (x *AliasedLinkIDsRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_apricot_proto_msgTypes[32] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AliasedLinkIDsRequest.ProtoReflect.Descriptor instead. +func (*AliasedLinkIDsRequest) Descriptor() ([]byte, []int) { + return file_protos_apricot_proto_rawDescGZIP(), []int{32} +} + +func (x *AliasedLinkIDsRequest) GetDetector() string { + if x != nil { + return x.Detector + } + return "" +} + +func (x *AliasedLinkIDsRequest) GetOnlyEnabled() bool { + if x != nil { + return x.OnlyEnabled + } + return false +} + +type AliasedLinkIDsResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + AliasedLinkIDs []string `protobuf:"bytes,1,rep,name=aliasedLinkIDs,proto3" json:"aliasedLinkIDs,omitempty"` +} + +func (x *AliasedLinkIDsResponse) Reset() { + *x = AliasedLinkIDsResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_apricot_proto_msgTypes[33] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AliasedLinkIDsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AliasedLinkIDsResponse) ProtoMessage() {} + +func (x *AliasedLinkIDsResponse) ProtoReflect() protoreflect.Message { + mi := &file_protos_apricot_proto_msgTypes[33] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AliasedLinkIDsResponse.ProtoReflect.Descriptor instead. +func (*AliasedLinkIDsResponse) Descriptor() ([]byte, []int) { + return file_protos_apricot_proto_rawDescGZIP(), []int{33} +} + +func (x *AliasedLinkIDsResponse) GetAliasedLinkIDs() []string { + if x != nil { + return x.AliasedLinkIDs + } + return nil +} + +var File_protos_apricot_proto protoreflect.FileDescriptor + +var file_protos_apricot_proto_rawDesc = []byte{ + 0x0a, 0x14, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x61, 0x70, 0x72, 0x69, 0x63, 0x6f, 0x74, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x07, 0x61, 0x70, 0x72, 0x69, 0x63, 0x6f, 0x74, 0x22, + 0x07, 0x0a, 0x05, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x92, 0x01, 0x0a, 0x0e, 0x43, 0x6f, 0x6d, + 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x1c, 0x0a, 0x09, 0x63, + 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, + 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x12, 0x2a, 0x0a, 0x07, 0x72, 0x75, 0x6e, + 0x54, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x10, 0x2e, 0x61, 0x70, 0x72, + 0x69, 0x63, 0x6f, 0x74, 0x2e, 0x52, 0x75, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x07, 0x72, 0x75, + 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, + 0x52, 0x6f, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6d, 0x61, 0x63, 0x68, + 0x69, 0x6e, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x6e, 0x74, 0x72, 0x79, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x22, 0x92, 0x02, + 0x0a, 0x10, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x14, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x48, 0x00, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x2f, 0x0a, 0x05, 0x71, 0x75, 0x65, 0x72, + 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x61, 0x70, 0x72, 0x69, 0x63, 0x6f, + 0x74, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x51, 0x75, 0x65, 0x72, 0x79, + 0x48, 0x00, 0x52, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x12, 0x28, 0x0a, 0x0f, 0x70, 0x72, 0x6f, + 0x63, 0x65, 0x73, 0x73, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x0f, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x54, 0x65, 0x6d, 0x70, 0x6c, + 0x61, 0x74, 0x65, 0x12, 0x43, 0x0a, 0x08, 0x76, 0x61, 0x72, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x18, + 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x61, 0x70, 0x72, 0x69, 0x63, 0x6f, 0x74, 0x2e, + 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x2e, 0x56, 0x61, 0x72, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, + 0x76, 0x61, 0x72, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x1a, 0x3b, 0x0a, 0x0d, 0x56, 0x61, 0x72, 0x53, + 0x74, 0x61, 0x63, 0x6b, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x0b, 0x0a, 0x09, 0x71, 0x75, 0x65, 0x72, 0x79, 0x50, 0x61, + 0x74, 0x68, 0x22, 0x2d, 0x0a, 0x11, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, + 0x61, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, + 0x64, 0x22, 0x58, 0x0a, 0x1e, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x57, 0x69, 0x74, 0x68, 0x4c, 0x61, 0x73, 0x74, 0x49, 0x6e, + 0x64, 0x65, 0x78, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x1c, 0x0a, + 0x09, 0x6c, 0x61, 0x73, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x09, 0x6c, 0x61, 0x73, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x22, 0x29, 0x0a, 0x0b, 0x48, + 0x6f, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x68, 0x6f, + 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x68, 0x6f, + 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x24, 0x0a, 0x0c, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x18, + 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x22, 0x2c, 0x0a, 0x10, + 0x44, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x22, 0x2f, 0x0a, 0x19, 0x44, 0x65, + 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x76, 0x65, 0x6e, 0x74, 0x6f, 0x72, 0x79, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x6c, 0x70, 0x73, 0x18, + 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x66, 0x6c, 0x70, 0x73, 0x22, 0xe2, 0x01, 0x0a, 0x17, + 0x44, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5f, 0x0a, 0x0f, 0x64, 0x65, 0x74, 0x65, 0x63, + 0x74, 0x6f, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x35, 0x2e, 0x61, 0x70, 0x72, 0x69, 0x63, 0x6f, 0x74, 0x2e, 0x44, 0x65, 0x74, 0x65, 0x63, + 0x74, 0x6f, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x2e, 0x44, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x69, + 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0f, 0x64, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, + 0x72, 0x45, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x1a, 0x66, 0x0a, 0x14, 0x44, 0x65, 0x74, 0x65, + 0x63, 0x74, 0x6f, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, + 0x65, 0x79, 0x12, 0x38, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x22, 0x2e, 0x61, 0x70, 0x72, 0x69, 0x63, 0x6f, 0x74, 0x2e, 0x44, 0x65, 0x74, 0x65, + 0x63, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x76, 0x65, 0x6e, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, + 0x22, 0x31, 0x0a, 0x11, 0x52, 0x75, 0x6e, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x72, 0x75, 0x6e, 0x4e, 0x75, 0x6d, 0x62, + 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x72, 0x75, 0x6e, 0x4e, 0x75, 0x6d, + 0x62, 0x65, 0x72, 0x22, 0x8a, 0x01, 0x0a, 0x09, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x4d, 0x61, + 0x70, 0x12, 0x3f, 0x0a, 0x09, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x4d, 0x61, 0x70, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x61, 0x70, 0x72, 0x69, 0x63, 0x6f, 0x74, 0x2e, 0x53, + 0x74, 0x72, 0x69, 0x6e, 0x67, 0x4d, 0x61, 0x70, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x4d, + 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x09, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x4d, + 0x61, 0x70, 0x1a, 0x3c, 0x0a, 0x0e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x4d, 0x61, 0x70, 0x45, + 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, + 0x22, 0x32, 0x0a, 0x16, 0x52, 0x61, 0x77, 0x47, 0x65, 0x74, 0x52, 0x65, 0x63, 0x75, 0x72, 0x73, + 0x69, 0x76, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x61, + 0x77, 0x50, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x72, 0x61, 0x77, + 0x50, 0x61, 0x74, 0x68, 0x22, 0x48, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x52, 0x75, 0x6e, 0x74, 0x69, + 0x6d, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, + 0x0a, 0x09, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x09, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x12, 0x10, 0x0a, 0x03, + 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x22, 0x5e, + 0x0a, 0x16, 0x53, 0x65, 0x74, 0x52, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6f, 0x6d, 0x70, + 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6f, 0x6d, + 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x23, + 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x6b, 0x65, 0x79, 0x22, 0x38, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x52, 0x75, 0x6e, 0x74, 0x69, 0x6d, + 0x65, 0x45, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x1c, 0x0a, 0x09, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x09, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x22, 0x39, 0x0a, + 0x19, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x45, 0x6e, 0x74, 0x72, + 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6f, + 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, + 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x22, 0x83, 0x01, 0x0a, 0x15, 0x43, 0x6f, 0x6d, + 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x51, 0x75, 0x65, + 0x72, 0x79, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, + 0x12, 0x2a, 0x0a, 0x07, 0x72, 0x75, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0e, 0x32, 0x10, 0x2e, 0x61, 0x70, 0x72, 0x69, 0x63, 0x6f, 0x74, 0x2e, 0x52, 0x75, 0x6e, 0x54, + 0x79, 0x70, 0x65, 0x52, 0x07, 0x72, 0x75, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x20, 0x0a, 0x0b, + 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0b, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x6f, 0x6c, 0x65, 0x22, 0x78, + 0x0a, 0x1b, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x45, + 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, + 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x04, 0x70, + 0x61, 0x74, 0x68, 0x12, 0x36, 0x0a, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x61, 0x70, 0x72, 0x69, 0x63, 0x6f, 0x74, 0x2e, 0x43, 0x6f, 0x6d, + 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x51, 0x75, 0x65, + 0x72, 0x79, 0x48, 0x00, 0x52, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x42, 0x0b, 0x0a, 0x09, 0x71, + 0x75, 0x65, 0x72, 0x79, 0x50, 0x61, 0x74, 0x68, 0x22, 0x34, 0x0a, 0x18, 0x43, 0x6f, 0x6d, 0x70, + 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, + 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x22, 0x2a, + 0x0a, 0x10, 0x44, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x67, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x06, 0x67, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x22, 0x31, 0x0a, 0x11, 0x44, 0x65, + 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x1c, 0x0a, 0x09, 0x64, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, + 0x28, 0x09, 0x52, 0x09, 0x64, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x22, 0x2c, 0x0a, + 0x0e, 0x48, 0x6f, 0x73, 0x74, 0x47, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x1a, 0x0a, 0x08, 0x64, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x08, 0x64, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x22, 0x2b, 0x0a, 0x13, 0x48, + 0x6f, 0x73, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, + 0x09, 0x52, 0x05, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x22, 0x92, 0x01, 0x0a, 0x23, 0x49, 0x6d, 0x70, + 0x6f, 0x72, 0x74, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x2d, 0x0a, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x17, 0x2e, 0x61, 0x70, 0x72, 0x69, 0x63, 0x6f, 0x74, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, + 0x65, 0x6e, 0x74, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x12, + 0x18, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x22, 0x0a, 0x0c, 0x6e, 0x65, 0x77, + 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x0c, 0x6e, 0x65, 0x77, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x22, 0x96, 0x01, + 0x0a, 0x24, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, + 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3a, 0x0a, 0x18, 0x65, 0x78, 0x69, 0x73, 0x74, 0x69, + 0x6e, 0x67, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x18, 0x65, 0x78, 0x69, 0x73, 0x74, 0x69, + 0x6e, 0x67, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x64, 0x12, 0x32, 0x0a, 0x14, 0x65, 0x78, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x45, 0x6e, + 0x74, 0x72, 0x79, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x14, 0x65, 0x78, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x55, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x22, 0x28, 0x0a, 0x10, 0x43, 0x52, 0x55, 0x43, 0x61, 0x72, + 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x61, + 0x72, 0x64, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x63, 0x61, 0x72, 0x64, 0x73, + 0x22, 0x49, 0x0a, 0x0b, 0x43, 0x61, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x1a, 0x0a, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x63, + 0x61, 0x72, 0x64, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0a, 0x63, 0x61, 0x72, 0x64, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x22, 0x37, 0x0a, 0x17, 0x43, + 0x52, 0x55, 0x43, 0x61, 0x72, 0x64, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, + 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x65, 0x6e, 0x64, 0x70, 0x6f, + 0x69, 0x6e, 0x74, 0x73, 0x22, 0x8a, 0x01, 0x0a, 0x0e, 0x4c, 0x69, 0x6e, 0x6b, 0x49, 0x44, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, + 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x61, 0x72, 0x64, 0x53, 0x65, 0x72, 0x69, 0x61, + 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x61, 0x72, 0x64, 0x53, 0x65, 0x72, + 0x69, 0x61, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, + 0x20, 0x0a, 0x0b, 0x6f, 0x6e, 0x6c, 0x79, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x6f, 0x6e, 0x6c, 0x79, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, + 0x64, 0x22, 0x2b, 0x0a, 0x0f, 0x4c, 0x69, 0x6e, 0x6b, 0x49, 0x44, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6c, 0x69, 0x6e, 0x6b, 0x49, 0x44, 0x73, 0x18, + 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x6c, 0x69, 0x6e, 0x6b, 0x49, 0x44, 0x73, 0x22, 0x55, + 0x0a, 0x15, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x64, 0x4c, 0x69, 0x6e, 0x6b, 0x49, 0x44, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x64, 0x65, 0x74, 0x65, 0x63, + 0x74, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x65, 0x74, 0x65, 0x63, + 0x74, 0x6f, 0x72, 0x12, 0x20, 0x0a, 0x0b, 0x6f, 0x6e, 0x6c, 0x79, 0x45, 0x6e, 0x61, 0x62, 0x6c, + 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x6f, 0x6e, 0x6c, 0x79, 0x45, 0x6e, + 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0x40, 0x0a, 0x16, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x64, + 0x4c, 0x69, 0x6e, 0x6b, 0x49, 0x44, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x26, 0x0a, 0x0e, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x64, 0x4c, 0x69, 0x6e, 0x6b, 0x49, 0x44, + 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x64, + 0x4c, 0x69, 0x6e, 0x6b, 0x49, 0x44, 0x73, 0x2a, 0x96, 0x03, 0x0a, 0x07, 0x52, 0x75, 0x6e, 0x54, + 0x79, 0x70, 0x65, 0x12, 0x08, 0x0a, 0x04, 0x4e, 0x55, 0x4c, 0x4c, 0x10, 0x00, 0x12, 0x0b, 0x0a, + 0x07, 0x50, 0x48, 0x59, 0x53, 0x49, 0x43, 0x53, 0x10, 0x01, 0x12, 0x0d, 0x0a, 0x09, 0x54, 0x45, + 0x43, 0x48, 0x4e, 0x49, 0x43, 0x41, 0x4c, 0x10, 0x02, 0x12, 0x0c, 0x0a, 0x08, 0x50, 0x45, 0x44, + 0x45, 0x53, 0x54, 0x41, 0x4c, 0x10, 0x03, 0x12, 0x0a, 0x0a, 0x06, 0x50, 0x55, 0x4c, 0x53, 0x45, + 0x52, 0x10, 0x04, 0x12, 0x09, 0x0a, 0x05, 0x4c, 0x41, 0x53, 0x45, 0x52, 0x10, 0x05, 0x12, 0x1b, + 0x0a, 0x17, 0x43, 0x41, 0x4c, 0x49, 0x42, 0x52, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x49, 0x54, + 0x48, 0x52, 0x5f, 0x54, 0x55, 0x4e, 0x49, 0x4e, 0x47, 0x10, 0x06, 0x12, 0x1c, 0x0a, 0x18, 0x43, + 0x41, 0x4c, 0x49, 0x42, 0x52, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x56, 0x43, 0x41, 0x53, 0x4e, + 0x5f, 0x54, 0x55, 0x4e, 0x49, 0x4e, 0x47, 0x10, 0x07, 0x12, 0x18, 0x0a, 0x14, 0x43, 0x41, 0x4c, + 0x49, 0x42, 0x52, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x54, 0x48, 0x52, 0x5f, 0x53, 0x43, 0x41, + 0x4e, 0x10, 0x08, 0x12, 0x1c, 0x0a, 0x18, 0x43, 0x41, 0x4c, 0x49, 0x42, 0x52, 0x41, 0x54, 0x49, + 0x4f, 0x4e, 0x5f, 0x44, 0x49, 0x47, 0x49, 0x54, 0x41, 0x4c, 0x5f, 0x53, 0x43, 0x41, 0x4e, 0x10, + 0x09, 0x12, 0x1b, 0x0a, 0x17, 0x43, 0x41, 0x4c, 0x49, 0x42, 0x52, 0x41, 0x54, 0x49, 0x4f, 0x4e, + 0x5f, 0x41, 0x4e, 0x41, 0x4c, 0x4f, 0x47, 0x5f, 0x53, 0x43, 0x41, 0x4e, 0x10, 0x0a, 0x12, 0x13, + 0x0a, 0x0f, 0x43, 0x41, 0x4c, 0x49, 0x42, 0x52, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x46, 0x48, + 0x52, 0x10, 0x0b, 0x12, 0x1b, 0x0a, 0x17, 0x43, 0x41, 0x4c, 0x49, 0x42, 0x52, 0x41, 0x54, 0x49, + 0x4f, 0x4e, 0x5f, 0x41, 0x4c, 0x50, 0x49, 0x44, 0x45, 0x5f, 0x53, 0x43, 0x41, 0x4e, 0x10, 0x0c, + 0x12, 0x0f, 0x0a, 0x0b, 0x43, 0x41, 0x4c, 0x49, 0x42, 0x52, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x10, + 0x0d, 0x12, 0x0b, 0x0a, 0x07, 0x43, 0x4f, 0x53, 0x4d, 0x49, 0x43, 0x53, 0x10, 0x0e, 0x12, 0x0d, + 0x0a, 0x09, 0x53, 0x59, 0x4e, 0x54, 0x48, 0x45, 0x54, 0x49, 0x43, 0x10, 0x0f, 0x12, 0x09, 0x0a, + 0x05, 0x4e, 0x4f, 0x49, 0x53, 0x45, 0x10, 0x10, 0x12, 0x1c, 0x0a, 0x18, 0x43, 0x41, 0x4c, 0x49, + 0x42, 0x52, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x50, 0x55, 0x4c, 0x53, 0x45, 0x5f, 0x4c, 0x45, + 0x4e, 0x47, 0x54, 0x48, 0x10, 0x11, 0x12, 0x17, 0x0a, 0x13, 0x43, 0x41, 0x4c, 0x49, 0x42, 0x52, + 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x56, 0x52, 0x45, 0x53, 0x45, 0x54, 0x44, 0x10, 0x12, 0x12, + 0x08, 0x0a, 0x03, 0x41, 0x4e, 0x59, 0x10, 0xac, 0x02, 0x22, 0x05, 0x08, 0x13, 0x10, 0xab, 0x02, + 0x32, 0x91, 0x0f, 0x0a, 0x07, 0x41, 0x70, 0x72, 0x69, 0x63, 0x6f, 0x74, 0x12, 0x3c, 0x0a, 0x0c, + 0x4e, 0x65, 0x77, 0x52, 0x75, 0x6e, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x0e, 0x2e, 0x61, + 0x70, 0x72, 0x69, 0x63, 0x6f, 0x74, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1a, 0x2e, 0x61, + 0x70, 0x72, 0x69, 0x63, 0x6f, 0x74, 0x2e, 0x52, 0x75, 0x6e, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x33, 0x0a, 0x0b, 0x47, 0x65, + 0x74, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x12, 0x0e, 0x2e, 0x61, 0x70, 0x72, 0x69, + 0x63, 0x6f, 0x74, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x12, 0x2e, 0x61, 0x70, 0x72, 0x69, + 0x63, 0x6f, 0x74, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x4d, 0x61, 0x70, 0x22, 0x00, 0x12, + 0x2f, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x56, 0x61, 0x72, 0x73, 0x12, 0x0e, 0x2e, 0x61, 0x70, 0x72, + 0x69, 0x63, 0x6f, 0x74, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x12, 0x2e, 0x61, 0x70, 0x72, + 0x69, 0x63, 0x6f, 0x74, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x4d, 0x61, 0x70, 0x22, 0x00, + 0x12, 0x50, 0x0a, 0x0f, 0x52, 0x61, 0x77, 0x47, 0x65, 0x74, 0x52, 0x65, 0x63, 0x75, 0x72, 0x73, + 0x69, 0x76, 0x65, 0x12, 0x1f, 0x2e, 0x61, 0x70, 0x72, 0x69, 0x63, 0x6f, 0x74, 0x2e, 0x52, 0x61, + 0x77, 0x47, 0x65, 0x74, 0x52, 0x65, 0x63, 0x75, 0x72, 0x73, 0x69, 0x76, 0x65, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x61, 0x70, 0x72, 0x69, 0x63, 0x6f, 0x74, 0x2e, 0x43, + 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x00, 0x12, 0x48, 0x0a, 0x0d, 0x4c, 0x69, 0x73, 0x74, 0x44, 0x65, 0x74, 0x65, 0x63, 0x74, + 0x6f, 0x72, 0x73, 0x12, 0x19, 0x2e, 0x61, 0x70, 0x72, 0x69, 0x63, 0x6f, 0x74, 0x2e, 0x44, 0x65, + 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, + 0x2e, 0x61, 0x70, 0x72, 0x69, 0x63, 0x6f, 0x74, 0x2e, 0x44, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, + 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4b, 0x0a, 0x10, + 0x47, 0x65, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x49, 0x6e, 0x76, 0x65, 0x6e, 0x74, 0x6f, 0x72, 0x79, + 0x12, 0x17, 0x2e, 0x61, 0x70, 0x72, 0x69, 0x63, 0x6f, 0x74, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x47, + 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x61, 0x70, 0x72, 0x69, + 0x63, 0x6f, 0x74, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4b, 0x0a, 0x15, 0x47, 0x65, 0x74, + 0x44, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x49, 0x6e, 0x76, 0x65, 0x6e, 0x74, 0x6f, + 0x72, 0x79, 0x12, 0x0e, 0x2e, 0x61, 0x70, 0x72, 0x69, 0x63, 0x6f, 0x74, 0x2e, 0x45, 0x6d, 0x70, + 0x74, 0x79, 0x1a, 0x20, 0x2e, 0x61, 0x70, 0x72, 0x69, 0x63, 0x6f, 0x74, 0x2e, 0x44, 0x65, 0x74, + 0x65, 0x63, 0x74, 0x6f, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x47, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x44, 0x65, 0x74, + 0x65, 0x63, 0x74, 0x6f, 0x72, 0x46, 0x6f, 0x72, 0x48, 0x6f, 0x73, 0x74, 0x12, 0x14, 0x2e, 0x61, + 0x70, 0x72, 0x69, 0x63, 0x6f, 0x74, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x61, 0x70, 0x72, 0x69, 0x63, 0x6f, 0x74, 0x2e, 0x44, 0x65, 0x74, + 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, + 0x4b, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x44, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x46, + 0x6f, 0x72, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x12, 0x15, 0x2e, 0x61, 0x70, 0x72, 0x69, 0x63, 0x6f, + 0x74, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, + 0x2e, 0x61, 0x70, 0x72, 0x69, 0x63, 0x6f, 0x74, 0x2e, 0x44, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, + 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x47, 0x0a, 0x12, + 0x47, 0x65, 0x74, 0x43, 0x52, 0x55, 0x43, 0x61, 0x72, 0x64, 0x73, 0x46, 0x6f, 0x72, 0x48, 0x6f, + 0x73, 0x74, 0x12, 0x14, 0x2e, 0x61, 0x70, 0x72, 0x69, 0x63, 0x6f, 0x74, 0x2e, 0x48, 0x6f, 0x73, + 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x61, 0x70, 0x72, 0x69, 0x63, + 0x6f, 0x74, 0x2e, 0x43, 0x52, 0x55, 0x43, 0x61, 0x72, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x52, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x45, 0x6e, 0x64, 0x70, + 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x46, 0x6f, 0x72, 0x43, 0x52, 0x55, 0x43, 0x61, 0x72, 0x64, 0x12, + 0x14, 0x2e, 0x61, 0x70, 0x72, 0x69, 0x63, 0x6f, 0x74, 0x2e, 0x43, 0x61, 0x72, 0x64, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x61, 0x70, 0x72, 0x69, 0x63, 0x6f, 0x74, 0x2e, + 0x43, 0x52, 0x55, 0x43, 0x61, 0x72, 0x64, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4f, 0x0a, 0x18, 0x47, 0x65, 0x74, + 0x4c, 0x69, 0x6e, 0x6b, 0x49, 0x44, 0x73, 0x46, 0x6f, 0x72, 0x43, 0x52, 0x55, 0x45, 0x6e, 0x64, + 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x17, 0x2e, 0x61, 0x70, 0x72, 0x69, 0x63, 0x6f, 0x74, 0x2e, + 0x4c, 0x69, 0x6e, 0x6b, 0x49, 0x44, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, + 0x2e, 0x61, 0x70, 0x72, 0x69, 0x63, 0x6f, 0x74, 0x2e, 0x4c, 0x69, 0x6e, 0x6b, 0x49, 0x44, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x61, 0x0a, 0x1c, 0x47, 0x65, + 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x64, 0x4c, 0x69, 0x6e, 0x6b, 0x49, 0x44, 0x73, 0x46, + 0x6f, 0x72, 0x44, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x1e, 0x2e, 0x61, 0x70, 0x72, + 0x69, 0x63, 0x6f, 0x74, 0x2e, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x64, 0x4c, 0x69, 0x6e, 0x6b, + 0x49, 0x44, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x61, 0x70, 0x72, + 0x69, 0x63, 0x6f, 0x74, 0x2e, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x64, 0x4c, 0x69, 0x6e, 0x6b, + 0x49, 0x44, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x50, 0x0a, + 0x0f, 0x47, 0x65, 0x74, 0x52, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x12, 0x1f, 0x2e, 0x61, 0x70, 0x72, 0x69, 0x63, 0x6f, 0x74, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x75, + 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x1a, 0x2e, 0x61, 0x70, 0x72, 0x69, 0x63, 0x6f, 0x74, 0x2e, 0x43, 0x6f, 0x6d, 0x70, + 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, + 0x44, 0x0a, 0x0f, 0x53, 0x65, 0x74, 0x52, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x12, 0x1f, 0x2e, 0x61, 0x70, 0x72, 0x69, 0x63, 0x6f, 0x74, 0x2e, 0x53, 0x65, 0x74, + 0x52, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x0e, 0x2e, 0x61, 0x70, 0x72, 0x69, 0x63, 0x6f, 0x74, 0x2e, 0x45, 0x6d, + 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x4c, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x52, 0x75, 0x6e, 0x74, + 0x69, 0x6d, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x12, 0x21, 0x2e, 0x61, 0x70, 0x72, + 0x69, 0x63, 0x6f, 0x74, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x45, + 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, + 0x61, 0x70, 0x72, 0x69, 0x63, 0x6f, 0x74, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x4d, 0x61, + 0x70, 0x22, 0x00, 0x12, 0x5d, 0x0a, 0x12, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x75, 0x6e, 0x74, 0x69, + 0x6d, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x12, 0x22, 0x2e, 0x61, 0x70, 0x72, 0x69, + 0x63, 0x6f, 0x74, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x45, + 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, + 0x61, 0x70, 0x72, 0x69, 0x63, 0x6f, 0x74, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, + 0x74, 0x45, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x00, 0x12, 0x45, 0x0a, 0x0e, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, + 0x65, 0x6e, 0x74, 0x73, 0x12, 0x0e, 0x2e, 0x61, 0x70, 0x72, 0x69, 0x63, 0x6f, 0x74, 0x2e, 0x45, + 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x21, 0x2e, 0x61, 0x70, 0x72, 0x69, 0x63, 0x6f, 0x74, 0x2e, 0x43, + 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x61, 0x0a, 0x14, 0x4c, 0x69, 0x73, + 0x74, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x69, 0x65, + 0x73, 0x12, 0x24, 0x2e, 0x61, 0x70, 0x72, 0x69, 0x63, 0x6f, 0x74, 0x2e, 0x4c, 0x69, 0x73, 0x74, + 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x61, 0x70, 0x72, 0x69, 0x63, 0x6f, + 0x74, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x69, + 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x54, 0x0a, 0x19, + 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x19, 0x2e, 0x61, 0x70, 0x72, 0x69, + 0x63, 0x6f, 0x74, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x61, 0x70, 0x72, 0x69, 0x63, 0x6f, 0x74, 0x2e, 0x43, + 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x00, 0x12, 0x6e, 0x0a, 0x26, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, + 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x57, + 0x69, 0x74, 0x68, 0x4c, 0x61, 0x73, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x19, 0x2e, 0x61, + 0x70, 0x72, 0x69, 0x63, 0x6f, 0x74, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x61, 0x70, 0x72, 0x69, 0x63, 0x6f, + 0x74, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x57, 0x69, 0x74, 0x68, 0x4c, 0x61, 0x73, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, + 0x22, 0x00, 0x12, 0x4b, 0x0a, 0x15, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x43, 0x6f, 0x6d, + 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x17, 0x2e, 0x61, 0x70, + 0x72, 0x69, 0x63, 0x6f, 0x74, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x51, + 0x75, 0x65, 0x72, 0x79, 0x1a, 0x17, 0x2e, 0x61, 0x70, 0x72, 0x69, 0x63, 0x6f, 0x74, 0x2e, 0x43, + 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x51, 0x75, 0x65, 0x72, 0x79, 0x22, 0x00, 0x12, + 0x7d, 0x0a, 0x1c, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, + 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x2c, 0x2e, 0x61, 0x70, 0x72, 0x69, 0x63, 0x6f, 0x74, 0x2e, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, + 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d, 0x2e, + 0x61, 0x70, 0x72, 0x69, 0x63, 0x6f, 0x74, 0x2e, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x43, 0x6f, + 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x44, + 0x0a, 0x20, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6d, 0x70, + 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x43, 0x61, 0x63, + 0x68, 0x65, 0x12, 0x0e, 0x2e, 0x61, 0x70, 0x72, 0x69, 0x63, 0x6f, 0x74, 0x2e, 0x45, 0x6d, 0x70, + 0x74, 0x79, 0x1a, 0x0e, 0x2e, 0x61, 0x70, 0x72, 0x69, 0x63, 0x6f, 0x74, 0x2e, 0x45, 0x6d, 0x70, + 0x74, 0x79, 0x22, 0x00, 0x42, 0x5e, 0x0a, 0x22, 0x63, 0x68, 0x2e, 0x63, 0x65, 0x72, 0x6e, 0x2e, + 0x61, 0x6c, 0x69, 0x63, 0x65, 0x2e, 0x6f, 0x32, 0x2e, 0x61, 0x70, 0x72, 0x69, 0x63, 0x6f, 0x74, + 0x2e, 0x72, 0x70, 0x63, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5a, 0x38, 0x67, 0x69, 0x74, 0x68, + 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x41, 0x6c, 0x69, 0x63, 0x65, 0x4f, 0x32, 0x47, 0x72, + 0x6f, 0x75, 0x70, 0x2f, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2f, 0x61, 0x70, 0x72, 0x69, + 0x63, 0x6f, 0x74, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x3b, 0x61, 0x70, 0x72, 0x69, 0x63, + 0x6f, 0x74, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_protos_apricot_proto_rawDescOnce sync.Once + file_protos_apricot_proto_rawDescData = file_protos_apricot_proto_rawDesc +) + +func file_protos_apricot_proto_rawDescGZIP() []byte { + file_protos_apricot_proto_rawDescOnce.Do(func() { + file_protos_apricot_proto_rawDescData = protoimpl.X.CompressGZIP(file_protos_apricot_proto_rawDescData) + }) + return file_protos_apricot_proto_rawDescData +} + +var file_protos_apricot_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_protos_apricot_proto_msgTypes = make([]protoimpl.MessageInfo, 37) +var file_protos_apricot_proto_goTypes = []interface{}{ + (RunType)(0), // 0: apricot.RunType + (*Empty)(nil), // 1: apricot.Empty + (*ComponentQuery)(nil), // 2: apricot.ComponentQuery + (*ComponentRequest)(nil), // 3: apricot.ComponentRequest + (*ComponentResponse)(nil), // 4: apricot.ComponentResponse + (*ComponentResponseWithLastIndex)(nil), // 5: apricot.ComponentResponseWithLastIndex + (*HostRequest)(nil), // 6: apricot.HostRequest + (*HostsRequest)(nil), // 7: apricot.HostsRequest + (*DetectorResponse)(nil), // 8: apricot.DetectorResponse + (*DetectorInventoryResponse)(nil), // 9: apricot.DetectorInventoryResponse + (*DetectorEntriesResponse)(nil), // 10: apricot.DetectorEntriesResponse + (*RunNumberResponse)(nil), // 11: apricot.RunNumberResponse + (*StringMap)(nil), // 12: apricot.StringMap + (*RawGetRecursiveRequest)(nil), // 13: apricot.RawGetRecursiveRequest + (*GetRuntimeEntryRequest)(nil), // 14: apricot.GetRuntimeEntryRequest + (*SetRuntimeEntryRequest)(nil), // 15: apricot.SetRuntimeEntryRequest + (*GetEntryRequest)(nil), // 16: apricot.GetEntryRequest + (*GetRuntimeEntriesRequest)(nil), // 17: apricot.GetRuntimeEntriesRequest + (*ListRuntimeEntriesRequest)(nil), // 18: apricot.ListRuntimeEntriesRequest + (*ComponentEntriesQuery)(nil), // 19: apricot.ComponentEntriesQuery + (*ListComponentEntriesRequest)(nil), // 20: apricot.ListComponentEntriesRequest + (*ComponentEntriesResponse)(nil), // 21: apricot.ComponentEntriesResponse + (*DetectorsRequest)(nil), // 22: apricot.DetectorsRequest + (*DetectorsResponse)(nil), // 23: apricot.DetectorsResponse + (*HostGetRequest)(nil), // 24: apricot.HostGetRequest + (*HostEntriesResponse)(nil), // 25: apricot.HostEntriesResponse + (*ImportComponentConfigurationRequest)(nil), // 26: apricot.ImportComponentConfigurationRequest + (*ImportComponentConfigurationResponse)(nil), // 27: apricot.ImportComponentConfigurationResponse + (*CRUCardsResponse)(nil), // 28: apricot.CRUCardsResponse + (*CardRequest)(nil), // 29: apricot.CardRequest + (*CRUCardEndpointResponse)(nil), // 30: apricot.CRUCardEndpointResponse + (*LinkIDsRequest)(nil), // 31: apricot.LinkIDsRequest + (*LinkIDsResponse)(nil), // 32: apricot.LinkIDsResponse + (*AliasedLinkIDsRequest)(nil), // 33: apricot.AliasedLinkIDsRequest + (*AliasedLinkIDsResponse)(nil), // 34: apricot.AliasedLinkIDsResponse + nil, // 35: apricot.ComponentRequest.VarStackEntry + nil, // 36: apricot.DetectorEntriesResponse.DetectorEntriesEntry + nil, // 37: apricot.StringMap.StringMapEntry +} +var file_protos_apricot_proto_depIdxs = []int32{ + 0, // 0: apricot.ComponentQuery.runType:type_name -> apricot.RunType + 2, // 1: apricot.ComponentRequest.query:type_name -> apricot.ComponentQuery + 35, // 2: apricot.ComponentRequest.varStack:type_name -> apricot.ComponentRequest.VarStackEntry + 36, // 3: apricot.DetectorEntriesResponse.detectorEntries:type_name -> apricot.DetectorEntriesResponse.DetectorEntriesEntry + 37, // 4: apricot.StringMap.stringMap:type_name -> apricot.StringMap.StringMapEntry + 0, // 5: apricot.ComponentEntriesQuery.runType:type_name -> apricot.RunType + 19, // 6: apricot.ListComponentEntriesRequest.query:type_name -> apricot.ComponentEntriesQuery + 2, // 7: apricot.ImportComponentConfigurationRequest.query:type_name -> apricot.ComponentQuery + 9, // 8: apricot.DetectorEntriesResponse.DetectorEntriesEntry.value:type_name -> apricot.DetectorInventoryResponse + 1, // 9: apricot.Apricot.NewRunNumber:input_type -> apricot.Empty + 1, // 10: apricot.Apricot.GetDefaults:input_type -> apricot.Empty + 1, // 11: apricot.Apricot.GetVars:input_type -> apricot.Empty + 13, // 12: apricot.Apricot.RawGetRecursive:input_type -> apricot.RawGetRecursiveRequest + 22, // 13: apricot.Apricot.ListDetectors:input_type -> apricot.DetectorsRequest + 24, // 14: apricot.Apricot.GetHostInventory:input_type -> apricot.HostGetRequest + 1, // 15: apricot.Apricot.GetDetectorsInventory:input_type -> apricot.Empty + 6, // 16: apricot.Apricot.GetDetectorForHost:input_type -> apricot.HostRequest + 7, // 17: apricot.Apricot.GetDetectorsForHosts:input_type -> apricot.HostsRequest + 6, // 18: apricot.Apricot.GetCRUCardsForHost:input_type -> apricot.HostRequest + 29, // 19: apricot.Apricot.GetEndpointsForCRUCard:input_type -> apricot.CardRequest + 31, // 20: apricot.Apricot.GetLinkIDsForCRUEndpoint:input_type -> apricot.LinkIDsRequest + 33, // 21: apricot.Apricot.GetAliasedLinkIDsForDetector:input_type -> apricot.AliasedLinkIDsRequest + 14, // 22: apricot.Apricot.GetRuntimeEntry:input_type -> apricot.GetRuntimeEntryRequest + 15, // 23: apricot.Apricot.SetRuntimeEntry:input_type -> apricot.SetRuntimeEntryRequest + 17, // 24: apricot.Apricot.GetRuntimeEntries:input_type -> apricot.GetRuntimeEntriesRequest + 18, // 25: apricot.Apricot.ListRuntimeEntries:input_type -> apricot.ListRuntimeEntriesRequest + 1, // 26: apricot.Apricot.ListComponents:input_type -> apricot.Empty + 20, // 27: apricot.Apricot.ListComponentEntries:input_type -> apricot.ListComponentEntriesRequest + 3, // 28: apricot.Apricot.GetComponentConfiguration:input_type -> apricot.ComponentRequest + 3, // 29: apricot.Apricot.GetComponentConfigurationWithLastIndex:input_type -> apricot.ComponentRequest + 2, // 30: apricot.Apricot.ResolveComponentQuery:input_type -> apricot.ComponentQuery + 26, // 31: apricot.Apricot.ImportComponentConfiguration:input_type -> apricot.ImportComponentConfigurationRequest + 1, // 32: apricot.Apricot.InvalidateComponentTemplateCache:input_type -> apricot.Empty + 11, // 33: apricot.Apricot.NewRunNumber:output_type -> apricot.RunNumberResponse + 12, // 34: apricot.Apricot.GetDefaults:output_type -> apricot.StringMap + 12, // 35: apricot.Apricot.GetVars:output_type -> apricot.StringMap + 4, // 36: apricot.Apricot.RawGetRecursive:output_type -> apricot.ComponentResponse + 23, // 37: apricot.Apricot.ListDetectors:output_type -> apricot.DetectorsResponse + 25, // 38: apricot.Apricot.GetHostInventory:output_type -> apricot.HostEntriesResponse + 10, // 39: apricot.Apricot.GetDetectorsInventory:output_type -> apricot.DetectorEntriesResponse + 8, // 40: apricot.Apricot.GetDetectorForHost:output_type -> apricot.DetectorResponse + 23, // 41: apricot.Apricot.GetDetectorsForHosts:output_type -> apricot.DetectorsResponse + 28, // 42: apricot.Apricot.GetCRUCardsForHost:output_type -> apricot.CRUCardsResponse + 30, // 43: apricot.Apricot.GetEndpointsForCRUCard:output_type -> apricot.CRUCardEndpointResponse + 32, // 44: apricot.Apricot.GetLinkIDsForCRUEndpoint:output_type -> apricot.LinkIDsResponse + 34, // 45: apricot.Apricot.GetAliasedLinkIDsForDetector:output_type -> apricot.AliasedLinkIDsResponse + 4, // 46: apricot.Apricot.GetRuntimeEntry:output_type -> apricot.ComponentResponse + 1, // 47: apricot.Apricot.SetRuntimeEntry:output_type -> apricot.Empty + 12, // 48: apricot.Apricot.GetRuntimeEntries:output_type -> apricot.StringMap + 21, // 49: apricot.Apricot.ListRuntimeEntries:output_type -> apricot.ComponentEntriesResponse + 21, // 50: apricot.Apricot.ListComponents:output_type -> apricot.ComponentEntriesResponse + 21, // 51: apricot.Apricot.ListComponentEntries:output_type -> apricot.ComponentEntriesResponse + 4, // 52: apricot.Apricot.GetComponentConfiguration:output_type -> apricot.ComponentResponse + 5, // 53: apricot.Apricot.GetComponentConfigurationWithLastIndex:output_type -> apricot.ComponentResponseWithLastIndex + 2, // 54: apricot.Apricot.ResolveComponentQuery:output_type -> apricot.ComponentQuery + 27, // 55: apricot.Apricot.ImportComponentConfiguration:output_type -> apricot.ImportComponentConfigurationResponse + 1, // 56: apricot.Apricot.InvalidateComponentTemplateCache:output_type -> apricot.Empty + 33, // [33:57] is the sub-list for method output_type + 9, // [9:33] is the sub-list for method input_type + 9, // [9:9] is the sub-list for extension type_name + 9, // [9:9] is the sub-list for extension extendee + 0, // [0:9] is the sub-list for field type_name +} + +func init() { file_protos_apricot_proto_init() } +func file_protos_apricot_proto_init() { + if File_protos_apricot_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_protos_apricot_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Empty); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_apricot_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ComponentQuery); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_apricot_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ComponentRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_apricot_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ComponentResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_apricot_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ComponentResponseWithLastIndex); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_apricot_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*HostRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_apricot_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*HostsRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_apricot_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DetectorResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_apricot_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DetectorInventoryResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_apricot_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DetectorEntriesResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_apricot_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RunNumberResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_apricot_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StringMap); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_apricot_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RawGetRecursiveRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_apricot_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetRuntimeEntryRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_apricot_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SetRuntimeEntryRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_apricot_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetEntryRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_apricot_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetRuntimeEntriesRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_apricot_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ListRuntimeEntriesRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_apricot_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ComponentEntriesQuery); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_apricot_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ListComponentEntriesRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_apricot_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ComponentEntriesResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_apricot_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DetectorsRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_apricot_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DetectorsResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_apricot_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*HostGetRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_apricot_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*HostEntriesResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_apricot_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ImportComponentConfigurationRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_apricot_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ImportComponentConfigurationResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_apricot_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CRUCardsResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_apricot_proto_msgTypes[28].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CardRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_apricot_proto_msgTypes[29].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CRUCardEndpointResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_apricot_proto_msgTypes[30].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*LinkIDsRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_apricot_proto_msgTypes[31].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*LinkIDsResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_apricot_proto_msgTypes[32].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AliasedLinkIDsRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_apricot_proto_msgTypes[33].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AliasedLinkIDsResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + file_protos_apricot_proto_msgTypes[2].OneofWrappers = []interface{}{ + (*ComponentRequest_Path)(nil), + (*ComponentRequest_Query)(nil), + } + file_protos_apricot_proto_msgTypes[19].OneofWrappers = []interface{}{ + (*ListComponentEntriesRequest_Path)(nil), + (*ListComponentEntriesRequest_Query)(nil), + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_protos_apricot_proto_rawDesc, + NumEnums: 1, + NumMessages: 37, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_protos_apricot_proto_goTypes, + DependencyIndexes: file_protos_apricot_proto_depIdxs, + EnumInfos: file_protos_apricot_proto_enumTypes, + MessageInfos: file_protos_apricot_proto_msgTypes, + }.Build() + File_protos_apricot_proto = out.File + file_protos_apricot_proto_rawDesc = nil + file_protos_apricot_proto_goTypes = nil + file_protos_apricot_proto_depIdxs = nil +} diff --git a/apricot/protos/apricot.proto b/apricot/protos/apricot.proto new file mode 100644 index 000000000..d9729e79b --- /dev/null +++ b/apricot/protos/apricot.proto @@ -0,0 +1,250 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2020-2021 CERN and copyright holders of ALICE O². + * Author: Teo Mrnjavac + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +syntax = "proto3"; + +package apricot; +option java_package = "ch.cern.alice.o2.apricot.rpcserver"; +option go_package = "github.com/AliceO2Group/Control/apricot/protos;apricotpb"; + +service Apricot { + rpc NewRunNumber(Empty) returns (RunNumberResponse) {} + rpc GetDefaults(Empty) returns (StringMap) {} + rpc GetVars(Empty) returns (StringMap) {} + rpc RawGetRecursive(RawGetRecursiveRequest) returns (ComponentResponse) {} + + // Detectors and host inventories + rpc ListDetectors(DetectorsRequest) returns (DetectorsResponse) {} + rpc GetHostInventory(HostGetRequest) returns (HostEntriesResponse) {} + rpc GetDetectorsInventory(Empty) returns (DetectorEntriesResponse) {} + rpc GetDetectorForHost(HostRequest) returns (DetectorResponse) {} + rpc GetDetectorsForHosts(HostsRequest) returns (DetectorsResponse) {} + rpc GetCRUCardsForHost(HostRequest) returns (CRUCardsResponse) {} + rpc GetEndpointsForCRUCard(CardRequest) returns (CRUCardEndpointResponse) {} + rpc GetLinkIDsForCRUEndpoint(LinkIDsRequest) returns (LinkIDsResponse) {} + rpc GetAliasedLinkIDsForDetector(AliasedLinkIDsRequest) returns (AliasedLinkIDsResponse) {} + + // Runtime KV calls + rpc GetRuntimeEntry(GetRuntimeEntryRequest) returns (ComponentResponse) {} + rpc SetRuntimeEntry(SetRuntimeEntryRequest) returns (Empty) {} + rpc GetRuntimeEntries(GetRuntimeEntriesRequest) returns (StringMap) {} + rpc ListRuntimeEntries(ListRuntimeEntriesRequest) returns (ComponentEntriesResponse) {} + + // Component configuration calls + rpc ListComponents(Empty) returns (ComponentEntriesResponse) {} + rpc ListComponentEntries(ListComponentEntriesRequest) returns (ComponentEntriesResponse) {} + rpc GetComponentConfiguration(ComponentRequest) returns (ComponentResponse) {} + rpc GetComponentConfigurationWithLastIndex(ComponentRequest) returns (ComponentResponseWithLastIndex) {} + rpc ResolveComponentQuery(ComponentQuery) returns (ComponentQuery) {} + rpc ImportComponentConfiguration(ImportComponentConfigurationRequest) returns (ImportComponentConfigurationResponse) {} + rpc InvalidateComponentTemplateCache(Empty) returns (Empty) {} +} + +// NOTE: make sure the enum values include and match those in RunType in dcs.pb.go and runtype.go +// NOTE: this run type list is replicated in AliceO2 repo in +// https://github.com/AliceO2Group/AliceO2/blob/dev/DataFormats/Parameters/include/DataFormatsParameters/ECSDataAdapters.h +// Inform Ruben when the list is updated. + +enum RunType { + NULL = 0; + PHYSICS = 1; + TECHNICAL = 2; + PEDESTAL = 3; + PULSER = 4; + LASER = 5; + CALIBRATION_ITHR_TUNING = 6; + CALIBRATION_VCASN_TUNING = 7; + CALIBRATION_THR_SCAN = 8; + CALIBRATION_DIGITAL_SCAN = 9; + CALIBRATION_ANALOG_SCAN = 10; + CALIBRATION_FHR = 11; + CALIBRATION_ALPIDE_SCAN = 12; + CALIBRATION = 13; + COSMICS = 14; + SYNTHETIC = 15; + NOISE = 16; + CALIBRATION_PULSE_LENGTH = 17; + CALIBRATION_VRESETD = 18; + reserved 19 to 299; + + ANY = 300; +} + +message Empty {} + +message ComponentQuery { + string component = 1; + RunType runType = 2; + string machineRole = 3; + string entry = 4; +} + +message ComponentRequest { + oneof queryPath { + string path = 1; + ComponentQuery query = 2; + } + bool processTemplate = 3; + map varStack = 4; +} + +message ComponentResponse { + string payload = 1; +} + +message ComponentResponseWithLastIndex { + string payload = 1; + uint64 lastIndex = 2; +} + +message HostRequest { + string hostname = 1; +} + +message HostsRequest { + repeated string hosts = 1; +} + +message DetectorResponse { + string payload = 1; +} + +message DetectorInventoryResponse { + repeated string flps = 1; +} + +message DetectorEntriesResponse { + map detectorEntries = 1; +} + +message RunNumberResponse { + uint32 runNumber = 1; +} + +message StringMap { + map stringMap = 1; +} + +message RawGetRecursiveRequest { + string rawPath = 1; +} + +message GetRuntimeEntryRequest { + string component = 1; + string key = 2; +} + +message SetRuntimeEntryRequest { + string component = 1; + string key = 2; + string value = 3; +} +message GetEntryRequest { + string key = 1; +} + +message GetRuntimeEntriesRequest { + string component = 1; +} + +message ListRuntimeEntriesRequest { + string component = 1; +} + +message ComponentEntriesQuery { + string component = 1; + RunType runType = 2; + string machineRole = 3; +} + +message ListComponentEntriesRequest { + oneof queryPath { + string path = 1; + ComponentEntriesQuery query = 2; + } +} + +message ComponentEntriesResponse { + repeated string payload = 1; +} + +message DetectorsRequest { + bool getAll = 1; // if false(default) restricts "private" detectors (e.g. TRG) +} + +message DetectorsResponse { + repeated string detectors = 1; +} + +message HostGetRequest { + string detector = 1; +} + +message HostEntriesResponse { + repeated string hosts = 1; +} + +message ImportComponentConfigurationRequest { + ComponentQuery query = 1; + string payload = 2; + bool newComponent = 3; +} + +message ImportComponentConfigurationResponse { + bool existingComponentUpdated = 1; + bool existingEntryUpdated = 2; +} + +message CRUCardsResponse { + string cards = 1; +} + +message CardRequest { + string hostname = 1; + string cardSerial = 2; +} + +message CRUCardEndpointResponse { + string endpoints = 1; +} + +message LinkIDsRequest { + string hostname = 1; + string cardSerial = 2; + string endpoint = 3; + bool onlyEnabled = 4; +} + +message LinkIDsResponse { + repeated string linkIDs = 1; +} + +message AliasedLinkIDsRequest { + string detector = 1; + bool onlyEnabled = 2; +} + +message AliasedLinkIDsResponse { + repeated string aliasedLinkIDs = 1; +} \ No newline at end of file diff --git a/apricot/protos/apricot_grpc.pb.go b/apricot/protos/apricot_grpc.pb.go new file mode 100644 index 000000000..1a2d651f1 --- /dev/null +++ b/apricot/protos/apricot_grpc.pb.go @@ -0,0 +1,987 @@ +// +// === This file is part of ALICE O² === +// +// Copyright 2020-2021 CERN and copyright holders of ALICE O². +// Author: Teo Mrnjavac +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// In applying this license CERN does not waive the privileges and +// immunities granted to it by virtue of its status as an +// Intergovernmental Organization or submit itself to any jurisdiction. + +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.3.0 +// - protoc v5.29.3 +// source: protos/apricot.proto + +package apricotpb + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +const ( + Apricot_NewRunNumber_FullMethodName = "/apricot.Apricot/NewRunNumber" + Apricot_GetDefaults_FullMethodName = "/apricot.Apricot/GetDefaults" + Apricot_GetVars_FullMethodName = "/apricot.Apricot/GetVars" + Apricot_RawGetRecursive_FullMethodName = "/apricot.Apricot/RawGetRecursive" + Apricot_ListDetectors_FullMethodName = "/apricot.Apricot/ListDetectors" + Apricot_GetHostInventory_FullMethodName = "/apricot.Apricot/GetHostInventory" + Apricot_GetDetectorsInventory_FullMethodName = "/apricot.Apricot/GetDetectorsInventory" + Apricot_GetDetectorForHost_FullMethodName = "/apricot.Apricot/GetDetectorForHost" + Apricot_GetDetectorsForHosts_FullMethodName = "/apricot.Apricot/GetDetectorsForHosts" + Apricot_GetCRUCardsForHost_FullMethodName = "/apricot.Apricot/GetCRUCardsForHost" + Apricot_GetEndpointsForCRUCard_FullMethodName = "/apricot.Apricot/GetEndpointsForCRUCard" + Apricot_GetLinkIDsForCRUEndpoint_FullMethodName = "/apricot.Apricot/GetLinkIDsForCRUEndpoint" + Apricot_GetAliasedLinkIDsForDetector_FullMethodName = "/apricot.Apricot/GetAliasedLinkIDsForDetector" + Apricot_GetRuntimeEntry_FullMethodName = "/apricot.Apricot/GetRuntimeEntry" + Apricot_SetRuntimeEntry_FullMethodName = "/apricot.Apricot/SetRuntimeEntry" + Apricot_GetRuntimeEntries_FullMethodName = "/apricot.Apricot/GetRuntimeEntries" + Apricot_ListRuntimeEntries_FullMethodName = "/apricot.Apricot/ListRuntimeEntries" + Apricot_ListComponents_FullMethodName = "/apricot.Apricot/ListComponents" + Apricot_ListComponentEntries_FullMethodName = "/apricot.Apricot/ListComponentEntries" + Apricot_GetComponentConfiguration_FullMethodName = "/apricot.Apricot/GetComponentConfiguration" + Apricot_GetComponentConfigurationWithLastIndex_FullMethodName = "/apricot.Apricot/GetComponentConfigurationWithLastIndex" + Apricot_ResolveComponentQuery_FullMethodName = "/apricot.Apricot/ResolveComponentQuery" + Apricot_ImportComponentConfiguration_FullMethodName = "/apricot.Apricot/ImportComponentConfiguration" + Apricot_InvalidateComponentTemplateCache_FullMethodName = "/apricot.Apricot/InvalidateComponentTemplateCache" +) + +// ApricotClient is the client API for Apricot service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type ApricotClient interface { + NewRunNumber(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*RunNumberResponse, error) + GetDefaults(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*StringMap, error) + GetVars(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*StringMap, error) + RawGetRecursive(ctx context.Context, in *RawGetRecursiveRequest, opts ...grpc.CallOption) (*ComponentResponse, error) + // Detectors and host inventories + ListDetectors(ctx context.Context, in *DetectorsRequest, opts ...grpc.CallOption) (*DetectorsResponse, error) + GetHostInventory(ctx context.Context, in *HostGetRequest, opts ...grpc.CallOption) (*HostEntriesResponse, error) + GetDetectorsInventory(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*DetectorEntriesResponse, error) + GetDetectorForHost(ctx context.Context, in *HostRequest, opts ...grpc.CallOption) (*DetectorResponse, error) + GetDetectorsForHosts(ctx context.Context, in *HostsRequest, opts ...grpc.CallOption) (*DetectorsResponse, error) + GetCRUCardsForHost(ctx context.Context, in *HostRequest, opts ...grpc.CallOption) (*CRUCardsResponse, error) + GetEndpointsForCRUCard(ctx context.Context, in *CardRequest, opts ...grpc.CallOption) (*CRUCardEndpointResponse, error) + GetLinkIDsForCRUEndpoint(ctx context.Context, in *LinkIDsRequest, opts ...grpc.CallOption) (*LinkIDsResponse, error) + GetAliasedLinkIDsForDetector(ctx context.Context, in *AliasedLinkIDsRequest, opts ...grpc.CallOption) (*AliasedLinkIDsResponse, error) + // Runtime KV calls + GetRuntimeEntry(ctx context.Context, in *GetRuntimeEntryRequest, opts ...grpc.CallOption) (*ComponentResponse, error) + SetRuntimeEntry(ctx context.Context, in *SetRuntimeEntryRequest, opts ...grpc.CallOption) (*Empty, error) + GetRuntimeEntries(ctx context.Context, in *GetRuntimeEntriesRequest, opts ...grpc.CallOption) (*StringMap, error) + ListRuntimeEntries(ctx context.Context, in *ListRuntimeEntriesRequest, opts ...grpc.CallOption) (*ComponentEntriesResponse, error) + // Component configuration calls + ListComponents(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*ComponentEntriesResponse, error) + ListComponentEntries(ctx context.Context, in *ListComponentEntriesRequest, opts ...grpc.CallOption) (*ComponentEntriesResponse, error) + GetComponentConfiguration(ctx context.Context, in *ComponentRequest, opts ...grpc.CallOption) (*ComponentResponse, error) + GetComponentConfigurationWithLastIndex(ctx context.Context, in *ComponentRequest, opts ...grpc.CallOption) (*ComponentResponseWithLastIndex, error) + ResolveComponentQuery(ctx context.Context, in *ComponentQuery, opts ...grpc.CallOption) (*ComponentQuery, error) + ImportComponentConfiguration(ctx context.Context, in *ImportComponentConfigurationRequest, opts ...grpc.CallOption) (*ImportComponentConfigurationResponse, error) + InvalidateComponentTemplateCache(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Empty, error) +} + +type apricotClient struct { + cc grpc.ClientConnInterface +} + +func NewApricotClient(cc grpc.ClientConnInterface) ApricotClient { + return &apricotClient{cc} +} + +func (c *apricotClient) NewRunNumber(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*RunNumberResponse, error) { + out := new(RunNumberResponse) + err := c.cc.Invoke(ctx, Apricot_NewRunNumber_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *apricotClient) GetDefaults(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*StringMap, error) { + out := new(StringMap) + err := c.cc.Invoke(ctx, Apricot_GetDefaults_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *apricotClient) GetVars(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*StringMap, error) { + out := new(StringMap) + err := c.cc.Invoke(ctx, Apricot_GetVars_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *apricotClient) RawGetRecursive(ctx context.Context, in *RawGetRecursiveRequest, opts ...grpc.CallOption) (*ComponentResponse, error) { + out := new(ComponentResponse) + err := c.cc.Invoke(ctx, Apricot_RawGetRecursive_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *apricotClient) ListDetectors(ctx context.Context, in *DetectorsRequest, opts ...grpc.CallOption) (*DetectorsResponse, error) { + out := new(DetectorsResponse) + err := c.cc.Invoke(ctx, Apricot_ListDetectors_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *apricotClient) GetHostInventory(ctx context.Context, in *HostGetRequest, opts ...grpc.CallOption) (*HostEntriesResponse, error) { + out := new(HostEntriesResponse) + err := c.cc.Invoke(ctx, Apricot_GetHostInventory_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *apricotClient) GetDetectorsInventory(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*DetectorEntriesResponse, error) { + out := new(DetectorEntriesResponse) + err := c.cc.Invoke(ctx, Apricot_GetDetectorsInventory_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *apricotClient) GetDetectorForHost(ctx context.Context, in *HostRequest, opts ...grpc.CallOption) (*DetectorResponse, error) { + out := new(DetectorResponse) + err := c.cc.Invoke(ctx, Apricot_GetDetectorForHost_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *apricotClient) GetDetectorsForHosts(ctx context.Context, in *HostsRequest, opts ...grpc.CallOption) (*DetectorsResponse, error) { + out := new(DetectorsResponse) + err := c.cc.Invoke(ctx, Apricot_GetDetectorsForHosts_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *apricotClient) GetCRUCardsForHost(ctx context.Context, in *HostRequest, opts ...grpc.CallOption) (*CRUCardsResponse, error) { + out := new(CRUCardsResponse) + err := c.cc.Invoke(ctx, Apricot_GetCRUCardsForHost_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *apricotClient) GetEndpointsForCRUCard(ctx context.Context, in *CardRequest, opts ...grpc.CallOption) (*CRUCardEndpointResponse, error) { + out := new(CRUCardEndpointResponse) + err := c.cc.Invoke(ctx, Apricot_GetEndpointsForCRUCard_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *apricotClient) GetLinkIDsForCRUEndpoint(ctx context.Context, in *LinkIDsRequest, opts ...grpc.CallOption) (*LinkIDsResponse, error) { + out := new(LinkIDsResponse) + err := c.cc.Invoke(ctx, Apricot_GetLinkIDsForCRUEndpoint_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *apricotClient) GetAliasedLinkIDsForDetector(ctx context.Context, in *AliasedLinkIDsRequest, opts ...grpc.CallOption) (*AliasedLinkIDsResponse, error) { + out := new(AliasedLinkIDsResponse) + err := c.cc.Invoke(ctx, Apricot_GetAliasedLinkIDsForDetector_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *apricotClient) GetRuntimeEntry(ctx context.Context, in *GetRuntimeEntryRequest, opts ...grpc.CallOption) (*ComponentResponse, error) { + out := new(ComponentResponse) + err := c.cc.Invoke(ctx, Apricot_GetRuntimeEntry_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *apricotClient) SetRuntimeEntry(ctx context.Context, in *SetRuntimeEntryRequest, opts ...grpc.CallOption) (*Empty, error) { + out := new(Empty) + err := c.cc.Invoke(ctx, Apricot_SetRuntimeEntry_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *apricotClient) GetRuntimeEntries(ctx context.Context, in *GetRuntimeEntriesRequest, opts ...grpc.CallOption) (*StringMap, error) { + out := new(StringMap) + err := c.cc.Invoke(ctx, Apricot_GetRuntimeEntries_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *apricotClient) ListRuntimeEntries(ctx context.Context, in *ListRuntimeEntriesRequest, opts ...grpc.CallOption) (*ComponentEntriesResponse, error) { + out := new(ComponentEntriesResponse) + err := c.cc.Invoke(ctx, Apricot_ListRuntimeEntries_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *apricotClient) ListComponents(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*ComponentEntriesResponse, error) { + out := new(ComponentEntriesResponse) + err := c.cc.Invoke(ctx, Apricot_ListComponents_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *apricotClient) ListComponentEntries(ctx context.Context, in *ListComponentEntriesRequest, opts ...grpc.CallOption) (*ComponentEntriesResponse, error) { + out := new(ComponentEntriesResponse) + err := c.cc.Invoke(ctx, Apricot_ListComponentEntries_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *apricotClient) GetComponentConfiguration(ctx context.Context, in *ComponentRequest, opts ...grpc.CallOption) (*ComponentResponse, error) { + out := new(ComponentResponse) + err := c.cc.Invoke(ctx, Apricot_GetComponentConfiguration_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *apricotClient) GetComponentConfigurationWithLastIndex(ctx context.Context, in *ComponentRequest, opts ...grpc.CallOption) (*ComponentResponseWithLastIndex, error) { + out := new(ComponentResponseWithLastIndex) + err := c.cc.Invoke(ctx, Apricot_GetComponentConfigurationWithLastIndex_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *apricotClient) ResolveComponentQuery(ctx context.Context, in *ComponentQuery, opts ...grpc.CallOption) (*ComponentQuery, error) { + out := new(ComponentQuery) + err := c.cc.Invoke(ctx, Apricot_ResolveComponentQuery_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *apricotClient) ImportComponentConfiguration(ctx context.Context, in *ImportComponentConfigurationRequest, opts ...grpc.CallOption) (*ImportComponentConfigurationResponse, error) { + out := new(ImportComponentConfigurationResponse) + err := c.cc.Invoke(ctx, Apricot_ImportComponentConfiguration_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *apricotClient) InvalidateComponentTemplateCache(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Empty, error) { + out := new(Empty) + err := c.cc.Invoke(ctx, Apricot_InvalidateComponentTemplateCache_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// ApricotServer is the server API for Apricot service. +// All implementations should embed UnimplementedApricotServer +// for forward compatibility +type ApricotServer interface { + NewRunNumber(context.Context, *Empty) (*RunNumberResponse, error) + GetDefaults(context.Context, *Empty) (*StringMap, error) + GetVars(context.Context, *Empty) (*StringMap, error) + RawGetRecursive(context.Context, *RawGetRecursiveRequest) (*ComponentResponse, error) + // Detectors and host inventories + ListDetectors(context.Context, *DetectorsRequest) (*DetectorsResponse, error) + GetHostInventory(context.Context, *HostGetRequest) (*HostEntriesResponse, error) + GetDetectorsInventory(context.Context, *Empty) (*DetectorEntriesResponse, error) + GetDetectorForHost(context.Context, *HostRequest) (*DetectorResponse, error) + GetDetectorsForHosts(context.Context, *HostsRequest) (*DetectorsResponse, error) + GetCRUCardsForHost(context.Context, *HostRequest) (*CRUCardsResponse, error) + GetEndpointsForCRUCard(context.Context, *CardRequest) (*CRUCardEndpointResponse, error) + GetLinkIDsForCRUEndpoint(context.Context, *LinkIDsRequest) (*LinkIDsResponse, error) + GetAliasedLinkIDsForDetector(context.Context, *AliasedLinkIDsRequest) (*AliasedLinkIDsResponse, error) + // Runtime KV calls + GetRuntimeEntry(context.Context, *GetRuntimeEntryRequest) (*ComponentResponse, error) + SetRuntimeEntry(context.Context, *SetRuntimeEntryRequest) (*Empty, error) + GetRuntimeEntries(context.Context, *GetRuntimeEntriesRequest) (*StringMap, error) + ListRuntimeEntries(context.Context, *ListRuntimeEntriesRequest) (*ComponentEntriesResponse, error) + // Component configuration calls + ListComponents(context.Context, *Empty) (*ComponentEntriesResponse, error) + ListComponentEntries(context.Context, *ListComponentEntriesRequest) (*ComponentEntriesResponse, error) + GetComponentConfiguration(context.Context, *ComponentRequest) (*ComponentResponse, error) + GetComponentConfigurationWithLastIndex(context.Context, *ComponentRequest) (*ComponentResponseWithLastIndex, error) + ResolveComponentQuery(context.Context, *ComponentQuery) (*ComponentQuery, error) + ImportComponentConfiguration(context.Context, *ImportComponentConfigurationRequest) (*ImportComponentConfigurationResponse, error) + InvalidateComponentTemplateCache(context.Context, *Empty) (*Empty, error) +} + +// UnimplementedApricotServer should be embedded to have forward compatible implementations. +type UnimplementedApricotServer struct { +} + +func (UnimplementedApricotServer) NewRunNumber(context.Context, *Empty) (*RunNumberResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method NewRunNumber not implemented") +} +func (UnimplementedApricotServer) GetDefaults(context.Context, *Empty) (*StringMap, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetDefaults not implemented") +} +func (UnimplementedApricotServer) GetVars(context.Context, *Empty) (*StringMap, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetVars not implemented") +} +func (UnimplementedApricotServer) RawGetRecursive(context.Context, *RawGetRecursiveRequest) (*ComponentResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method RawGetRecursive not implemented") +} +func (UnimplementedApricotServer) ListDetectors(context.Context, *DetectorsRequest) (*DetectorsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ListDetectors not implemented") +} +func (UnimplementedApricotServer) GetHostInventory(context.Context, *HostGetRequest) (*HostEntriesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetHostInventory not implemented") +} +func (UnimplementedApricotServer) GetDetectorsInventory(context.Context, *Empty) (*DetectorEntriesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetDetectorsInventory not implemented") +} +func (UnimplementedApricotServer) GetDetectorForHost(context.Context, *HostRequest) (*DetectorResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetDetectorForHost not implemented") +} +func (UnimplementedApricotServer) GetDetectorsForHosts(context.Context, *HostsRequest) (*DetectorsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetDetectorsForHosts not implemented") +} +func (UnimplementedApricotServer) GetCRUCardsForHost(context.Context, *HostRequest) (*CRUCardsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetCRUCardsForHost not implemented") +} +func (UnimplementedApricotServer) GetEndpointsForCRUCard(context.Context, *CardRequest) (*CRUCardEndpointResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetEndpointsForCRUCard not implemented") +} +func (UnimplementedApricotServer) GetLinkIDsForCRUEndpoint(context.Context, *LinkIDsRequest) (*LinkIDsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetLinkIDsForCRUEndpoint not implemented") +} +func (UnimplementedApricotServer) GetAliasedLinkIDsForDetector(context.Context, *AliasedLinkIDsRequest) (*AliasedLinkIDsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetAliasedLinkIDsForDetector not implemented") +} +func (UnimplementedApricotServer) GetRuntimeEntry(context.Context, *GetRuntimeEntryRequest) (*ComponentResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetRuntimeEntry not implemented") +} +func (UnimplementedApricotServer) SetRuntimeEntry(context.Context, *SetRuntimeEntryRequest) (*Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method SetRuntimeEntry not implemented") +} +func (UnimplementedApricotServer) GetRuntimeEntries(context.Context, *GetRuntimeEntriesRequest) (*StringMap, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetRuntimeEntries not implemented") +} +func (UnimplementedApricotServer) ListRuntimeEntries(context.Context, *ListRuntimeEntriesRequest) (*ComponentEntriesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ListRuntimeEntries not implemented") +} +func (UnimplementedApricotServer) ListComponents(context.Context, *Empty) (*ComponentEntriesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ListComponents not implemented") +} +func (UnimplementedApricotServer) ListComponentEntries(context.Context, *ListComponentEntriesRequest) (*ComponentEntriesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ListComponentEntries not implemented") +} +func (UnimplementedApricotServer) GetComponentConfiguration(context.Context, *ComponentRequest) (*ComponentResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetComponentConfiguration not implemented") +} +func (UnimplementedApricotServer) GetComponentConfigurationWithLastIndex(context.Context, *ComponentRequest) (*ComponentResponseWithLastIndex, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetComponentConfigurationWithLastIndex not implemented") +} +func (UnimplementedApricotServer) ResolveComponentQuery(context.Context, *ComponentQuery) (*ComponentQuery, error) { + return nil, status.Errorf(codes.Unimplemented, "method ResolveComponentQuery not implemented") +} +func (UnimplementedApricotServer) ImportComponentConfiguration(context.Context, *ImportComponentConfigurationRequest) (*ImportComponentConfigurationResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ImportComponentConfiguration not implemented") +} +func (UnimplementedApricotServer) InvalidateComponentTemplateCache(context.Context, *Empty) (*Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method InvalidateComponentTemplateCache not implemented") +} + +// UnsafeApricotServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to ApricotServer will +// result in compilation errors. +type UnsafeApricotServer interface { + mustEmbedUnimplementedApricotServer() +} + +func RegisterApricotServer(s grpc.ServiceRegistrar, srv ApricotServer) { + s.RegisterService(&Apricot_ServiceDesc, srv) +} + +func _Apricot_NewRunNumber_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(Empty) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ApricotServer).NewRunNumber(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Apricot_NewRunNumber_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ApricotServer).NewRunNumber(ctx, req.(*Empty)) + } + return interceptor(ctx, in, info, handler) +} + +func _Apricot_GetDefaults_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(Empty) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ApricotServer).GetDefaults(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Apricot_GetDefaults_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ApricotServer).GetDefaults(ctx, req.(*Empty)) + } + return interceptor(ctx, in, info, handler) +} + +func _Apricot_GetVars_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(Empty) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ApricotServer).GetVars(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Apricot_GetVars_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ApricotServer).GetVars(ctx, req.(*Empty)) + } + return interceptor(ctx, in, info, handler) +} + +func _Apricot_RawGetRecursive_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RawGetRecursiveRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ApricotServer).RawGetRecursive(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Apricot_RawGetRecursive_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ApricotServer).RawGetRecursive(ctx, req.(*RawGetRecursiveRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Apricot_ListDetectors_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DetectorsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ApricotServer).ListDetectors(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Apricot_ListDetectors_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ApricotServer).ListDetectors(ctx, req.(*DetectorsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Apricot_GetHostInventory_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(HostGetRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ApricotServer).GetHostInventory(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Apricot_GetHostInventory_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ApricotServer).GetHostInventory(ctx, req.(*HostGetRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Apricot_GetDetectorsInventory_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(Empty) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ApricotServer).GetDetectorsInventory(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Apricot_GetDetectorsInventory_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ApricotServer).GetDetectorsInventory(ctx, req.(*Empty)) + } + return interceptor(ctx, in, info, handler) +} + +func _Apricot_GetDetectorForHost_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(HostRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ApricotServer).GetDetectorForHost(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Apricot_GetDetectorForHost_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ApricotServer).GetDetectorForHost(ctx, req.(*HostRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Apricot_GetDetectorsForHosts_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(HostsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ApricotServer).GetDetectorsForHosts(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Apricot_GetDetectorsForHosts_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ApricotServer).GetDetectorsForHosts(ctx, req.(*HostsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Apricot_GetCRUCardsForHost_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(HostRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ApricotServer).GetCRUCardsForHost(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Apricot_GetCRUCardsForHost_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ApricotServer).GetCRUCardsForHost(ctx, req.(*HostRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Apricot_GetEndpointsForCRUCard_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CardRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ApricotServer).GetEndpointsForCRUCard(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Apricot_GetEndpointsForCRUCard_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ApricotServer).GetEndpointsForCRUCard(ctx, req.(*CardRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Apricot_GetLinkIDsForCRUEndpoint_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(LinkIDsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ApricotServer).GetLinkIDsForCRUEndpoint(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Apricot_GetLinkIDsForCRUEndpoint_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ApricotServer).GetLinkIDsForCRUEndpoint(ctx, req.(*LinkIDsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Apricot_GetAliasedLinkIDsForDetector_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(AliasedLinkIDsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ApricotServer).GetAliasedLinkIDsForDetector(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Apricot_GetAliasedLinkIDsForDetector_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ApricotServer).GetAliasedLinkIDsForDetector(ctx, req.(*AliasedLinkIDsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Apricot_GetRuntimeEntry_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetRuntimeEntryRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ApricotServer).GetRuntimeEntry(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Apricot_GetRuntimeEntry_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ApricotServer).GetRuntimeEntry(ctx, req.(*GetRuntimeEntryRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Apricot_SetRuntimeEntry_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SetRuntimeEntryRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ApricotServer).SetRuntimeEntry(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Apricot_SetRuntimeEntry_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ApricotServer).SetRuntimeEntry(ctx, req.(*SetRuntimeEntryRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Apricot_GetRuntimeEntries_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetRuntimeEntriesRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ApricotServer).GetRuntimeEntries(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Apricot_GetRuntimeEntries_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ApricotServer).GetRuntimeEntries(ctx, req.(*GetRuntimeEntriesRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Apricot_ListRuntimeEntries_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListRuntimeEntriesRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ApricotServer).ListRuntimeEntries(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Apricot_ListRuntimeEntries_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ApricotServer).ListRuntimeEntries(ctx, req.(*ListRuntimeEntriesRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Apricot_ListComponents_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(Empty) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ApricotServer).ListComponents(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Apricot_ListComponents_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ApricotServer).ListComponents(ctx, req.(*Empty)) + } + return interceptor(ctx, in, info, handler) +} + +func _Apricot_ListComponentEntries_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListComponentEntriesRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ApricotServer).ListComponentEntries(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Apricot_ListComponentEntries_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ApricotServer).ListComponentEntries(ctx, req.(*ListComponentEntriesRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Apricot_GetComponentConfiguration_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ComponentRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ApricotServer).GetComponentConfiguration(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Apricot_GetComponentConfiguration_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ApricotServer).GetComponentConfiguration(ctx, req.(*ComponentRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Apricot_GetComponentConfigurationWithLastIndex_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ComponentRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ApricotServer).GetComponentConfigurationWithLastIndex(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Apricot_GetComponentConfigurationWithLastIndex_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ApricotServer).GetComponentConfigurationWithLastIndex(ctx, req.(*ComponentRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Apricot_ResolveComponentQuery_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ComponentQuery) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ApricotServer).ResolveComponentQuery(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Apricot_ResolveComponentQuery_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ApricotServer).ResolveComponentQuery(ctx, req.(*ComponentQuery)) + } + return interceptor(ctx, in, info, handler) +} + +func _Apricot_ImportComponentConfiguration_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ImportComponentConfigurationRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ApricotServer).ImportComponentConfiguration(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Apricot_ImportComponentConfiguration_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ApricotServer).ImportComponentConfiguration(ctx, req.(*ImportComponentConfigurationRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Apricot_InvalidateComponentTemplateCache_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(Empty) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ApricotServer).InvalidateComponentTemplateCache(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Apricot_InvalidateComponentTemplateCache_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ApricotServer).InvalidateComponentTemplateCache(ctx, req.(*Empty)) + } + return interceptor(ctx, in, info, handler) +} + +// Apricot_ServiceDesc is the grpc.ServiceDesc for Apricot service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var Apricot_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "apricot.Apricot", + HandlerType: (*ApricotServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "NewRunNumber", + Handler: _Apricot_NewRunNumber_Handler, + }, + { + MethodName: "GetDefaults", + Handler: _Apricot_GetDefaults_Handler, + }, + { + MethodName: "GetVars", + Handler: _Apricot_GetVars_Handler, + }, + { + MethodName: "RawGetRecursive", + Handler: _Apricot_RawGetRecursive_Handler, + }, + { + MethodName: "ListDetectors", + Handler: _Apricot_ListDetectors_Handler, + }, + { + MethodName: "GetHostInventory", + Handler: _Apricot_GetHostInventory_Handler, + }, + { + MethodName: "GetDetectorsInventory", + Handler: _Apricot_GetDetectorsInventory_Handler, + }, + { + MethodName: "GetDetectorForHost", + Handler: _Apricot_GetDetectorForHost_Handler, + }, + { + MethodName: "GetDetectorsForHosts", + Handler: _Apricot_GetDetectorsForHosts_Handler, + }, + { + MethodName: "GetCRUCardsForHost", + Handler: _Apricot_GetCRUCardsForHost_Handler, + }, + { + MethodName: "GetEndpointsForCRUCard", + Handler: _Apricot_GetEndpointsForCRUCard_Handler, + }, + { + MethodName: "GetLinkIDsForCRUEndpoint", + Handler: _Apricot_GetLinkIDsForCRUEndpoint_Handler, + }, + { + MethodName: "GetAliasedLinkIDsForDetector", + Handler: _Apricot_GetAliasedLinkIDsForDetector_Handler, + }, + { + MethodName: "GetRuntimeEntry", + Handler: _Apricot_GetRuntimeEntry_Handler, + }, + { + MethodName: "SetRuntimeEntry", + Handler: _Apricot_SetRuntimeEntry_Handler, + }, + { + MethodName: "GetRuntimeEntries", + Handler: _Apricot_GetRuntimeEntries_Handler, + }, + { + MethodName: "ListRuntimeEntries", + Handler: _Apricot_ListRuntimeEntries_Handler, + }, + { + MethodName: "ListComponents", + Handler: _Apricot_ListComponents_Handler, + }, + { + MethodName: "ListComponentEntries", + Handler: _Apricot_ListComponentEntries_Handler, + }, + { + MethodName: "GetComponentConfiguration", + Handler: _Apricot_GetComponentConfiguration_Handler, + }, + { + MethodName: "GetComponentConfigurationWithLastIndex", + Handler: _Apricot_GetComponentConfigurationWithLastIndex_Handler, + }, + { + MethodName: "ResolveComponentQuery", + Handler: _Apricot_ResolveComponentQuery_Handler, + }, + { + MethodName: "ImportComponentConfiguration", + Handler: _Apricot_ImportComponentConfiguration_Handler, + }, + { + MethodName: "InvalidateComponentTemplateCache", + Handler: _Apricot_InvalidateComponentTemplateCache_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "protos/apricot.proto", +} diff --git a/apricot/remote/client.go b/apricot/remote/client.go new file mode 100644 index 000000000..6ded1cfbb --- /dev/null +++ b/apricot/remote/client.go @@ -0,0 +1,55 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2020-2021 CERN and copyright holders of ALICE O². + * Author: Teo Mrnjavac + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package remote + +import ( + "context" + + apricotpb "github.com/AliceO2Group/Control/apricot/protos" + "google.golang.org/grpc" +) + +type rpcClient struct { + apricotpb.ApricotClient + conn *grpc.ClientConn +} + +func newRpcClient(cxt context.Context, cancel context.CancelFunc, endpoint string) *rpcClient { + conn, err := grpc.DialContext(cxt, endpoint, grpc.WithInsecure(), grpc.WithBlock()) + if err != nil { + log.WithField("error", err.Error()). + WithField("endpoint", endpoint). + Errorf("cannot dial RPC endpoint") + cancel() + return nil + } + + client := &rpcClient{ + ApricotClient: apricotpb.NewApricotClient(conn), + conn: conn, + } + + return client +} diff --git a/apricot/remote/remoteutil.go b/apricot/remote/remoteutil.go new file mode 100644 index 000000000..126bca90d --- /dev/null +++ b/apricot/remote/remoteutil.go @@ -0,0 +1,47 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2018 CERN and copyright holders of ALICE O². + * Author: Claire Guyot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package remote + +import apricotpb "github.com/AliceO2Group/Control/apricot/protos" + +func DetectorInventoryToPbDetectorInventory(inventory map[string][]string) map[string]*apricotpb.DetectorInventoryResponse { + response := make(map[string]*apricotpb.DetectorInventoryResponse) + var inventoryResp *apricotpb.DetectorInventoryResponse + for det, flps := range inventory { + inventoryResp = &apricotpb.DetectorInventoryResponse{ + Flps: flps, + } + response[det] = inventoryResp + } + return response +} + +func PbDetectorInventoryToDetectorInventory(inventory map[string]*apricotpb.DetectorInventoryResponse) map[string][]string { + response := make(map[string][]string) + for det, flps := range inventory { + response[det] = flps.Flps + } + return response +} diff --git a/apricot/remote/server.go b/apricot/remote/server.go new file mode 100644 index 000000000..53baffc91 --- /dev/null +++ b/apricot/remote/server.go @@ -0,0 +1,548 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2020-2021 CERN and copyright holders of ALICE O². + * Author: Teo Mrnjavac + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +// A Processor and ReposItory for COnfiguration Templates +package remote + +import ( + "context" + "encoding/json" + "runtime" + "strings" + + apricotpb "github.com/AliceO2Group/Control/apricot/protos" + "github.com/AliceO2Group/Control/common/logger" + "github.com/AliceO2Group/Control/configuration" + "github.com/AliceO2Group/Control/configuration/componentcfg" + "github.com/sirupsen/logrus" + "github.com/spf13/viper" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/health" + "google.golang.org/grpc/health/grpc_health_v1" + "google.golang.org/grpc/reflection" + "google.golang.org/grpc/status" +) + +var log = logger.New(logrus.StandardLogger(), "apricot") + +var ( + E_OK = status.New(codes.OK, "") + E_CONFIGURATION_BACKEND_UNAVAILABLE = status.Errorf(codes.Internal, "configuration backend unavailable") + E_BAD_INPUT = status.Errorf(codes.InvalidArgument, "bad request received") +) + +type RpcServer struct { + service configuration.Service +} + +func NewServer(service configuration.Service) *grpc.Server { + s := grpc.NewServer() + grpc_health_v1.RegisterHealthServer(s, health.NewServer()) + apricotpb.RegisterApricotServer(s, &RpcServer{ + service: service, + }) + // Register reflection service on gRPC server. + reflection.Register(s) + return s +} + +func (m *RpcServer) NewRunNumber(_ context.Context, _ *apricotpb.Empty) (*apricotpb.RunNumberResponse, error) { + if m == nil || m.service == nil { + return nil, E_CONFIGURATION_BACKEND_UNAVAILABLE + } + m.logMethod() + rn, err := m.service.NewRunNumber() + return &apricotpb.RunNumberResponse{RunNumber: rn}, err +} + +func (m *RpcServer) GetDefaults(_ context.Context, _ *apricotpb.Empty) (*apricotpb.StringMap, error) { + if m == nil || m.service == nil { + return nil, E_CONFIGURATION_BACKEND_UNAVAILABLE + } + m.logMethod() + varStack := m.service.GetDefaults() + return &apricotpb.StringMap{StringMap: varStack}, E_OK.Err() +} + +func (m *RpcServer) GetVars(_ context.Context, _ *apricotpb.Empty) (*apricotpb.StringMap, error) { + if m == nil || m.service == nil { + return nil, E_CONFIGURATION_BACKEND_UNAVAILABLE + } + m.logMethod() + varStack := m.service.GetVars() + return &apricotpb.StringMap{StringMap: varStack}, E_OK.Err() +} + +func (m *RpcServer) GetComponentConfiguration(_ context.Context, request *apricotpb.ComponentRequest) (*apricotpb.ComponentResponse, error) { + if m == nil || m.service == nil { + return nil, E_CONFIGURATION_BACKEND_UNAVAILABLE + } + m.logMethod() + + if request == nil { + return nil, E_BAD_INPUT + } + + var query *componentcfg.Query + if rawPath := request.GetPath(); len(rawPath) > 0 { + var err error + query, err = componentcfg.NewQuery(rawPath) + if err != nil { + return nil, E_BAD_INPUT + } + } else if reqQuery := request.GetQuery(); reqQuery != nil { + query = &componentcfg.Query{ + Component: reqQuery.Component, + RunType: reqQuery.RunType, + RoleName: reqQuery.MachineRole, + EntryKey: reqQuery.Entry, + } + } else { + return nil, E_BAD_INPUT + } + + var payload string + var err error + if request.ProcessTemplate { + payload, err = m.service.GetAndProcessComponentConfiguration(query, request.GetVarStack()) + } else { + payload, err = m.service.GetComponentConfiguration(query) + } + + if err != nil { + return nil, err + } + return &apricotpb.ComponentResponse{Payload: payload}, E_OK.Err() +} + +func (m *RpcServer) GetComponentConfigurationWithLastIndex(_ context.Context, request *apricotpb.ComponentRequest) (*apricotpb.ComponentResponseWithLastIndex, error) { + if m == nil || m.service == nil { + return nil, E_CONFIGURATION_BACKEND_UNAVAILABLE + } + m.logMethod() + + if request == nil { + return nil, E_BAD_INPUT + } + + var query *componentcfg.Query + if rawPath := request.GetPath(); len(rawPath) > 0 { + var err error + query, err = componentcfg.NewQuery(rawPath) + if err != nil { + return nil, E_BAD_INPUT + } + } else if reqQuery := request.GetQuery(); reqQuery != nil { + query = &componentcfg.Query{ + Component: reqQuery.Component, + RunType: reqQuery.RunType, + RoleName: reqQuery.MachineRole, + EntryKey: reqQuery.Entry, + } + } else { + return nil, E_BAD_INPUT + } + + var payload string + var lastIndex uint64 + var err error + // ProcessTemplates not supported for this response + if request.ProcessTemplate { + return nil, E_BAD_INPUT + } else { + payload, lastIndex, err = m.service.GetComponentConfigurationWithLastIndex(query) + } + + if err != nil { + return nil, err + } + return &apricotpb.ComponentResponseWithLastIndex{Payload: payload, LastIndex: lastIndex}, E_OK.Err() +} + +func (m *RpcServer) ResolveComponentQuery(_ context.Context, request *apricotpb.ComponentQuery) (*apricotpb.ComponentQuery, error) { + if m == nil || m.service == nil { + return nil, E_CONFIGURATION_BACKEND_UNAVAILABLE + } + m.logMethod() + + if request == nil { + return nil, E_BAD_INPUT + } + + query := &componentcfg.Query{ + Component: request.Component, + RunType: request.RunType, + RoleName: request.MachineRole, + EntryKey: request.Entry, + } + + resolved, err := m.service.ResolveComponentQuery(query) + if err != nil { + return nil, err + } + return &apricotpb.ComponentQuery{ + Component: resolved.Component, + RunType: resolved.RunType, + MachineRole: resolved.RoleName, + Entry: resolved.EntryKey, + }, E_OK.Err() +} + +func (m *RpcServer) GetDetectorForHost(_ context.Context, request *apricotpb.HostRequest) (*apricotpb.DetectorResponse, error) { + if m == nil || m.service == nil { + return nil, E_CONFIGURATION_BACKEND_UNAVAILABLE + } + m.logMethod() + if request == nil { + return nil, E_BAD_INPUT + } + + payload, err := m.service.GetDetectorForHost(request.GetHostname()) + if err != nil { + return nil, err + } + return &apricotpb.DetectorResponse{Payload: payload}, E_OK.Err() +} + +func (m *RpcServer) GetDetectorsForHosts(_ context.Context, request *apricotpb.HostsRequest) (*apricotpb.DetectorsResponse, error) { + if m == nil || m.service == nil { + return nil, E_CONFIGURATION_BACKEND_UNAVAILABLE + } + m.logMethod() + if request == nil { + return nil, E_BAD_INPUT + } + + payload, err := m.service.GetDetectorsForHosts(request.GetHosts()) + if err != nil { + return nil, err + } + return &apricotpb.DetectorsResponse{Detectors: payload}, E_OK.Err() +} + +func (m *RpcServer) GetCRUCardsForHost(_ context.Context, request *apricotpb.HostRequest) (*apricotpb.CRUCardsResponse, error) { + if m == nil || m.service == nil { + return nil, E_CONFIGURATION_BACKEND_UNAVAILABLE + } + m.logMethod() + if request == nil { + return nil, E_BAD_INPUT + } + + cards, err := m.service.GetCRUCardsForHost(request.GetHostname()) + if err != nil { + return nil, err + } + cardsJson, err := json.Marshal(cards) + if err != nil { + return nil, err + } + + return &apricotpb.CRUCardsResponse{Cards: string(cardsJson)}, E_OK.Err() +} + +func (m *RpcServer) GetEndpointsForCRUCard(_ context.Context, request *apricotpb.CardRequest) (*apricotpb.CRUCardEndpointResponse, error) { + if m == nil || m.service == nil { + return nil, E_CONFIGURATION_BACKEND_UNAVAILABLE + } + m.logMethod() + if request == nil { + return nil, E_BAD_INPUT + } + + endpoints, err := m.service.GetEndpointsForCRUCard(request.GetHostname(), request.GetCardSerial()) + if err != nil { + return nil, err + } + endpointsSpaceSeparated := strings.Join(endpoints, " ") + + return &apricotpb.CRUCardEndpointResponse{Endpoints: endpointsSpaceSeparated}, E_OK.Err() +} + +func (m *RpcServer) GetLinkIDsForCRUEndpoint(_ context.Context, request *apricotpb.LinkIDsRequest) (*apricotpb.LinkIDsResponse, error) { + if m == nil || m.service == nil { + return nil, E_CONFIGURATION_BACKEND_UNAVAILABLE + } + m.logMethod() + if request == nil { + return nil, E_BAD_INPUT + } + + linkIDs, err := m.service.GetLinkIDsForCRUEndpoint( + request.GetHostname(), + request.GetCardSerial(), + request.GetEndpoint(), + request.GetOnlyEnabled(), + ) + if err != nil { + return nil, err + } + return &apricotpb.LinkIDsResponse{LinkIDs: linkIDs}, E_OK.Err() +} + +func (m *RpcServer) GetAliasedLinkIDsForDetector(_ context.Context, request *apricotpb.AliasedLinkIDsRequest) (*apricotpb.AliasedLinkIDsResponse, error) { + if m == nil || m.service == nil { + return nil, E_CONFIGURATION_BACKEND_UNAVAILABLE + } + m.logMethod() + if request == nil { + return nil, E_BAD_INPUT + } + + aliasedLinkIDs, err := m.service.GetAliasedLinkIDsForDetector( + request.GetDetector(), + request.GetOnlyEnabled(), + ) + if err != nil { + return nil, err + } + return &apricotpb.AliasedLinkIDsResponse{AliasedLinkIDs: aliasedLinkIDs}, E_OK.Err() +} + +func (m *RpcServer) GetRuntimeEntry(_ context.Context, request *apricotpb.GetRuntimeEntryRequest) (*apricotpb.ComponentResponse, error) { + if m == nil || m.service == nil { + return nil, E_CONFIGURATION_BACKEND_UNAVAILABLE + } + m.logMethod() + if request == nil { + return nil, E_BAD_INPUT + } + + payload, err := m.service.GetRuntimeEntry(request.Component, request.Key) + if err != nil { + return nil, err + } + return &apricotpb.ComponentResponse{Payload: payload}, E_OK.Err() +} + +func (m *RpcServer) SetRuntimeEntry(_ context.Context, request *apricotpb.SetRuntimeEntryRequest) (*apricotpb.Empty, error) { + if m == nil || m.service == nil { + return nil, E_CONFIGURATION_BACKEND_UNAVAILABLE + } + m.logMethod() + if request == nil { + return nil, E_BAD_INPUT + } + + err := m.service.SetRuntimeEntry(request.Component, request.Key, request.Value) + if err != nil { + return nil, err + } + return &apricotpb.Empty{}, E_OK.Err() +} + +func (m *RpcServer) GetRuntimeEntries(_ context.Context, request *apricotpb.GetRuntimeEntriesRequest) (*apricotpb.StringMap, error) { + if m == nil || m.service == nil { + return nil, E_CONFIGURATION_BACKEND_UNAVAILABLE + } + m.logMethod() + if request == nil { + return nil, E_BAD_INPUT + } + + entries, err := m.service.GetRuntimeEntries(request.Component) + if err != nil { + return nil, err + } + return &apricotpb.StringMap{StringMap: entries}, E_OK.Err() +} + +func (m *RpcServer) ListRuntimeEntries(_ context.Context, request *apricotpb.ListRuntimeEntriesRequest) (*apricotpb.ComponentEntriesResponse, error) { + if m == nil || m.service == nil { + return nil, E_CONFIGURATION_BACKEND_UNAVAILABLE + } + m.logMethod() + if request == nil { + return nil, E_BAD_INPUT + } + + payload, err := m.service.ListRuntimeEntries(request.Component) + if err != nil { + return nil, err + } + return &apricotpb.ComponentEntriesResponse{Payload: payload}, E_OK.Err() +} + +func (m *RpcServer) RawGetRecursive(_ context.Context, request *apricotpb.RawGetRecursiveRequest) (*apricotpb.ComponentResponse, error) { + if m == nil || m.service == nil { + return nil, E_CONFIGURATION_BACKEND_UNAVAILABLE + } + m.logMethod() + + if request == nil { + return nil, E_BAD_INPUT + } + + payload, err := m.service.RawGetRecursive(request.RawPath) + if err != nil { + return &apricotpb.ComponentResponse{Payload: ""}, err + } + return &apricotpb.ComponentResponse{Payload: payload}, nil +} + +func (m *RpcServer) ListDetectors(_ context.Context, request *apricotpb.DetectorsRequest) (*apricotpb.DetectorsResponse, error) { + if m == nil || m.service == nil { + return nil, E_CONFIGURATION_BACKEND_UNAVAILABLE + } + m.logMethod() + + detectors, err := m.service.ListDetectors(request.GetAll) + if err != nil { + return nil, err + } + return &apricotpb.DetectorsResponse{Detectors: detectors}, nil +} + +func (m *RpcServer) GetHostInventory(_ context.Context, request *apricotpb.HostGetRequest) (*apricotpb.HostEntriesResponse, error) { + if m == nil || m.service == nil { + return nil, E_CONFIGURATION_BACKEND_UNAVAILABLE + } + m.logMethod() + + if request == nil { + return nil, E_BAD_INPUT + } + + entries, err := m.service.GetHostInventory(request.Detector) + if err != nil { + return nil, err + } + return &apricotpb.HostEntriesResponse{Hosts: entries}, nil +} + +func (m *RpcServer) GetDetectorsInventory(_ context.Context, _ *apricotpb.Empty) (*apricotpb.DetectorEntriesResponse, error) { + if m == nil || m.service == nil { + return nil, E_CONFIGURATION_BACKEND_UNAVAILABLE + } + m.logMethod() + + entries, err := m.service.GetDetectorsInventory() + if err != nil { + return nil, err + } + return &apricotpb.DetectorEntriesResponse{DetectorEntries: DetectorInventoryToPbDetectorInventory(entries)}, nil +} + +func (m *RpcServer) ListComponents(_ context.Context, _ *apricotpb.Empty) (*apricotpb.ComponentEntriesResponse, error) { + if m == nil || m.service == nil { + return nil, E_CONFIGURATION_BACKEND_UNAVAILABLE + } + m.logMethod() + entries, err := m.service.ListComponents() + if err != nil { + return nil, err + } + response := &apricotpb.ComponentEntriesResponse{Payload: entries} + return response, nil +} + +func (m *RpcServer) ListComponentEntries(_ context.Context, request *apricotpb.ListComponentEntriesRequest) (*apricotpb.ComponentEntriesResponse, error) { + if m == nil || m.service == nil { + return nil, E_CONFIGURATION_BACKEND_UNAVAILABLE + } + m.logMethod() + + if request == nil { + return nil, E_BAD_INPUT + } + + var query *componentcfg.EntriesQuery + if rawPath := request.GetPath(); len(rawPath) > 0 { + var err error + query, err = componentcfg.NewEntriesQuery(rawPath) + if err != nil { + return nil, E_BAD_INPUT + } + } else if reqQuery := request.GetQuery(); reqQuery != nil { + query = &componentcfg.EntriesQuery{ + Component: reqQuery.Component, + RunType: reqQuery.RunType, + RoleName: reqQuery.MachineRole, + } + } else { + return nil, E_BAD_INPUT + } + + entries, err := m.service.ListComponentEntries(query) + if err != nil { + return nil, err + } + response := &apricotpb.ComponentEntriesResponse{Payload: entries} + return response, nil +} + +func (m *RpcServer) ImportComponentConfiguration(_ context.Context, request *apricotpb.ImportComponentConfigurationRequest) (*apricotpb.ImportComponentConfigurationResponse, error) { + if m == nil || m.service == nil { + return nil, E_CONFIGURATION_BACKEND_UNAVAILABLE + } + m.logMethod() + + if request == nil { + return nil, E_BAD_INPUT + } + + pushQuery := &componentcfg.Query{ + Component: request.Query.Component, + RunType: request.Query.RunType, + RoleName: request.Query.MachineRole, + EntryKey: request.Query.Entry, + } + + existingComponentUpdated, existingEntryUpdated, err := m.service.ImportComponentConfiguration(pushQuery, request.Payload, request.NewComponent) + if err != nil { + return nil, err + } + response := &apricotpb.ImportComponentConfigurationResponse{ + ExistingComponentUpdated: existingComponentUpdated, + ExistingEntryUpdated: existingEntryUpdated, + } + return response, nil +} + +func (m *RpcServer) InvalidateComponentTemplateCache(_ context.Context, _ *apricotpb.Empty) (*apricotpb.Empty, error) { + if m == nil || m.service == nil { + return nil, E_CONFIGURATION_BACKEND_UNAVAILABLE + } + m.logMethod() + m.service.InvalidateComponentTemplateCache() + return &apricotpb.Empty{}, nil +} + +func (m *RpcServer) logMethod() { + if !viper.GetBool("verbose") { + return + } + pc, _, _, ok := runtime.Caller(1) + if !ok { + return + } + fun := runtime.FuncForPC(pc) + if fun == nil { + return + } + log.WithPrefix("apricot"). + WithField("method", fun.Name()). + Trace("handling RPC request") +} diff --git a/apricot/remote/service.go b/apricot/remote/service.go new file mode 100644 index 000000000..a80efa1c6 --- /dev/null +++ b/apricot/remote/service.go @@ -0,0 +1,397 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2020-2021 CERN and copyright holders of ALICE O². + * Author: Teo Mrnjavac + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +// Package remote implements a remote configuration backend for the configuration +// service, accessing configuration handled by a different application via gRPC. +package remote + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "net/url" + "strings" + "time" + + apricotpb "github.com/AliceO2Group/Control/apricot/protos" + "github.com/AliceO2Group/Control/configuration" + "github.com/AliceO2Group/Control/configuration/componentcfg" + "google.golang.org/grpc" +) + +const CALL_TIMEOUT = 10 * time.Second + +type RemoteService struct { + cli rpcClient +} + +func NewService(configUri string) (svc configuration.Service, err error) { + var parsedUri *url.URL + parsedUri, err = url.Parse(configUri) + if err != nil { + return nil, err + } + if parsedUri.Scheme != "apricot" { + return nil, errors.New("remote configuration URI scheme must be apricot://") + } + endpoint := parsedUri.Host + + cxt, cancel := context.WithTimeout(context.Background(), CALL_TIMEOUT) + + rpcClient := newRpcClient(cxt, cancel, endpoint) + if rpcClient == nil { + return nil, fmt.Errorf("cannot dial apricot service at %s", endpoint) + } + return &RemoteService{ + cli: *rpcClient, + }, nil +} + +func (c *RemoteService) NewRunNumber() (runNumber uint32, err error) { + var response *apricotpb.RunNumberResponse + response, err = c.cli.NewRunNumber(context.Background(), &apricotpb.Empty{}, grpc.EmptyCallOption{}) + if err != nil { + return 0, err + } + return response.GetRunNumber(), nil +} + +func (c *RemoteService) GetDefaults() map[string]string { + response, err := c.cli.GetDefaults(context.Background(), &apricotpb.Empty{}, grpc.EmptyCallOption{}) + if err != nil { + return nil + } + return response.GetStringMap() +} + +func (c *RemoteService) GetVars() map[string]string { + response, err := c.cli.GetVars(context.Background(), &apricotpb.Empty{}, grpc.EmptyCallOption{}) + if err != nil { + return nil + } + return response.GetStringMap() +} + +func (c *RemoteService) GetComponentConfiguration(query *componentcfg.Query) (payload string, err error) { + return c.getComponentConfigurationInternal(query, false, nil) +} + +func (c *RemoteService) GetComponentConfigurationWithLastIndex(query *componentcfg.Query) (payload string, lastIndex uint64, err error) { + return c.getComponentConfigurationInternalWithLastIndex(query, false, nil) +} + +func (c *RemoteService) GetAndProcessComponentConfiguration(query *componentcfg.Query, varStack map[string]string) (payload string, err error) { + return c.getComponentConfigurationInternal(query, true, varStack) +} + +func (c *RemoteService) getComponentConfigurationInternal(query *componentcfg.Query, processTemplate bool, varStack map[string]string) (payload string, err error) { + var response *apricotpb.ComponentResponse + componentQuery := &apricotpb.ComponentQuery{ + Component: query.Component, + RunType: query.RunType, + MachineRole: query.RoleName, + Entry: query.EntryKey, + } + request := &apricotpb.ComponentRequest{ + QueryPath: &apricotpb.ComponentRequest_Query{Query: componentQuery}, + ProcessTemplate: processTemplate, + VarStack: varStack, + } + response, err = c.cli.GetComponentConfiguration(context.Background(), request, grpc.EmptyCallOption{}) + if err != nil { + return "", err + } + return response.GetPayload(), nil +} + +func (c *RemoteService) getComponentConfigurationInternalWithLastIndex(query *componentcfg.Query, processTemplate bool, varStack map[string]string) (payload string, lastIndex uint64, err error) { + var response *apricotpb.ComponentResponseWithLastIndex + componentQuery := &apricotpb.ComponentQuery{ + Component: query.Component, + RunType: query.RunType, + MachineRole: query.RoleName, + Entry: query.EntryKey, + } + request := &apricotpb.ComponentRequest{ + QueryPath: &apricotpb.ComponentRequest_Query{Query: componentQuery}, + ProcessTemplate: processTemplate, + VarStack: varStack, + } + response, err = c.cli.GetComponentConfigurationWithLastIndex(context.Background(), request, grpc.EmptyCallOption{}) + if err != nil { + return "", 0, err + } + return response.GetPayload(), response.GetLastIndex(), nil +} + +func (c *RemoteService) ResolveComponentQuery(query *componentcfg.Query) (resolved *componentcfg.Query, err error) { + var response *apricotpb.ComponentQuery + componentQuery := &apricotpb.ComponentQuery{ + Component: query.Component, + RunType: query.RunType, + MachineRole: query.RoleName, + Entry: query.EntryKey, + } + response, err = c.cli.ResolveComponentQuery(context.Background(), componentQuery, grpc.EmptyCallOption{}) + if err != nil { + return nil, err + } + resolved = &componentcfg.Query{ + Component: response.Component, + RunType: response.RunType, + RoleName: response.MachineRole, + EntryKey: response.Entry, + } + return resolved, nil +} + +func (c *RemoteService) RawGetRecursive(path string) (payload string, err error) { + var response *apricotpb.ComponentResponse + request := &apricotpb.RawGetRecursiveRequest{RawPath: path} + response, err = c.cli.RawGetRecursive(context.Background(), request, grpc.EmptyCallOption{}) + if err != nil { + return "", err + } + return response.GetPayload(), nil +} + +func (c *RemoteService) GetDetectorForHost(hostname string) (payload string, err error) { + var response *apricotpb.DetectorResponse + request := &apricotpb.HostRequest{ + Hostname: hostname, + } + response, err = c.cli.GetDetectorForHost(context.Background(), request, grpc.EmptyCallOption{}) + if err != nil { + return "", err + } + return response.GetPayload(), nil + +} + +func (c *RemoteService) GetDetectorsForHosts(hosts []string) (payload []string, err error) { + var response *apricotpb.DetectorsResponse + request := &apricotpb.HostsRequest{ + Hosts: hosts, + } + response, err = c.cli.GetDetectorsForHosts(context.Background(), request, grpc.EmptyCallOption{}) + if err != nil { + return []string{}, err + } + return response.GetDetectors(), nil + +} + +func (c *RemoteService) GetCRUCardsForHost(hostname string) (cards []string, err error) { + var response *apricotpb.CRUCardsResponse + request := &apricotpb.HostRequest{ + Hostname: hostname, + } + response, err = c.cli.GetCRUCardsForHost(context.Background(), request, grpc.EmptyCallOption{}) + if err != nil { + return nil, err + } + cardsStr := response.GetCards() + err = json.Unmarshal([]byte(cardsStr), &cards) + if err != nil { + return nil, err + } + + return cards, nil +} + +func (c *RemoteService) GetEndpointsForCRUCard(hostname, cardSerial string) (endpoints []string, err error) { + var response *apricotpb.CRUCardEndpointResponse + request := &apricotpb.CardRequest{ + Hostname: hostname, + CardSerial: cardSerial, + } + response, err = c.cli.GetEndpointsForCRUCard(context.Background(), request, grpc.EmptyCallOption{}) + if err != nil { + return nil, err + } + endpointsStr := response.GetEndpoints() + endpoints = strings.Split(endpointsStr, " ") + return endpoints, nil +} + +func (c *RemoteService) GetLinkIDsForCRUEndpoint(hostname, cardSerial, endpoint string, onlyEnabled bool) ([]string, error) { + request := &apricotpb.LinkIDsRequest{ + Hostname: hostname, + CardSerial: cardSerial, + Endpoint: endpoint, + } + response, err := c.cli.GetLinkIDsForCRUEndpoint(context.Background(), request, grpc.EmptyCallOption{}) + if err != nil { + return nil, err + } + return response.GetLinkIDs(), nil +} + +func (c *RemoteService) GetAliasedLinkIDsForDetector(detector string, onlyEnabled bool) ([]string, error) { + request := &apricotpb.AliasedLinkIDsRequest{ + Detector: detector, + OnlyEnabled: onlyEnabled, + } + response, err := c.cli.GetAliasedLinkIDsForDetector(context.Background(), request, grpc.EmptyCallOption{}) + if err != nil { + return nil, err + } + return response.GetAliasedLinkIDs(), nil +} + +func (c *RemoteService) GetRuntimeEntry(component string, key string) (payload string, err error) { + var response *apricotpb.ComponentResponse + request := &apricotpb.GetRuntimeEntryRequest{ + Component: component, + Key: key, + } + response, err = c.cli.GetRuntimeEntry(context.Background(), request, grpc.EmptyCallOption{}) + if err != nil { + return "", err + } + return response.GetPayload(), nil +} + +func (c *RemoteService) SetRuntimeEntry(component string, key string, value string) (err error) { + request := &apricotpb.SetRuntimeEntryRequest{ + Component: component, + Key: key, + Value: value, + } + _, err = c.cli.SetRuntimeEntry(context.Background(), request, grpc.EmptyCallOption{}) + if err != nil { + return err + } + return nil +} + +func (c *RemoteService) GetRuntimeEntries(component string) (payload map[string]string, err error) { + request := &apricotpb.GetRuntimeEntriesRequest{ + Component: component, + } + var response *apricotpb.StringMap + response, err = c.cli.GetRuntimeEntries(context.Background(), request, grpc.EmptyCallOption{}) + if err != nil { + return nil, err + } + return response.GetStringMap(), nil +} + +func (c *RemoteService) ListRuntimeEntries(component string) (payload []string, err error) { + request := &apricotpb.ListRuntimeEntriesRequest{ + Component: component, + } + var response *apricotpb.ComponentEntriesResponse + response, err = c.cli.ListRuntimeEntries(context.Background(), request, grpc.EmptyCallOption{}) + if err != nil { + return nil, err + } + return response.GetPayload(), nil +} + +func (c *RemoteService) ListDetectors(getAll bool) (detectors []string, err error) { + var response *apricotpb.DetectorsResponse + response, err = c.cli.ListDetectors(context.Background(), &apricotpb.DetectorsRequest{GetAll: getAll}, grpc.EmptyCallOption{}) + if err != nil { + return nil, err + } + return response.GetDetectors(), nil +} + +func (c *RemoteService) GetHostInventory(detector string) (hosts []string, err error) { + var response *apricotpb.HostEntriesResponse + request := &apricotpb.HostGetRequest{Detector: detector} + response, err = c.cli.GetHostInventory(context.Background(), request, grpc.EmptyCallOption{}) + if err != nil { + return nil, err + } + return response.GetHosts(), nil +} + +func (c *RemoteService) GetDetectorsInventory() (inventory map[string][]string, err error) { + var response *apricotpb.DetectorEntriesResponse + response, err = c.cli.GetDetectorsInventory(context.Background(), &apricotpb.Empty{}, grpc.EmptyCallOption{}) + if err != nil { + return nil, err + } + inventory = PbDetectorInventoryToDetectorInventory(response.GetDetectorEntries()) + return +} + +func (c *RemoteService) ListComponents() (components []string, err error) { + var response *apricotpb.ComponentEntriesResponse + response, err = c.cli.ListComponents(context.Background(), &apricotpb.Empty{}, grpc.EmptyCallOption{}) + if err != nil { + return + } + components = response.GetPayload() + return +} + +func (c *RemoteService) ListComponentEntries(query *componentcfg.EntriesQuery) (entries []string, err error) { + var response *apricotpb.ComponentEntriesResponse + entriesQuery := &apricotpb.ComponentEntriesQuery{ + Component: query.Component, + RunType: query.RunType, + MachineRole: query.RoleName, + } + request := &apricotpb.ListComponentEntriesRequest{ + QueryPath: &apricotpb.ListComponentEntriesRequest_Query{Query: entriesQuery}, + } + + response, err = c.cli.ListComponentEntries(context.Background(), request, grpc.EmptyCallOption{}) + if err != nil { + return + } + entries = response.GetPayload() + return +} + +func (c *RemoteService) ImportComponentConfiguration(query *componentcfg.Query, payload string, newComponent bool) (existingComponentUpdated bool, existingEntryUpdated bool, err error) { + var response *apricotpb.ImportComponentConfigurationResponse + request := &apricotpb.ImportComponentConfigurationRequest{ + Query: &apricotpb.ComponentQuery{ + Component: query.Component, + RunType: query.RunType, + MachineRole: query.RoleName, + Entry: query.EntryKey, + }, + Payload: payload, + NewComponent: newComponent, + } + + response, err = c.cli.ImportComponentConfiguration(context.Background(), request, grpc.EmptyCallOption{}) + if err != nil { + return + } + + existingComponentUpdated = response.ExistingComponentUpdated + existingEntryUpdated = response.ExistingEntryUpdated + return +} + +func (c *RemoteService) InvalidateComponentTemplateCache() { + _, _ = c.cli.InvalidateComponentTemplateCache(context.Background(), &apricotpb.Empty{}, grpc.EmptyCallOption{}) +} diff --git a/apricot/signals.go b/apricot/signals.go new file mode 100644 index 000000000..4c89da385 --- /dev/null +++ b/apricot/signals.go @@ -0,0 +1,71 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2021 CERN and copyright holders of ALICE O². + * Author: Teo Mrnjavac + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package apricot + +import ( + "context" + "net/http" + "os" + "os/signal" + "syscall" + "time" + + "google.golang.org/grpc" +) + +func signals(srv *grpc.Server, httpsvr *http.Server) { + + // Create channel to receive unix signals + signal_chan := make(chan os.Signal, 1) + + //Register channel to receive SIGINT and SIGTERM signals + signal.Notify(signal_chan, + syscall.SIGINT, + syscall.SIGTERM) + + // Goroutine executes a blocking receive for signals + go func() { + s := <-signal_chan + log.WithField("signal", s.String()). + Debug("received signal") + + srv.Stop() + if err := httpsvr.Shutdown(context.Background()); err != nil { + log.Warn("Error while shutting down http server.") + } + + // Mesos calls are async.Sleep for 2s to mark tasks as completed. + time.Sleep(2 * time.Second) + log.WithField("signal", s.String()). + Info("service stopped") + + switch s { + case syscall.SIGINT: + os.Exit(130) // 128+2 + case syscall.SIGTERM: + os.Exit(143) // 128+15 + } + }() +} diff --git a/cmd/coconut/main.go b/cmd/coconut/main.go index 5d37d112f..e3a66aa7b 100644 --- a/cmd/coconut/main.go +++ b/cmd/coconut/main.go @@ -24,18 +24,18 @@ package main -import( +import ( "github.com/AliceO2Group/Control/coconut/cmd" - "github.com/teo/logrus-prefixed-formatter" log "github.com/sirupsen/logrus" + "github.com/teo/logrus-prefixed-formatter" ) func init() { log.SetFormatter(&prefixed.TextFormatter{ - DisableTimestamp:true, - FullTimestamp: true, - SpacePadding: 20, - PrefixPadding: 12, + DisableTimestamp: true, + FullTimestamp: true, + SpacePadding: 20, + PrefixPadding: 12, // Needed for colored stdout/stderr in GoLand, IntelliJ, etc. ForceColors: true, @@ -45,4 +45,4 @@ func init() { func main() { cmd.Execute() -} \ No newline at end of file +} diff --git a/cmd/o2-aliecs-core/main.go b/cmd/o2-aliecs-core/main.go new file mode 100644 index 000000000..1da25fdd6 --- /dev/null +++ b/cmd/o2-aliecs-core/main.go @@ -0,0 +1,110 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2017-2018 CERN and copyright holders of ALICE O². + * Author: Teo Mrnjavac + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package main + +import ( + "os" + + "github.com/AliceO2Group/Control/common/logger/infologger" + "github.com/AliceO2Group/Control/core" + "github.com/AliceO2Group/Control/core/integration" + "github.com/AliceO2Group/Control/core/integration/bookkeeping" + "github.com/AliceO2Group/Control/core/integration/ccdb" + "github.com/AliceO2Group/Control/core/integration/dcs" + "github.com/AliceO2Group/Control/core/integration/ddsched" + "github.com/AliceO2Group/Control/core/integration/kafka" + "github.com/AliceO2Group/Control/core/integration/lhc" + "github.com/AliceO2Group/Control/core/integration/odc" + "github.com/AliceO2Group/Control/core/integration/testplugin" + "github.com/AliceO2Group/Control/core/integration/trg" + log "github.com/sirupsen/logrus" + "github.com/teo/logrus-prefixed-formatter" +) + +func init() { + // TODO: enable/disable switches for plugins + integration.RegisterPlugin( + "bookkeeping", + "bookkeepingBaseUri", + bookkeeping.NewPlugin) + integration.RegisterPlugin( + "ccdb", + "ccdbEndpoint", + ccdb.NewPlugin) + integration.RegisterPlugin( + "dcs", + "dcsServiceEndpoint", + dcs.NewPlugin) + integration.RegisterPlugin( + "ddsched", + "ddSchedulerEndpoint", + ddsched.NewPlugin) + integration.RegisterPlugin( + "kafka", + "kafkaEndpoint", + kafka.NewPlugin) + integration.RegisterPlugin( + "lhc", + "kafkaEndpoints", + lhc.NewPlugin) + integration.RegisterPlugin( + "odc", + "odcEndpoint", + odc.NewPlugin) + integration.RegisterPlugin( + "trg", + "trgServiceEndpoint", + trg.NewPlugin) + integration.RegisterPlugin( + "testplugin", + "testPluginEndpoint", + testplugin.NewPlugin) + + log.SetFormatter(&prefixed.TextFormatter{ + FullTimestamp: true, + SpacePadding: 20, + PrefixPadding: 12, + + // Needed for colored stdout/stderr in GoLand, IntelliJ, etc. + ForceColors: true, + ForceFormatting: true, + }) + log.SetOutput(os.Stdout) +} + +func main() { + if err := core.NewConfig(); err != nil { + log.Fatal(err) + } + + ilHook, err := infologger.NewDirectHook("ECS", "core", nil) + if err == nil { + log.AddHook(ilHook) + } + + if err := core.Run(); err != nil { + log.Fatal(err) + } +} diff --git a/cmd/o2control-executor/main.go b/cmd/o2-aliecs-executor/main.go similarity index 89% rename from cmd/o2control-executor/main.go rename to cmd/o2-aliecs-executor/main.go index 9f76f2d46..5bcda4de5 100644 --- a/cmd/o2control-executor/main.go +++ b/cmd/o2-aliecs-executor/main.go @@ -25,18 +25,19 @@ package main import ( + "fmt" "os" - "github.com/AliceO2Group/Control/common/logger/infologger" - "github.com/mesos/mesos-go/api/v1/lib/executor/config" "github.com/AliceO2Group/Control/common/logger" + "github.com/AliceO2Group/Control/common/logger/infologger" "github.com/AliceO2Group/Control/executor" + "github.com/mesos/mesos-go/api/v1/lib/executor/config" "github.com/sirupsen/logrus" ) func init() { logrus.SetOutput(os.Stdout) - ilHook, err := infologger.NewHook("ECS", "AliECS executor") + ilHook, err := infologger.NewDirectHook("ECS", "executor", logrus.AllLevels) if err == nil { logrus.AddHook(ilHook) } @@ -44,10 +45,10 @@ func init() { var log = logger.New(logrus.StandardLogger(), "executor") - // Entry point, reads configuration from environment variables. func main() { logrus.SetLevel(logrus.DebugLevel) + infologger.Pid = fmt.Sprintf("%d", os.Getpid()) cfg, err := config.FromEnv() if err != nil { @@ -55,5 +56,6 @@ func main() { } log.WithField("configuration", cfg).Debug("configuration loaded") executor.Run(cfg) + log.WithField("executorId", cfg.ExecutorID).Info("executor exiting") os.Exit(0) } diff --git a/cmd/o2control-core/main.go b/cmd/o2-apricot/main.go similarity index 81% rename from cmd/o2control-core/main.go rename to cmd/o2-apricot/main.go index da66e82d2..5bba3f759 100644 --- a/cmd/o2control-core/main.go +++ b/cmd/o2-apricot/main.go @@ -1,7 +1,7 @@ /* * === This file is part of ALICE O² === * - * Copyright 2017-2018 CERN and copyright holders of ALICE O². + * Copyright 2017-2021 CERN and copyright holders of ALICE O². * Author: Teo Mrnjavac * * This program is free software: you can redistribute it and/or modify @@ -27,35 +27,36 @@ package main import ( "os" + "github.com/AliceO2Group/Control/apricot" "github.com/AliceO2Group/Control/common/logger/infologger" - "github.com/AliceO2Group/Control/core" log "github.com/sirupsen/logrus" "github.com/teo/logrus-prefixed-formatter" ) func init() { log.SetFormatter(&prefixed.TextFormatter{ - FullTimestamp: true, - SpacePadding: 20, - PrefixPadding: 12, + FullTimestamp: true, + SpacePadding: 20, + PrefixPadding: 12, // Needed for colored stdout/stderr in GoLand, IntelliJ, etc. ForceColors: true, ForceFormatting: true, }) log.SetOutput(os.Stdout) - ilHook, err := infologger.NewHook("ECS", "AliECS core") - if err == nil { - log.AddHook(ilHook) - } } func main() { - if err := core.NewConfig(); err != nil { + if err := apricot.NewConfig(); err != nil { log.Fatal(err) } - if err := core.Run(); err != nil { + ilHook, err := infologger.NewDirectHook("ECS", "apricot", nil) + if err == nil { + log.AddHook(ilHook) + } + + if err := apricot.Run(); err != nil { log.Fatal(err) } } diff --git a/cmd/peanut/main.go b/cmd/peanut/main.go index 59ce9efc2..95ca39f02 100644 --- a/cmd/peanut/main.go +++ b/cmd/peanut/main.go @@ -25,17 +25,90 @@ package main import ( + "flag" + "fmt" "os" - "strings" "github.com/AliceO2Group/Control/occ/peanut" ) func main() { - cmdArg := os.Args[1:] - cmdString := strings.Trim(strings.Join(cmdArg, " "), " ") + fs := flag.NewFlagSet("peanut", flag.ExitOnError) + addr := fs.String("addr", "", "OCC gRPC address (host:port); if empty, OCC_CONTROL_PORT env var is used in direct mode") + mode := fs.String("mode", "direct", "control mode: direct (default), fmq, or fmq-step") + fs.Usage = func() { + fmt.Fprint(os.Stderr, `peanut — process execution and control utility for OCC / FairMQ processes - if err := peanut.Run(cmdString); err != nil { - panic(err) +TUI mode (interactive, launched when no command is given): + peanut direct mode via OCC_CONTROL_PORT env var + peanut -addr host:port direct mode (OCC protobuf, one button per transition) + peanut -addr host:port -mode fmq fmq batched mode (drives full FairMQ sequence per transition) + peanut -addr host:port -mode fmq-step fmq single-step mode (one button per raw FairMQ event) + +CLI mode (non-interactive, launched when a command is given): + peanut [flags] [args] + +TUI Flags: + -addr string gRPC address (host:port); if empty, uses OCC_CONTROL_PORT env var in direct mode + -mode string direct (default), fmq, or fmq-step + +CLI Flags: + -addr string gRPC address (default "localhost:47100") + -mode string fmq (default) or direct + -timeout duration unary call timeout (default 30s) + -config string path to YAML/JSON file with arguments to push (inline key=val args take precedence) + +CLI Commands: + get-state + Print the current FSM state. + + transition [key=val ...] + High-level OCC transition. In fmq mode drives the full multi-step FairMQ sequence: + STANDBY→CONFIGURED runs INIT DEVICE, COMPLETE INIT, BIND, CONNECT, INIT TASK + CONFIGURED→RUNNING runs RUN + RUNNING→CONFIGURED runs STOP + CONFIGURED→STANDBY runs RESET TASK, RESET DEVICE + In direct mode sends a single OCC protobuf Transition RPC. + key=val pairs are forwarded as ConfigEntry arguments. + + direct-step [key=val ...] + Low-level: send a single raw OCC protobuf Transition RPC regardless of -mode. + Events: CONFIGURE, RESET, START, STOP, RECOVER, EXIT + + fmq-step [key=val ...] + Low-level: send a single raw FairMQ gRPC Transition call regardless of -mode. + FairMQ state/event names that contain spaces must be quoted. + + state-stream + Subscribe to StateStream; print updates until interrupted (ctrl-c to stop). + + event-stream + Subscribe to EventStream; print events until interrupted (ctrl-c to stop). + +Examples: + peanut -addr localhost:47100 get-state + peanut -addr localhost:47100 transition STANDBY CONFIGURED chans.x.0.address=ipc://@foo + peanut -addr localhost:47100 -config args.yaml transition STANDBY CONFIGURED + peanut -addr localhost:47100 fmq-step IDLE "INIT DEVICE" chans.x.0.address=ipc://@foo + peanut -addr localhost:47100 direct-step STANDBY CONFIGURE key=val + peanut -addr localhost:47100 state-stream + peanut -addr localhost:47100 -mode direct transition STANDBY CONFIGURED +`) + } + _ = fs.Parse(os.Args[1:]) + + if fs.NArg() > 0 { + // CLI mode — pass all original args so RunCLI can re-parse its own flags + if err := peanut.RunCLI(os.Args[1:]); err != nil { + fmt.Fprintf(os.Stderr, "peanut: %v\n", err) + os.Exit(1) + } + return + } + + // TUI mode + if err := peanut.Run(peanut.Options{Addr: *addr, Mode: *mode}); err != nil { + fmt.Fprintf(os.Stderr, "peanut: %v\n", err) + os.Exit(1) } } diff --git a/coconut/README.md b/coconut/README.md index f5a6ddb43..086de07f0 100644 --- a/coconut/README.md +++ b/coconut/README.md @@ -1,12 +1,13 @@ -# `coconut` - the O² control and configuration utility +# The O² control and configuration utility overview -The O² **co**ntrol and **con**figuration **ut**ility is a command line program for interacting with the O² -Control core. +The O² **co**ntrol and **con**figuration **ut**ility is a command line program for interacting with the AliECS core. ## Configuration file -`coconut` can be used with no config file, and by default it will look for a running O² Control -core at `127.0.0.1:47102`. +`coconut` can be used with no config file, and by default it will look for a running AliECS +core at `127.0.0.1:32102`. + +On `aliBuild` it is provided by the `coconut` recipe (RPM `alisw-coconut`, `alienv`/`aliswmod enter coconut`). Once built, it is a portable, static executable that can safely be copied to another Linux machine and executed. You can check the local `coconut` configuration with ``` @@ -15,15 +16,17 @@ $ coconut about If no config file is used, the previous command will print out `config: builtin`. -To override this, create a file `~/.config/coconut/settings.yaml` and fill it in along these lines: +To override this, you may create a file `~/.config/coconut/settings.yaml` and fill it as follows: ```yaml --- -endpoint: "127.0.0.1:47102" -config_endpoint: "consul://some-host-with-consul:8500" # or: "file:///path/to/o2control-core/config.yaml" +endpoint: "127.0.0.1:32102" # host:port of AliECS core +config_endpoint: "consul://some-host-with-consul:8500" # or: "file:///path/to/o2control-core/config.yaml", AliECS configuration endpoint log: - #values: panic fatal error warning info debug - level: info + level: info # values: panic fatal error warning info debug +verbose: false # set to true to debug coconut +nospinner: false # set to true if calling coconut from a script +nocolor: false # set to true if calling coconut from a script ``` ## Using `coconut` @@ -37,7 +40,7 @@ At any step, you can type `$ coconut help ` to get information on wh ``` $ coconut help environment list The environment list command shows a list of currently active environments. -This includes O² environments in any state. +This includes environments in any state. Usage: coconut environment list [flags] @@ -49,22 +52,25 @@ Flags: -h, --help help for list Global Flags: - --config string configuration file (default $HOME/.config/coconut/settings.yaml) - --config_endpoint string O² Configuration endpoint as PROTO://HOST:PORT (default "consul://127.0.0.1:8500") - --endpoint string O² Control endpoint as HOST:PORT (default "127.0.0.1:47102") + --config string optional configuration file for coconut (default $HOME/.config/coconut/settings.yaml) + --config_endpoint string configuration endpoint used by AliECS core as PROTO://HOST:PORT (default "consul://127.0.0.1:8500") + --endpoint string AliECS core endpoint as HOST:PORT (default "127.0.0.1:32102") + --nospinner disable animations in output + --nocolor disable colors in output -v, --verbose show verbose output for debug purposes - ``` -Assuming there's a running O² Control core and `coconut` is correctly configured, the following command should -return some details on the O² Control core: +Assuming there's a running AliECS core and `coconut` is correctly configured, the following command should +return some details on the AliECS core: ``` $ coconut info -O² Control core running on 127.0.0.1:47102 -framework id: 1f303909-7beb-4bd2-800d-d71470e211d4-0078 +instance name: AliECS instance +endpoint: 127.0.0.1:32102 +core version: AliECS 0.16.0 revision 977208f +framework id: fde7f033-0aaf-4d02-9f4e-9ee5ee5824e3-0000 environments count: 0 -roles count: 0 +active tasks count: 0 global state: CONNECTED ``` @@ -73,18 +79,32 @@ resource management system (Apache Mesos). No environments and roles running yet ### Creating an environment -If you started the core with the provided `config.yaml`, it should come preloaded with some FairMQ examples. -The main subcommand for dealing with environments is (unsurprisingly) `environment`. Most subcommands have +Assuming AliECS was deployed as O²/FLP Suite, FairMQ examples should be available. +The main subcommand for dealing with environments is `environment`. Most subcommands have shortened variants, so you might as well type `env` or `e`. Let's see what's running. ``` $ coconut env list no environments running ``` -How do we create one? We can always ask `coconut`. +How do we create one? We can always ask `coconut` for a detailed overview. ``` $ coconut help env create -The environment create command requests from O² Control the -creation of a new O² environment. +The environment create command requests from AliECS the +creation of a new environment. + +The operation may or may not be successful depending on available resources and configuration. + +A valid workflow template (sometimes called simply "workflow" for brevity) must be passed to this command via the mandatory workflow-template flag. + +Workflows and tasks are managed with a git based configuration system, so the workflow template may be provided simply by name or with repository and branch/tag/hash constraints. +Examples: + + * `coconut env create -w myworkflow` - loads workflow `myworkflow` from default configuration repository at HEAD of master branch + * `coconut env create -w github.com/AliceO2Group/MyConfRepo/myworkflow` - loads a workflow from a specific git repository, HEAD of master branch + * `coconut env create -w myworkflow@rev` - loads a workflow from default repository, on branch, tag or revision `rev` + * `coconut env create -w github.com/AliceO2Group/MyConfRepo/myworkflow@rev` - loads a workflow from a specific git repository, on branch, tag or revision `rev` + +For more information on the AliECS workflow configuration system, see documentation for the `coconut repository` command. Usage: coconut environment create [flags] @@ -93,22 +113,41 @@ Aliases: create, new, c, n Flags: - -h, --help help for create - -w, --workflow string workflow to be loaded in the new environment -# ... + -e, --extra-vars key1=val1,key2=val2 values passed using key=value CSV or JSON syntax, interpreted as strings key1=val1,key2=val2 or `{"key1": "value1", "key2": "value2"}` + -h, --help help for create + -w, --workflow-template string workflow to be loaded in the new environment + +Global Flags: + --config string optional configuration file for coconut (default $HOME/.config/coconut/settings.yaml) + --config_endpoint string configuration endpoint used by AliECS core as PROTO://HOST:PORT (default "consul://127.0.0.1:8500") + --endpoint string AliECS core endpoint as HOST:PORT (default "127.0.0.1:32102") + --nospinner disable animations in output + --nocolor disable colors in output + -v, --verbose show verbose output for debug purposes ``` Note that if your `coconut` instance is configured correctly to point to the core's configuration (either Consul -or file), you can use the low level `dump` subcommand to list the available workflow templates. +or file), you can use the `coconut template list` command to view the available workflow templates. As usual, see +`coconut help template list` for a detailed explanation of the query syntax. ``` -$ coconut config dump /o2/control/workflows +$ coconut template list +Available templates in loaded configuration: +github.com/AliceO2Group/ControlWorkflows/ +└── [revision] flp-suite-v0.9.0 + ├── odc-shim + ├── readout-qc + ├── readout-stfb-qc + ├── readout-stfb-stfs-odc + ├── readout-stfb-stfs + ├── readout-stfb + └── readout ``` Let's create an environment by loading the workflow template for the FairMQ 1-n-1 example. This will take a few seconds. ``` -$ coconut env create -w fairmq-ex-1-n-1 +$ coconut env create -w fairmq-ex-1-n-1@master new environment created environment id: 8132d249-e1b4-11e8-9f09-a08cfdc880fc state: CONFIGURED @@ -141,7 +180,7 @@ $ coconut role list ### Controlling an environment -Let's start the data flow. If all goes well, `START_ACTIVITY` takes us to `RUNNING`. +Let's start the data flow. If all goes well, `START_ACTIVITY` (or `start`) takes us to `RUNNING`. ``` $ coconut env control 8132d249-e1b4-11e8-9f09-a08cfdc880fc --event START_ACTIVITY @@ -167,11 +206,3 @@ transition complete environment id: 8132d249-e1b4-11e8-9f09-a08cfdc880fc state: CONFIGURED ``` - -As of 11/2018 InfoLogger integration is work in progress, so the best way to check what's up with a specific -task is with the Mesos GUI. -On the DC/OS Vagrant test cluster, this is accessible at [http://m1.dcos/mesos/](). -Pick the correct task by ID, Name, State, etc. and click on *Sandbox* in the rightmost column, and then open -`stderr`. - -Environment teardown is also work in progress, so in the short term `pkill` will have to do. diff --git a/coconut/app/constants.go b/coconut/app/constants.go index 89a56c547..2f8338da9 100644 --- a/coconut/app/constants.go +++ b/coconut/app/constants.go @@ -22,10 +22,12 @@ * Intergovernmental Organization or submit itself to any jurisdiction. */ +// Package app provides application constants and metadata for the coconut +// command line interface and its components. package app const ( NAME = "coconut" PRETTY_SHORTNAME = "coconut" PRETTY_FULLNAME = "O² Control and Configuration Utility" -) \ No newline at end of file +) diff --git a/coconut/client.go b/coconut/client.go index a61d446f0..acfb73196 100644 --- a/coconut/client.go +++ b/coconut/client.go @@ -26,21 +26,28 @@ package coconut import ( - "google.golang.org/grpc" - "github.com/AliceO2Group/Control/common/logger" + "context" + "github.com/AliceO2Group/Control/coconut/protos" + "github.com/AliceO2Group/Control/common/logger" "github.com/sirupsen/logrus" - "context" + "google.golang.org/grpc" ) var log = logger.New(logrus.StandardLogger(), "coconut") -type internalState struct { +const GrpcMaxCallRecvSize = 100 * 1024 * 1024 +type internalState struct { } func NewClient(cxt context.Context, cancel context.CancelFunc, endpoint string) *RpcClient { - conn, err := grpc.DialContext(cxt, endpoint, grpc.WithInsecure()) + conn, err := grpc.DialContext( + cxt, + endpoint, + grpc.WithInsecure(), + grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(GrpcMaxCallRecvSize)), + ) if err != nil { log.WithField("error", err.Error()). WithField("endpoint", endpoint). @@ -51,10 +58,10 @@ func NewClient(cxt context.Context, cancel context.CancelFunc, endpoint string) state := &internalState{} - client := &RpcClient { + client := &RpcClient{ ControlClient: pb.NewControlClient(conn), - state: state, - conn: conn, + state: state, + conn: conn, } return client @@ -62,10 +69,10 @@ func NewClient(cxt context.Context, cancel context.CancelFunc, endpoint string) type RpcClient struct { pb.ControlClient - state *internalState - conn *grpc.ClientConn + state *internalState + conn *grpc.ClientConn } func (m *RpcClient) Close() error { return m.conn.Close() -} \ No newline at end of file +} diff --git a/coconut/cmd/about.go b/coconut/cmd/about.go index 6519b4fc5..02b9f1298 100644 --- a/coconut/cmd/about.go +++ b/coconut/cmd/about.go @@ -25,20 +25,20 @@ package cmd import ( + "fmt" "github.com/AliceO2Group/Control/coconut/app" + "github.com/fatih/color" "github.com/spf13/cobra" - "fmt" "github.com/spf13/viper" - "github.com/fatih/color" "time" ) // aboutCmd represents the about command var aboutCmd = &cobra.Command{ - Use: "about", + Use: "about", Aliases: []string{}, - Short: fmt.Sprintf("about %s", app.NAME), - Long: `The about command shows some basic information on this utility.`, + Short: fmt.Sprintf("about %s", app.NAME), + Long: `The about command shows some basic information on this utility.`, Run: func(*cobra.Command, []string) { color.Set(color.FgHiWhite) fmt.Print(app.PRETTY_SHORTNAME + " *** ") @@ -52,15 +52,20 @@ endpoint: %s config_endpoint: %s `, color.HiGreenString(viper.GetString("version")), - color.HiGreenString(func() string {if len(viper.ConfigFileUsed()) > 0 { return viper.ConfigFileUsed() }; return "builtin"}()), + color.HiGreenString(func() string { + if len(viper.ConfigFileUsed()) > 0 { + return viper.ConfigFileUsed() + } + return "builtin" + }()), color.HiGreenString(viper.GetString("endpoint")), color.HiGreenString(viper.GetString("config_endpoint"))) color.Set(color.FgHiBlue) - fmt.Printf("\nCopyright 2017-%d CERN and the copyright holders of ALICE O².\n" + - "This program is free software: you can redistribute it and/or modify \n" + - "it under the terms of the GNU General Public License as published by \n" + - "the Free Software Foundation, either version 3 of the License, or \n" + + fmt.Printf("\nCopyright 2017-%d CERN and the copyright holders of ALICE O².\n"+ + "This program is free software: you can redistribute it and/or modify \n"+ + "it under the terms of the GNU General Public License as published by \n"+ + "the Free Software Foundation, either version 3 of the License, or \n"+ "(at your option) any later version.\n", time.Now().Year()) color.Unset() diff --git a/coconut/cmd/configuration.go b/coconut/cmd/configuration.go index 39bfde7a3..2f1455323 100644 --- a/coconut/cmd/configuration.go +++ b/coconut/cmd/configuration.go @@ -30,9 +30,9 @@ import ( // configurationCmd represents the configuration command var configurationCmd = &cobra.Command{ - Use: "configuration", + Use: "configuration", Aliases: []string{"config", "conf", "c"}, - Short: "view or modify O² configuration", + Short: "view or modify O² configuration", Long: `The configuration command allows you to perform operations on the O² configuration store.`, } diff --git a/coconut/cmd/configuration_dump.go b/coconut/cmd/configuration_dump.go index 8c7abc943..86d93d5a1 100644 --- a/coconut/cmd/configuration_dump.go +++ b/coconut/cmd/configuration_dump.go @@ -25,20 +25,22 @@ package cmd import ( - "github.com/spf13/cobra" "github.com/AliceO2Group/Control/coconut/configuration" + "github.com/spf13/cobra" ) -// configurationDumpCmd represents the environment list command +// configurationDumpCmd represents the configuration list command var configurationDumpCmd = &cobra.Command{ - Use: "dump [key]", + Use: "dump [key]", Aliases: []string{"d"}, - Short: "dump configuration subtree", + Short: "dump configuration subtree", Long: `The configuration dump command requests from O² Configuration a subtree of key-values, and dumps it to standard output in the specified -format.`, - Run: configuration.WrapCall(configuration.Dump), - Args: cobra.ExactArgs(1), +format. This command has full read access to the O² Configuration store +and performs a raw query with no additional processing or access control +semantics.`, + Run: configuration.WrapCall(configuration.Dump), + Args: cobra.ExactArgs(1), } func init() { diff --git a/coconut/cmd/configuration_import.go b/coconut/cmd/configuration_import.go new file mode 100644 index 000000000..3f9c49250 --- /dev/null +++ b/coconut/cmd/configuration_import.go @@ -0,0 +1,58 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2020 CERN and copyright holders of ALICE O². + * Author: George Raduta + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package cmd + +import ( + "github.com/AliceO2Group/Control/coconut/configuration" + "github.com/spf13/cobra" +) + +var configurationImportCmd = &cobra.Command{ + Use: "import ", + Aliases: []string{"i", "imp"}, + Example: `coconut conf import +coconut conf import /// +coconut conf import --new-component +coconut conf import /// --format=json +coconut conf import .json +coconut conf import +coconut conf import --new-component +`, + Short: "Import a configuration file for the specified component and entry", + Long: `The configuration import command generates a timestamp and saves +the configuration file to Consul under the / path. +Supported configuration file types are JSON, YAML, TOML and INI, +and their file extensions are recognized automatically.`, + Run: configuration.WrapCall(configuration.Import), + Args: cobra.RangeArgs(2, 3), +} + +func init() { + configurationCmd.AddCommand(configurationImportCmd) + configurationImportCmd.Flags().BoolP("new-component", "n", false, "create a new configuration component while importing entry") + configurationImportCmd.Flags().StringP("format", "f", "", "force a specific configuration file type, overriding any file extension") + configurationImportCmd.Flags().StringP("runtype", "r", "", "request configuration for this run type (e.g. PHYSICS, TECHNICAL, etc.)") + configurationImportCmd.Flags().StringP("role", "l", "", "request configuration for this O² machine role") +} diff --git a/coconut/cmd/configuration_list.go b/coconut/cmd/configuration_list.go new file mode 100644 index 000000000..0a116da4f --- /dev/null +++ b/coconut/cmd/configuration_list.go @@ -0,0 +1,49 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2019 CERN and copyright holders of ALICE O². + * Author: George Raduta + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package cmd + +import ( + "github.com/AliceO2Group/Control/coconut/configuration" + "github.com/spf13/cobra" +) + +var configurationListCmd = &cobra.Command{ + Use: "list [component]", + Aliases: []string{"l", "ls"}, + Example: `coconut conf list +coconut conf list +coconut conf list -t`, + Short: "List all existing O² components in Consul", + Long: `The configuration list command requests all components +from O² Configuration as a list and displays it on the standard output`, + Run: configuration.WrapCall(configuration.List), + Args: cobra.MaximumNArgs(1), +} + +func init() { + configurationCmd.AddCommand(configurationListCmd) + configurationListCmd.Flags().StringP("output", "o", "yaml", "output format for the configuration list (yaml/json)") + configurationListCmd.Flags().BoolP("timestamp", "t", false, "display latest timestamp entries for the requested component") +} diff --git a/coconut/cmd/configuration_show.go b/coconut/cmd/configuration_show.go new file mode 100644 index 000000000..712d55ba2 --- /dev/null +++ b/coconut/cmd/configuration_show.go @@ -0,0 +1,58 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2019 CERN and copyright holders of ALICE O². + * Author: George Raduta + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package cmd + +import ( + "github.com/AliceO2Group/Control/coconut/configuration" + "github.com/spf13/cobra" +) + +var configurationShowCmd = &cobra.Command{ + Use: "show ", + Aliases: []string{"s"}, + Example: `coconut conf show +coconut conf show -t +coconut conf show /// +coconut conf show /// -t +coconut conf show ///@ +coconut conf show -r -l -t ' +coconut conf show -s -e '{"key1": "value1", "key2": "value2"}'`, + Short: "Show configuration for the component and entry specified", + Long: `The configuration show command returns the most recent +configuration revision for the specified component and entry. +It can also return a specific revision, requested with the --timestamp/-t flag`, + Run: configuration.WrapCall(configuration.Show), + Args: cobra.RangeArgs(0, 3), +} + +func init() { + configurationCmd.AddCommand(configurationShowCmd) + configurationShowCmd.Flags().StringP("timestamp", "t", "", "request configuration for this timestamp") + configurationShowCmd.Flags().StringP("runtype", "r", "", "request configuration for this run type (e.g. PHYSICS, TECHNICAL, etc.)") + configurationShowCmd.Flags().StringP("role", "l", "", "request configuration for this O² machine role") + configurationShowCmd.Flags().BoolP("simulate", "s", false, "simulate runtime template processing on the configuration payload") + // The following only applies if simulate is set: + configurationShowCmd.Flags().StringP("extra-vars", "e", "", "values passed using key=value CSV or JSON syntax, interpreted as strings `key1=val1,key2=val2` or `{\"key1\": \"value1\", \"key2\": \"value2\"}`") +} diff --git a/coconut/cmd/environment.go b/coconut/cmd/environment.go index ef523abd4..125e947b8 100644 --- a/coconut/cmd/environment.go +++ b/coconut/cmd/environment.go @@ -22,8 +22,10 @@ * Intergovernmental Organization or submit itself to any jurisdiction. */ -//go:generate protoc -I ../../core --gofast_out=plugins=grpc:.. protos/o2control.proto +//go:generate protoc -I=../../core -I=../../common --go_out=.. --go_opt=paths=source_relative --go-grpc_opt=paths=source_relative --go-grpc_out=require_unimplemented_servers=false:.. protos/o2control.proto +// Package cmd implements the command line interface for coconut, providing +// various subcommands for managing O² Control environments and configurations. package cmd import ( @@ -35,9 +37,9 @@ import ( // environmentCmd represents the environment command var environmentCmd = &cobra.Command{ - Use: "environment", + Use: "environment", Aliases: []string{"env", "e"}, - Short: fmt.Sprintf("create, destroy and manage %s environments", product.PRETTY_SHORTNAME), + Short: fmt.Sprintf("create, destroy and manage %s environments", product.PRETTY_SHORTNAME), Long: `The environments command allows you to perform operations on environments. An environment is an instance of a data-driven workflow of tasks, along with its workflow configuration, task configuration and state. diff --git a/coconut/cmd/environment_control.go b/coconut/cmd/environment_control.go index 94878d5d4..8b08c2a7f 100644 --- a/coconut/cmd/environment_control.go +++ b/coconut/cmd/environment_control.go @@ -25,15 +25,15 @@ package cmd import ( - "github.com/spf13/cobra" "github.com/AliceO2Group/Control/coconut/control" + "github.com/spf13/cobra" ) // environmentControlCmd represents the environment list command var environmentControlCmd = &cobra.Command{ - Use: "control [environment id]", + Use: "control [environment id]", Aliases: []string{"ctl", "ct", "t"}, - Short: "control the state machine of an environment", + Short: "control the state machine of an environment", Long: `The environment control command triggers an event in the state machine of an existing O² environment. The event, if valid, starts a transition. The reached state is returned. @@ -44,8 +44,8 @@ Valid events: START_ACTIVITY STOP_ACTIVITY Not all events are available in all states.`, - Run: control.WrapCall(control.ControlEnvironment), - Args: cobra.ExactArgs(1), + Run: control.WrapCall(control.ControlEnvironment), + Args: cobra.ExactArgs(1), } func init() { diff --git a/coconut/cmd/environment_create.go b/coconut/cmd/environment_create.go index a250fa2ae..5cdce95bf 100644 --- a/coconut/cmd/environment_create.go +++ b/coconut/cmd/environment_create.go @@ -27,16 +27,16 @@ package cmd import ( "fmt" + "github.com/AliceO2Group/Control/coconut/control" "github.com/AliceO2Group/Control/common/product" "github.com/spf13/cobra" - "github.com/AliceO2Group/Control/coconut/control" ) // environmentCreateCmd represents the environment list command var environmentCreateCmd = &cobra.Command{ - Use: "create", + Use: "create", Aliases: []string{"new", "c", "n"}, - Short: "create a new environment", + Short: "create a new environment", Long: fmt.Sprintf(`The environment create command requests from %s the creation of a new environment. @@ -46,19 +46,27 @@ A valid workflow template (sometimes called simply "workflow" for brevity) must Workflows and tasks are managed with a git based configuration system, so the workflow template may be provided simply by name or with repository and branch/tag/hash constraints. Examples: - * ` + "`coconut env create -w myworkflow`" + ` - loads workflow ` + "`myworkflow`" + ` from default configuration repository at HEAD of master branch - * ` + "`coconut env create -w github.com/AliceO2Group/MyConfRepo/myworkflow`" + ` - loads a workflow from a specific git repository, HEAD of master branch - * ` + "`myworkflow@rev`" + ` - loads a workflow from default repository, on branch, tag or revision ` + "`rev`" + ` - * ` + "`coconut env create -w github.com/AliceO2Group/MyConfRepo/myworkflow@rev`" + ` - loads a workflow from a specific git repository, on branch, tag or revision ` + "`rev`" + ` - -For more information on the %s workflow configuration system, see documentation for the ` + "`coconut repository`" + ` command.`, product.PRETTY_SHORTNAME, product.PRETTY_SHORTNAME), - Run: control.WrapCall(control.CreateEnvironment), + * `+"`coconut env create -w myworkflow`"+` - loads workflow `+"`myworkflow`"+` from default configuration repository at HEAD of master branch + * `+"`coconut env create -w github.com/AliceO2Group/MyConfRepo/myworkflow`"+` - loads a workflow from a specific git repository, HEAD of master branch + * `+"`coconut env create -w myworkflow@rev`"+` - loads a workflow from default repository, on branch, tag or revision `+"`rev`"+` + * `+"`coconut env create -w github.com/AliceO2Group/MyConfRepo/myworkflow@rev`"+` - loads a workflow from a specific git repository, on branch, tag or revision `+"`rev`"+` + * `+"`coconut env create -c /home/myrepo/myconfigfile.json -e '{\"hosts\":\"[\\\"my-test-machine\\\"]\"}'`"+` + * `+"`coconut env create -c consul:///o2/runtime/COG-v1/TECHNICAL -w readout-dataflow@myBranch -e '{\"hosts\":\"[\\\"my-test-machine\\\"]\"}'`"+` + * `+"`coconut env create -c TECHNICAL -e '{\"hosts\":\"[\\\"my-test-machine\\\"]\"}'`"+` +For more information on the %s workflow configuration system, see documentation for the `+"`coconut repository`"+` command.`, product.PRETTY_SHORTNAME, product.PRETTY_SHORTNAME), + Run: control.WrapCall(control.CreateEnvironment), } func init() { environmentCmd.AddCommand(environmentCreateCmd) + environmentCreateCmd.Flags().StringP("configuration", "c", "", "high-level configuration payload to be loaded for the new environment") environmentCreateCmd.Flags().StringP("workflow-template", "w", "", "workflow to be loaded in the new environment") - environmentCreateCmd.MarkFlagRequired("workflow-template") + + environmentCreateCmd.Flags().BoolP("auto", "a", false, "create an autorun environment") + environmentCreateCmd.Flags().BoolP("public", "p", true, "control public rights of the environment") + environmentCreateCmd.Flags().BoolP("asynchronous", "y", false, "use asynchronous mode for environment creation") + + environmentCreateCmd.Flags().StringP("extra-vars", "e", "", "values passed using key=value CSV or JSON syntax, interpreted as strings `key1=val1,key2=val2` or `{\"key1\": \"value1\", \"key2\": \"value2\"}`") } diff --git a/coconut/cmd/environment_destroy.go b/coconut/cmd/environment_destroy.go index 00cf955fe..ff06c4f78 100644 --- a/coconut/cmd/environment_destroy.go +++ b/coconut/cmd/environment_destroy.go @@ -27,27 +27,29 @@ package cmd import ( "fmt" + "github.com/AliceO2Group/Control/coconut/control" "github.com/AliceO2Group/Control/common/product" "github.com/spf13/cobra" - "github.com/AliceO2Group/Control/coconut/control" ) // environmentDestroyCmd represents the environment list command var environmentDestroyCmd = &cobra.Command{ - Use: "destroy [environment id]", + Use: "destroy [environment id]", Aliases: []string{"des", "d"}, - Short: "destroy an environment", + Short: "destroy an environment", Long: fmt.Sprintf(`The environment destroy command instructs %s to teardown an existing O² environment. The environment must be in the CONFIGURED or STANDBY state. By default, all active tasks are killed unless the keep-tasks flag is passed, in which case all tasks are left idle.`, product.PRETTY_SHORTNAME), - Run: control.WrapCall(control.DestroyEnvironment), - Args: cobra.ExactArgs(1), + Run: control.WrapCall(control.DestroyEnvironment), + Args: cobra.ExactArgs(1), } func init() { environmentCmd.AddCommand(environmentDestroyCmd) environmentDestroyCmd.Flags().BoolP("keep-tasks", "k", false, "keep tasks active after destroying the environment") + environmentDestroyCmd.Flags().BoolP("allow-in-running-state", "a", false, "allows destroying an environment while in Running state") + environmentDestroyCmd.Flags().BoolP("force", "f", false, "force destroy of an environment") } diff --git a/coconut/cmd/environment_list.go b/coconut/cmd/environment_list.go index 477f81589..8f8c179e5 100644 --- a/coconut/cmd/environment_list.go +++ b/coconut/cmd/environment_list.go @@ -25,20 +25,21 @@ package cmd import ( - "github.com/spf13/cobra" "github.com/AliceO2Group/Control/coconut/control" + "github.com/spf13/cobra" ) // environmentListCmd represents the environment list command var environmentListCmd = &cobra.Command{ - Use: "list", + Use: "list", Aliases: []string{"ls", "l"}, - Short: "list environments", + Short: "list environments", Long: `The environment list command shows a list of currently active environments. This includes environments in any state.`, - Run: control.WrapCall(control.GetEnvironments), + Run: control.WrapCall(control.GetEnvironments), } func init() { environmentCmd.AddCommand(environmentListCmd) + environmentListCmd.Flags().BoolP("show-all", "s", false, "print public and non-public environments") } diff --git a/coconut/cmd/environment_modify.go b/coconut/cmd/environment_modify.go index c05e64952..89dc2173e 100644 --- a/coconut/cmd/environment_modify.go +++ b/coconut/cmd/environment_modify.go @@ -25,19 +25,19 @@ package cmd import ( - "github.com/spf13/cobra" "github.com/AliceO2Group/Control/coconut/control" + "github.com/spf13/cobra" ) // environmentModifyCmd represents the environment list command var environmentModifyCmd = &cobra.Command{ - Use: "modify [environment id]", + Use: "modify [environment id]", Aliases: []string{"mod", "m"}, - Short: "modify an environment", + Short: "modify an environment", Long: `The environment modify command changes the roles workflow of an existing O² environment.`, - Run: control.WrapCall(control.ModifyEnvironment), - Args: cobra.ExactArgs(1), + Run: control.WrapCall(control.ModifyEnvironment), + Args: cobra.ExactArgs(1), } func init() { diff --git a/coconut/cmd/environment_show.go b/coconut/cmd/environment_show.go index c9fff5b6b..d1b190781 100644 --- a/coconut/cmd/environment_show.go +++ b/coconut/cmd/environment_show.go @@ -27,20 +27,20 @@ package cmd import ( "fmt" + "github.com/AliceO2Group/Control/coconut/control" "github.com/AliceO2Group/Control/common/product" "github.com/spf13/cobra" - "github.com/AliceO2Group/Control/coconut/control" ) // environmentShowCmd represents the environment list command var environmentShowCmd = &cobra.Command{ - Use: "show [environment id]", + Use: "show [environment id]", Aliases: []string{"get", "s", "g"}, - Short: "show environment information", + Short: "show environment information", Long: fmt.Sprintf(`The environment show command requests from %s the details of an existing environment.`, product.PRETTY_SHORTNAME), - Run: control.WrapCall(control.ShowEnvironment), - Args: cobra.ExactArgs(1), + Run: control.WrapCall(control.ShowEnvironment), + Args: cobra.ExactArgs(1), } func init() { @@ -48,4 +48,5 @@ func init() { environmentShowCmd.Flags().BoolP("tasks", "t", false, "print a list of tasks in this environment") environmentShowCmd.Flags().BoolP("workflow", "w", false, "print the workflow tree") + environmentShowCmd.Flags().BoolP("services", "s", false, "print information on this environment from integrated services") } diff --git a/coconut/cmd/info.go b/coconut/cmd/info.go index 86d254c1a..3a06fddbb 100644 --- a/coconut/cmd/info.go +++ b/coconut/cmd/info.go @@ -27,19 +27,19 @@ package cmd import ( "fmt" + "github.com/AliceO2Group/Control/coconut/control" "github.com/AliceO2Group/Control/common/product" "github.com/spf13/cobra" - "github.com/AliceO2Group/Control/coconut/control" ) // infoCmd represents the info command var infoCmd = &cobra.Command{ - Use: "info", + Use: "info", Aliases: []string{}, - Short: fmt.Sprintf("get information on the %s core instance", product.PRETTY_SHORTNAME), + Short: fmt.Sprintf("get information on the %s core instance", product.PRETTY_SHORTNAME), Long: fmt.Sprintf(`The info command queries the running instance of %s core and displays some general information.`, product.PRETTY_SHORTNAME), - Run: control.WrapCall(control.GetInfo), + Run: control.WrapCall(control.GetInfo), } func init() { diff --git a/coconut/cmd/repo.go b/coconut/cmd/repo.go index e704688c9..fc79990ca 100644 --- a/coconut/cmd/repo.go +++ b/coconut/cmd/repo.go @@ -22,8 +22,6 @@ * Intergovernmental Organization or submit itself to any jurisdiction. */ -//go:generate protoc -I ../../core --gofast_out=plugins=grpc:.. protos/o2control.proto - package cmd import ( @@ -32,32 +30,39 @@ import ( // repoCmd represents the repository command var repoCmd = &cobra.Command{ - Use: "repository", + Use: "repository", Aliases: []string{"repo"}, - Short: "manage git repositories for task and workflow configuration", + Short: "manage git repositories for task and workflow configuration", Long: `The repository command performs operations on the repositories used for task and workflow configuration. A valid workflow configuration repository must contain the directories ` + "`tasks`" + ` and ` + "`workflows`" + ` in its ` + "`master`" + ` branch. -When referencing a repository, the clone method should never be prepended. Examples of valid repository identifiers: +When referencing a repository, the clone method should never be prepended. Supported repo backends and their expected format are: +- https: [hostname]/[repo_path] +- ssh: [hostname]:[repo_path] +- local [repo_path] (local repo entries are ephemeral and will not survive a core restart) + +Examples of valid repository identifiers: ` + "```" + ` -github.com/AliceO2Group/ControlWorkflows -gitlab.cern.ch/tmrnjava/AliECS_conf/ +github.com/AliceO2Group/ControlWorkflows (https) +gitlab.cern.ch/tmrnjava/AliECS_conf/ (https) +alio2-cr1-hv-gw01.cern.ch:/opt/git/ControlWorkflows (ssh) +/home/flp/git/ControlWorkflows (local filesystem - (*entry does not survive a core restart*)) ` + "```" + ` By default, all short task and workflow names are assumed to be in the default repository (see ` + "`coconut repo list`" + ` command). -Any workflow from any repository can be loaded by providing a full and unique path (the ` + "`workflows`" + ` directory is omitted from the loading path as it's name and existence is enforced by convention when we know we're dealing with workflows), e.g. the following two are different workflows: +Any workflow from any repository can be loaded by providing a full and unique path, e.g. the following two are different workflows: ` + "```" + ` -github.com/AliceO2Group/ControlWorkflows/readout-qc-1 -gitlab.cern.ch/tmrnjava/AliECS_conf/readout-qc-1 +github.com/AliceO2Group/ControlWorkflows/workflows/readout-qc-1 +gitlab.cern.ch/tmrnjava/AliECS_conf/workflows/readout-qc-1 ` + "```" + ` By default a workflow is loaded from its state at HEAD in the master branch. A request to load a workflow can further be qualified with a branch, tag or commit hash: ` + "```" + ` readout-qc-1@readout-testing -gitlab.cern.ch/tmrnjava/AliECS_conf/readout-qc-1@5c7f1c1f +gitlab.cern.ch/tmrnjava/AliECS_conf/workflows/readout-qc-1@5c7f1c1f ` + "```" + ` Make sure to run ` + "`coconut repo refresh`" + ` if you make changes to a configuration repository.`, diff --git a/coconut/cmd/repo_add.go b/coconut/cmd/repo_add.go index f4b6435fa..3083bb3b9 100644 --- a/coconut/cmd/repo_add.go +++ b/coconut/cmd/repo_add.go @@ -25,22 +25,41 @@ package cmd import ( - "fmt" - "github.com/AliceO2Group/Control/coconut/control" "github.com/spf13/cobra" ) // repoAddCmd represents the repo add command var repoAddCmd = &cobra.Command{ - Use: "add", + Use: "add ", Aliases: []string{"new", "a"}, - Short: "add a new git repository", - Long: fmt.Sprintf(`The repository add command adds a git repository -to the catalogue of repositories used for task and workflow configuration.`), - Run: control.WrapCall(control.AddRepo), + Short: "add a new git repository", + Long: `The repository add command adds a git repository to the catalogue of repositories used for task and workflow configuration. +The default revision of the repository may be explicitly specified by passing the flag ` + "`--default-revision`" + ` . In any case, +the ensuing list is followed until a valid revision has been identified: + +- explicitly set default revision (optional) +- global default revision +- ` + "`master`" + ` +- ` + "`main`" + ` + +Exhaustion of the aforementioned list results in a repo add failure. + +` + "`coconut repo add`" + ` can be called with +1) a repository identifier +2) a repository identifier coupled with the ` + "`--default-revision`" + ` flag (see examples below) + +The protocol prefix should always be omitted.`, + Example: ` * ` + "`coconut repo add github.com/AliceO2Group/ControlWorkflows`" + ` + * ` + "`coconut repo add github.com/AliceO2Group/ControlWorkflows --default-revision custom-rev`" + ` + * ` + "`coconut repo add alio2-cr1-hv-gw01.cern.ch:/opt/git/ControlWorkflows --default-revision custom-rev`" + ` + * ` + "`coconut repo add /home/flp/git/ControlWorkflows`", + Run: control.WrapCall(control.AddRepo), + Args: cobra.ExactArgs(1), } func init() { repoCmd.AddCommand(repoAddCmd) + + repoAddCmd.Flags().StringP("default-revision", "d", "", "default revision for repository to add") } diff --git a/coconut/cmd/repo_default.go b/coconut/cmd/repo_default.go index f82eabaea..f9eb40ecc 100644 --- a/coconut/cmd/repo_default.go +++ b/coconut/cmd/repo_default.go @@ -25,19 +25,19 @@ package cmd import ( - "fmt" - "github.com/AliceO2Group/Control/coconut/control" "github.com/spf13/cobra" ) // repoRemoveCmd represents the repository remove command var repoDefaultCmd = &cobra.Command{ - Use: "default", + Use: "default ", Short: "set a git repository as default", - Long: fmt.Sprintf(`The repository default command sets a git repository -as the default repository for incoming workflow deployment requests.`), - Run: control.WrapCall(control.SetDefaultRepo), + Long: `The repository default command sets a git repository as the default repository for incoming workflow deployment requests. +A repository is referenced through its repo id, as reported by ` + "`coconut repo list`.", + Example: ` * ` + "`coconut repo default 2`", + Run: control.WrapCall(control.SetDefaultRepo), + Args: cobra.ExactArgs(1), } func init() { diff --git a/coconut/cmd/repo_default_revision.go b/coconut/cmd/repo_default_revision.go new file mode 100644 index 000000000..0067ba46c --- /dev/null +++ b/coconut/cmd/repo_default_revision.go @@ -0,0 +1,48 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2020 CERN and copyright holders of ALICE O². + * Author: Kostas Alexopoulos + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package cmd + +import ( + "github.com/AliceO2Group/Control/coconut/control" + "github.com/spf13/cobra" +) + +// repoDefaultRevision sets global and per-repo default revision +var repoDefaultRevisionCmd = &cobra.Command{ + Use: "default-revision ", + Short: "set default global and per-repository revision", + Long: `The repository default-revision command sets the global default repository revision.' + +To set a per repository default revision, the default revision specified needs to be preceded by the repository index (not its name), as is reported by ` + "`coconut repo list`.", + Example: ` * ` + "`coconut repo default-revision basic-tasks`" + ` Sets ` + "`basic-tasks`" + `as the global default-revision + * ` + "`coconut repo default-revision 0 master`" + ` Sets ` + "`master`" + `as the default-revision for repo with index 0 + * ` + "`coconut repo default-revision 2 vs-sftb`" + ` Sets ` + "`vs-sftb`" + `as the default-revision for repo with index 2`, + Run: control.WrapCall(control.SetDefaultRevision), + Args: cobra.RangeArgs(1, 2), +} + +func init() { + repoCmd.AddCommand(repoDefaultRevisionCmd) +} diff --git a/coconut/cmd/repo_list.go b/coconut/cmd/repo_list.go index 3dd63f27b..eecdea2b6 100644 --- a/coconut/cmd/repo_list.go +++ b/coconut/cmd/repo_list.go @@ -25,20 +25,17 @@ package cmd import ( - "fmt" - "github.com/AliceO2Group/Control/coconut/control" "github.com/spf13/cobra" ) // repoListCmd represents the repo list command var repoListCmd = &cobra.Command{ - Use: "list", + Use: "list", Aliases: []string{"l"}, - Short: "list git repositories", - Long: fmt.Sprintf(`The repostory list command lists available git repositories -to be used for task and workflow configuration.`), - Run: control.WrapCall(control.ListRepos), + Short: "list git repositories", + Long: "The repostory list command lists available git repositories to be used for task and workflow configuration.", + Run: control.WrapCall(control.ListRepos), } func init() { diff --git a/coconut/cmd/repo_refresh.go b/coconut/cmd/repo_refresh.go index c73610f3b..ee86cd0c6 100644 --- a/coconut/cmd/repo_refresh.go +++ b/coconut/cmd/repo_refresh.go @@ -25,22 +25,23 @@ package cmd import ( - "fmt" - "github.com/AliceO2Group/Control/coconut/control" "github.com/spf13/cobra" ) // repoListCmd represents the repo list command var repoRefreshCmd = &cobra.Command{ - Use: "refresh", + Use: "refresh [repo id]", Aliases: []string{"update", "u"}, - Short: "refresh git repositories", - Long: fmt.Sprintf(`The repository refresh command makes sure -all git repositories used for task and workflow configuration are up to date.`), - Run: control.WrapCall(control.RefreshRepos), + Short: "refresh git repositories", + Long: `The repository refresh command makes sure all git repositories used for task and workflow configuration are up to date. +It can optionally be supplied with a repo id, to only refresh a specific repo. Repo ids are reported by ` + "`coconut repo list`.", + Example: ` * ` + "`coconut repo refresh`" + ` + * ` + "`coconut repo refresh 1`", + Run: control.WrapCall(control.RefreshRepos), + Args: cobra.MaximumNArgs(1), } func init() { repoCmd.AddCommand(repoRefreshCmd) -} \ No newline at end of file +} diff --git a/coconut/cmd/repo_remove.go b/coconut/cmd/repo_remove.go index c970a326e..b2cbdb90f 100644 --- a/coconut/cmd/repo_remove.go +++ b/coconut/cmd/repo_remove.go @@ -25,20 +25,21 @@ package cmd import ( - "fmt" - "github.com/AliceO2Group/Control/coconut/control" "github.com/spf13/cobra" ) // repoRemoveCmd represents the repo remove command var repoRemoveCmd = &cobra.Command{ - Use: "remove", - Aliases: []string{"r", "delete", "d"}, - Short: "remove a git repository", - Long: fmt.Sprintf(`The repository remove command removes a git repository -from the catalogue of workflow configuration sources.`), - Run: control.WrapCall(control.RemoveRepo), + Use: "remove ", + Aliases: []string{"r", "delete", "del", "d"}, + Short: "remove a git repository", + Long: `The repository remove command removes a git repository from the catalogue of workflow configuration sources. +A repository is referenced by its repo id, as reported by` + "`coconut repo list`", + Example: ` * ` + "`coconut repo remove 1`" + ` + * ` + "`coconut repo del 2`", + Run: control.WrapCall(control.RemoveRepo), + Args: cobra.ExactArgs(1), } func init() { diff --git a/coconut/cmd/role.go b/coconut/cmd/role.go index 091232087..1455a1a40 100644 --- a/coconut/cmd/role.go +++ b/coconut/cmd/role.go @@ -33,9 +33,9 @@ import ( // roleCmd represents the role command var roleCmd = &cobra.Command{ - Use: "role", + Use: "role", Aliases: []string{"r"}, - Short: "query roles in an environment", + Short: "query roles in an environment", Long: fmt.Sprintf(`The role command queries the running instance of %s to display information on active roles.`, product.PRETTY_SHORTNAME), } diff --git a/coconut/cmd/role_query.go b/coconut/cmd/role_query.go index 6b0ea35a1..59f7f63e9 100644 --- a/coconut/cmd/role_query.go +++ b/coconut/cmd/role_query.go @@ -25,18 +25,35 @@ package cmd import ( - "github.com/spf13/cobra" "github.com/AliceO2Group/Control/coconut/control" + "github.com/spf13/cobra" ) // roleQueryCmd represents the role list command var roleQueryCmd = &cobra.Command{ - Use: "query [environment id] [query path]", + Use: "query [environment id] [query path]", Aliases: []string{"query", "q"}, - Short: "query O² roles", - Long: `The role query command returns one or more role trees.`, - Run: control.WrapCall(control.QueryRoles), - Args: cobra.ExactArgs(2), + Short: "query O² roles", + Long: `The role query command returns one or more role subtrees within a given environment. + +It allows the user to inspect the environment's workflow at runtime, after template processing operations, including +iterator expansion, just-in-time subworkflow inclusion, and variable substitution, have taken place. + +For the target role or roles, it also prints the locally defined variables, as well as the consolidated variable +stack from the point of view of that role. + +The role query command accepts two arguments: the environment ID and the query path. The query path is a dot-separated +walk through the role tree of the given environment, starting from the root role. Wildcard expressions are allowed, as +per https://github.com/gobwas/glob syntax. + +Examples: + * ` + "`coconut role query 2rE9AV3m1HL readout-dataflow`" + ` - queries the role ` + "`readout-dataflow`" + ` in environment ` + "`2rE9AV3m1HL`" + `, prints the full tree, along with the variables defined in the root role + * ` + "`coconut role query 2rE9AV3m1HL readout-dataflow.host-aido2-bld4-lab102`" + ` - queries the role ` + "`readout-dataflow.host-aido2-bld4-lab102`" + `, prints the subtree of that role, along with the variables defined in it + * ` + "`coconut role query 2rE9AV3m1HL readout-dataflow.host-aido2-bld4-lab102.data-distribution.stfs`" + ` - queries the role at the given path, it is a task role so there is no subtree, prints the variables defined in that role + * ` + "`coconut role query 2rE9AV3m1HL readout-dataflow.host-aido2-bld4-lab*`" + ` - queries the roles matching the given glob expression, prints all the subtrees and variables + * ` + "`coconut role query 2rE9AV3m1HL readout-dataflow.host-aido2-bld4-lab*.data-distribution.*`" + ` - queries the task roles matching the given glob expression, prints all the variables`, + Run: control.WrapCall(control.QueryRoles), + Args: cobra.ExactArgs(2), } func init() { diff --git a/coconut/cmd/root.go b/coconut/cmd/root.go index 94ce87512..9876a8a65 100644 --- a/coconut/cmd/root.go +++ b/coconut/cmd/root.go @@ -74,18 +74,23 @@ func init() { cobra.OnInitialize(initConfig) viper.Set("version", product.VERSION) + viper.Set("component", "coconut") // Here you will define your flags and configuration settings. // Cobra supports persistent flags, which, if defined here, // will be global for your application. rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", fmt.Sprintf("optional configuration file for %s (default $HOME/.config/%s/settings.yaml)", app.NAME, app.NAME)) - rootCmd.PersistentFlags().String("endpoint", "127.0.0.1:47102", product.PRETTY_SHORTNAME + " core endpoint as HOST:PORT") - rootCmd.PersistentFlags().String("config_endpoint", "consul://127.0.0.1:8500", "configuration endpoint used by AliECS core as PROTO://HOST:PORT") + rootCmd.PersistentFlags().String("endpoint", "127.0.0.1:32102", product.PRETTY_SHORTNAME+" core endpoint as HOST:PORT") + rootCmd.PersistentFlags().String("config_endpoint", "apricot://127.0.0.1:32101", "configuration endpoint used by AliECS core as PROTO://HOST:PORT") rootCmd.PersistentFlags().BoolP("verbose", "v", false, "show verbose output for debug purposes") + rootCmd.PersistentFlags().Bool("nospinner", false, "disable animations in output") + rootCmd.PersistentFlags().Bool("nocolor", false, "disable colors in output") viper.BindPFlag("endpoint", rootCmd.PersistentFlags().Lookup("endpoint")) viper.BindPFlag("config_endpoint", rootCmd.PersistentFlags().Lookup("config_endpoint")) viper.BindPFlag("verbose", rootCmd.PersistentFlags().Lookup("verbose")) + viper.BindPFlag("nospinner", rootCmd.PersistentFlags().Lookup("nospinner")) + viper.BindPFlag("nocolor", rootCmd.PersistentFlags().Lookup("nocolor")) // Cobra also supports local flags, which will only run // when this action is called directly. @@ -95,9 +100,11 @@ func init() { // initConfig reads in config file and ENV variables if set. func initConfig() { viper.SetDefault("log.level", "info") - viper.SetDefault("endpoint", "127.0.0.1:47102") - viper.SetDefault("config_endpoint", "127.0.0.1:8500") + viper.SetDefault("endpoint", "127.0.0.1:32102") + viper.SetDefault("config_endpoint", "apricot://127.0.0.1:32101") viper.SetDefault("verbose", false) + viper.SetDefault("nospinner", false) + viper.SetDefault("nocolor", false) if cfgFile != "" { // Use config file from the flag. @@ -111,7 +118,7 @@ func initConfig() { } // Search config in .config/coconut directory with name "settings.yaml" - viper.AddConfigPath(path.Join(home, ".config/" + app.NAME)) + viper.AddConfigPath(path.Join(home, ".config/"+app.NAME)) viper.SetConfigName("settings") } diff --git a/coconut/cmd/task.go b/coconut/cmd/task.go index a2695146e..d858ec39e 100644 --- a/coconut/cmd/task.go +++ b/coconut/cmd/task.go @@ -33,9 +33,9 @@ import ( // taskCmd represents the task command var taskCmd = &cobra.Command{ - Use: "task", + Use: "task", Aliases: []string{"t"}, - Short: "manage active tasks", + Short: "manage active tasks", Long: fmt.Sprintf(`The task command interacts with the running instance of %s to manage active tasks.`, product.PRETTY_SHORTNAME), } diff --git a/coconut/cmd/task_clean.go b/coconut/cmd/task_clean.go index 20fada4e7..d0fa8b21f 100644 --- a/coconut/cmd/task_clean.go +++ b/coconut/cmd/task_clean.go @@ -25,20 +25,20 @@ package cmd import ( - "github.com/spf13/cobra" "github.com/AliceO2Group/Control/coconut/control" + "github.com/spf13/cobra" ) // taskCleanCmd represents the task clean command var taskCleanCmd = &cobra.Command{ - Use: "clean", + Use: "clean", Aliases: []string{"clean", "cleanup", "cl"}, - Short: "clean up idle O² tasks", + Short: "clean up idle O² tasks", Long: `The task clean command removes all tasks that aren't currently associated with an environment. This includes AliECS tasks in any state. Alternatively, a list of task IDs to remove can be passed as a space-separated sequence of parameters.`, - Run: control.WrapCall(control.CleanTasks), - Args: cobra.ArbitraryArgs, + Run: control.WrapCall(control.CleanTasks), + Args: cobra.ArbitraryArgs, } func init() { diff --git a/coconut/cmd/task_list.go b/coconut/cmd/task_list.go index fdaeb0f65..7a5ad4a4d 100644 --- a/coconut/cmd/task_list.go +++ b/coconut/cmd/task_list.go @@ -25,18 +25,18 @@ package cmd import ( - "github.com/spf13/cobra" "github.com/AliceO2Group/Control/coconut/control" + "github.com/spf13/cobra" ) // taskListCmd represents the task list command var taskListCmd = &cobra.Command{ - Use: "list", + Use: "list", Aliases: []string{"list", "ls", "l"}, - Short: "list O² tasks", + Short: "list O² tasks", Long: `The task list command shows a list of currently active tasks. This includes AliECS tasks in any state.`, - Run: control.WrapCall(control.GetTasks), + Run: control.WrapCall(control.GetTasks), } func init() { diff --git a/coconut/cmd/template.go b/coconut/cmd/template.go index c81c1b11e..d4723a835 100644 --- a/coconut/cmd/template.go +++ b/coconut/cmd/template.go @@ -32,9 +32,9 @@ import ( // templateCmd represents the template command var templateCmd = &cobra.Command{ - Use: "template", + Use: "template", Aliases: []string{"templ"}, - Short: "query available workflow templates in configuration repositories", + Short: "query available workflow templates in configuration repositories", Long: fmt.Sprintf(`The template command interacts with the workflow configuration system to display information on available workflow templates.`), } diff --git a/coconut/cmd/template_list.go b/coconut/cmd/template_list.go index c0d4c09dc..4061bdfdb 100644 --- a/coconut/cmd/template_list.go +++ b/coconut/cmd/template_list.go @@ -25,20 +25,42 @@ package cmd import ( - "github.com/spf13/cobra" "github.com/AliceO2Group/Control/coconut/control" + "github.com/spf13/cobra" ) // templateListCmd represents the template list command var templateListCmd = &cobra.Command{ - Use: "list", + Use: "list", Aliases: []string{"list", "ls", "l"}, - Short: "list available workflow templates", + Short: "list available workflow templates", Long: `The template list command shows a list of available workflow templates. -These workflow templates can then be loaded to create an environment.`, - Run: control.WrapCall(control.ListWorkflowTemplates), +These workflow templates can then be loaded to create an environment. + +` + "`coconut templ list` " + `can be called with +1) a combination of the ` + "`--repo` " + `, ` + "`--revision` " + `, ` + "`--all-branches` " + `, ` + "`--all-tags` " + `, ` + "`--all-workflows` " + `flags, or with +2) an argument in the form of [repo-pattern]@[revision-pattern], where the patterns are globbing.`, + Example: ` * ` + "`coconut templ list`" + ` lists templates from the HEAD of master for all git repositories + * ` + "`coconut templ list --all-workflows`" + ` lists all templates (including non-public ones) from the HEAD of master for all git repositories + * ` + "`coconut templ list '*AliceO2Group*'`" + ` lists all templates coming from the HEAD of master of git repositories that match the pattern *AliceO2Group* + * ` + "`coconut templ list '*@v*'`" + ` lists templates coming from revisions matching the ` + "`v*`" + `pattern for all git repositories + * ` + "`coconut templ list --repository='*AliceO2Group*'`" + ` lists all templates coming from the HEAD of master of git repositories that match the pattern *AliceO2Group* + * ` + "`coconut templ list --revision='dev*'`" + ` lists templates coming from revisions matching the ` + "`dev*`" + `pattern for all git repositories + * ` + "`coconut templ list --repository='*gitlab.cern.ch*' --revision=master`" + ` lists templates for revisions ` + "`master`" + `for git repositories matching ` + "`*gitlab.cern.ch*`" + ` + * ` + "`coconut templ list --all-branches`" + ` lists templates from all branches for all git repositories + * ` + "`coconut templ list --repository='*github.com*' --all-tags`" + ` lists templates from all tags for git repositories which match the *github.com* pattern + * ` + "`coconut templ list --revision=5c7f1c1fded1b87243998579ed876c8035a08377 `" + ` lists templates from the commit corresponding to the hash for all git repositories`, + + Run: control.WrapCall(control.ListWorkflowTemplates), } func init() { templateCmd.AddCommand(templateListCmd) + + templateListCmd.Flags().StringP("repository", "r", "", "repositories to list templates from") + templateListCmd.Flags().StringP("revision", "i", "", "revisions (branches/tags) to list templates from") + templateListCmd.Flags().BoolP("all-branches", "b", false, "list templates from all branches") + templateListCmd.Flags().BoolP("all-tags", "t", false, "list templates from all tags") + templateListCmd.Flags().BoolP("all-workflows", "a", false, "list all templates, even non-public ones") + templateListCmd.Flags().BoolP("show-description", "d", false, "show the description of each template") } diff --git a/coconut/configuration/configuration.go b/coconut/configuration/configuration.go index be7a567ae..74cc3d6e7 100644 --- a/coconut/configuration/configuration.go +++ b/coconut/configuration/configuration.go @@ -1,7 +1,7 @@ /* * === This file is part of ALICE O² === * - * Copyright 2018 CERN and copyright holders of ALICE O². + * Copyright 2018-2021 CERN and copyright holders of ALICE O². * Author: Teo Mrnjavac * * This program is free software: you can redistribute it and/or modify @@ -27,31 +27,45 @@ package configuration import ( - "github.com/spf13/cobra" - "github.com/spf13/viper" - "github.com/briandowns/spinner" - "time" + "encoding/json" + "errors" + "fmt" "io" - "github.com/sirupsen/logrus" "os" - "github.com/AliceO2Group/Control/common/logger" - "fmt" "strings" + "time" + + "github.com/AliceO2Group/Control/apricot" + apricotpb "github.com/AliceO2Group/Control/apricot/protos" + "github.com/AliceO2Group/Control/common/logger" + "github.com/AliceO2Group/Control/common/utils" "github.com/AliceO2Group/Control/configuration" - "errors" - "encoding/json" - "gopkg.in/yaml.v2" + "github.com/AliceO2Group/Control/configuration/componentcfg" + "github.com/briandowns/spinner" "github.com/naoina/toml" + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + "github.com/spf13/viper" + "gopkg.in/yaml.v3" ) var log = logger.New(logrus.StandardLogger(), "coconut") type RunFunc func(*cobra.Command, []string) -type ConfigurationCall func(configuration.Source, *cobra.Command, []string, io.Writer) (error) +type CallFunc func(configuration.Service, *cobra.Command, []string, io.Writer) (error, int) +const ( + EC_ZERO = iota + EC_INVALID_ARGS = iota // Provided args by the user are invalid + EC_INVALID_ARGS_MSG = "component and entry names cannot contain `/` or `@`" + EC_CONNECTION_ERROR = iota // Source connection error + EC_EMPTY_DATA = iota // Source retrieved empty data + EC_EMPTY_DATA_MSG = "no data was found" + EC_LOGIC_ERROR = iota // Logic/Output error +) -func WrapCall(call ConfigurationCall) RunFunc { +func WrapCall(call CallFunc) RunFunc { return func(cmd *cobra.Command, args []string) { endpoint := viper.GetString("config_endpoint") log.WithPrefix(cmd.Use). @@ -59,77 +73,237 @@ func WrapCall(call ConfigurationCall) RunFunc { Debug("initializing configuration client") s := spinner.New(spinner.CharSets[11], 100*time.Millisecond) - s.Color("yellow") + _ = s.Color("yellow") s.Suffix = " working..." s.Start() - cfg, err := configuration.NewSource(endpoint) - if err != nil { - var fields logrus.Fields - if logrus.GetLevel() == logrus.DebugLevel { - fields = logrus.Fields{"error": err} - } - log.WithPrefix(cmd.Use). - WithFields(fields). - Fatal("cannot query endpoint") - os.Exit(1) - } + apricotServiceInstance := apricot.Instance() var out strings.Builder // redirect stdout to null, the only way to output is stdout := os.Stdout - os.Stdout,_ = os.Open(os.DevNull) - err = call(cfg, cmd, args, &out) + os.Stdout, _ = os.Open(os.DevNull) + err, code := call(apricotServiceInstance, cmd, args, &out) os.Stdout = stdout s.Stop() + fmt.Print(out.String()) + if err != nil { - var fields logrus.Fields - if logrus.GetLevel() == logrus.DebugLevel { - fields = logrus.Fields{"error": err} - } log.WithPrefix(cmd.Use). - WithFields(fields). - Fatal("cannot query endpoint") - os.Exit(1) + WithError(err). + Fatal("command finished with error") + os.Exit(code) } - - fmt.Print(out.String()) } } -func Dump(cfg configuration.Source, cmd *cobra.Command, args []string, o io.Writer) (err error) { +func Dump(svc configuration.Service, cmd *cobra.Command, args []string, o io.Writer) (err error, code int) { if len(args) != 1 { err = errors.New(fmt.Sprintf("accepts 1 arg(s), received %d", len(args))) - return + return err, EC_INVALID_ARGS } key := args[0] - data, err := cfg.GetRecursive(key) + dataJson, err := svc.RawGetRecursive(key) + if err != nil { + return err, EC_CONNECTION_ERROR + } + + var raw map[string]interface{} + err = json.Unmarshal([]byte(dataJson), &raw) if err != nil { - return + return err, EC_LOGIC_ERROR } format, err := cmd.Flags().GetString("format") if err != nil { - return + return err, EC_INVALID_ARGS } var output []byte switch strings.ToLower(format) { case "json": - output, err = json.MarshalIndent(data, "", " ") + output, err = json.MarshalIndent(raw, "", " ") case "yaml": - output, err = yaml.Marshal(data) + output, err = yaml.Marshal(raw) case "toml": - output, err = toml.Marshal(data) + output, err = toml.Marshal(raw) } if err != nil { log.WithField("error", err.Error()).Fatalf("cannot serialize subtree to %s", strings.ToLower(format)) - return + return err, EC_LOGIC_ERROR + } + + _, _ = fmt.Fprintln(o, string(output)) + + return nil, EC_ZERO +} + +// coconut conf list +func List(svc configuration.Service, cmd *cobra.Command, args []string, o io.Writer) (err error, code int) { + if len(args) > 1 { + return errors.New(fmt.Sprintf("command requires maximum 1 arg but received %d", len(args))), EC_INVALID_ARGS + } + + // we have 0 or 1 args + if err != nil { + return err, EC_INVALID_ARGS + } + + if len(args) == 0 { // 0 args: conf list + components, _ := svc.ListComponents() + output, err := formatListOutput(cmd, components) + if err != nil { + return err, EC_LOGIC_ERROR + } + _, _ = fmt.Fprintln(o, string(output)) + return nil, EC_ZERO + } + + // 1 arg: conf list + if !componentcfg.IsInputValidComponentName(args[0]) { + return errors.New(EC_INVALID_ARGS_MSG), EC_INVALID_ARGS + } + + components, _ := svc.ListComponentEntries(&componentcfg.EntriesQuery{ + Component: args[0], + RunType: apricotpb.RunType_NULL, + RoleName: "", + }) + + output, err := formatListOutput(cmd, components) + if err != nil { + return err, EC_LOGIC_ERROR + } + _, _ = fmt.Fprintln(o, string(output)) + return nil, EC_ZERO +} + +// coconut conf show +func Show(svc configuration.Service, cmd *cobra.Command, args []string, o io.Writer) (err error, code int) { + // Allowed inputs: + // # len(args) == 1: + // coconut conf show component/RUNTYPE/role/entry + // # len(args) == 2: + // coconut conf show component entry [--runtype RUNTYPE_EXPR] [--role role_expr] + // # ↑ if runtype/role is empty, we default to ANY/any + // # if it's e.g. SOME_RUNTYPE/some_role we just build the path for now; + // # TODO: advanced query expressions come later + + var query *componentcfg.Query + query, err = queryFromFlags(cmd, args) + if err != nil { + return err, EC_INVALID_ARGS + } + // At this point we know what to query, thanks to a + // componentcfg.Query that can be fed to configuration.Instance(). + var ( + cfgPayload string + simulate bool + ) + simulate, err = cmd.Flags().GetBool("simulate") + if err != nil { + return err, EC_INVALID_ARGS + } + + if simulate { + var extraVars string + extraVars, err = cmd.Flags().GetString("extra-vars") + if err != nil { + return err, EC_INVALID_ARGS + } + + extraVars = strings.TrimSpace(extraVars) + if cmd.Flags().Changed("extra-vars") && len(extraVars) == 0 { + err = errors.New("empty list of extra-vars supplied") + return err, EC_INVALID_ARGS + } + + var extraVarsMap map[string]string + extraVarsMap, err = utils.ParseExtraVars(extraVars) + if err != nil { + return err, EC_INVALID_ARGS + } + + cfgPayload, err = svc.GetAndProcessComponentConfiguration(query, extraVarsMap) + } else { + // No template processing simulation requested, getting raw payload + cfgPayload, err = svc.GetComponentConfiguration(query) + } + if err != nil { + return err, EC_CONNECTION_ERROR + } + if cfgPayload == "" { + return errors.New(EC_EMPTY_DATA_MSG), EC_EMPTY_DATA + } + + _, _ = fmt.Fprintln(o, cfgPayload) + + return nil, EC_ZERO +} + +func Import(svc configuration.Service, cmd *cobra.Command, args []string, o io.Writer) (err error, code int) { + useNewComponent, err := cmd.Flags().GetBool("new-component") + if err != nil { + return err, EC_INVALID_ARGS + } + useExtension, err := cmd.Flags().GetString("format") + if err != nil { + return err, EC_INVALID_ARGS + } + + var pushQuery *componentcfg.Query + // The last argument is always assumed to be the input file, so we must exclude it + queryArgs := args[:len(args)-1] // we can do this because it's known that len(args)>0 + pushQuery, err = queryFromFlags(cmd, queryArgs) + if err != nil { + return err, EC_INVALID_ARGS + } + filePath := args[len(args)-1] + + fileParts := strings.Split(filePath, ".") + extension := "" + if len(fileParts) > 1 { + extension = fileParts[len(fileParts)-1] + } + + if !isFileExtensionValid(extension) && useExtension == "" { + return errors.New("supported file extensions: JSON, YAML, INI or TOML." + + " To force a specific configuration format, see flag --format/-f"), EC_INVALID_ARGS + } else if useExtension != "" { + extension = strings.ToUpper(useExtension) + } + + var payload []byte + payload, err = getFileContent(filePath) + if err != nil { + return err, EC_LOGIC_ERROR + } + + var existingComponentUpdated, existingEntryUpdated bool + existingComponentUpdated, existingEntryUpdated, err = svc.ImportComponentConfiguration(pushQuery, string(payload), useNewComponent) + if err != nil { + return err, EC_LOGIC_ERROR + } + + toPrintKey := red(pushQuery.Component) + componentcfg.SEPARATOR + + blue(pushQuery.RunType) + componentcfg.SEPARATOR + + red(pushQuery.RoleName) + componentcfg.SEPARATOR + + blue(pushQuery.EntryKey) + + userMsg := "" + if !existingComponentUpdated { + userMsg = "New component created: " + red(pushQuery.Component) + "\n" + } + if !existingEntryUpdated { + userMsg += "New entry created: " + blue(pushQuery.EntryKey) + "\n" + } else { + userMsg += "Entry updated: " + blue(pushQuery.EntryKey) + "\n" } - fmt.Fprintln(o, string(output)) + userMsg += "Configuration imported: " + toPrintKey - return nil + _, _ = fmt.Fprintln(o, userMsg) + return nil, 0 } diff --git a/coconut/configuration/configurationutil.go b/coconut/configuration/configurationutil.go new file mode 100644 index 000000000..fefd20a53 --- /dev/null +++ b/coconut/configuration/configurationutil.go @@ -0,0 +1,158 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2020 CERN and copyright holders of ALICE O². + * Author: George Raduta + * Author: Teo Mrnjavac + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package configuration + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + apricotpb "github.com/AliceO2Group/Control/apricot/protos" + "github.com/AliceO2Group/Control/configuration/componentcfg" + "github.com/fatih/color" + "github.com/spf13/cobra" + "gopkg.in/yaml.v3" + "io/ioutil" + "os" + "regexp" + "strings" +) + +var ( + blue = color.New(color.FgHiBlue).SprintFunc() + red = color.New(color.FgHiRed).SprintFunc() + // component /RUNTYPE /rolename /entry + inputComponentEntryRegex = regexp.MustCompile(`^([a-zA-Z0-9-_]+)(\/[A-Z0-9-_]+){1}(\/[a-z-A-Z0-9-_]+){1}(\/[a-z-A-Z0-9-_]+){1}$`) +) + +// Utility function to create a componentcfg.Query from combos of args and flags +func queryFromFlags(cmd *cobra.Command, args []string) (query *componentcfg.Query, err error) { + if len(args) < 1 || len(args) > 2 { + err = errors.New(fmt.Sprintf("accepts 1 or 2 arg(s), but received %d", len(args))) + return + } + + switch len(args) { + case 1: // coconut conf show component/RUNTYPE/role/entry + if componentcfg.IsStringValidQueryPath(args[0]) { + if strings.Contains(args[0], "/") { + // coconut conf show c/R/r/e + query, err = componentcfg.NewQuery(args[0]) + if err != nil { + return + } + } + } else { + err = errors.New("please provide entry name") + return + } + case 2: // coconut conf show component entry [--runtype RUNTYPE_EXPR] [--role role_expr] + var runTypeS, machineRole string + var runType apricotpb.RunType + runTypeS, err = cmd.Flags().GetString("runtype") + if err != nil { + return + } + if len(runTypeS) == 0 { + runType = apricotpb.RunType_ANY // default value for empty runType input + } else { + runTypeI, ok := apricotpb.RunType_value[runTypeS] + if !ok { + err = fmt.Errorf("bad value for run type: %s", runTypeS) + return + } + runType = apricotpb.RunType(runTypeI) + } + machineRole, err = cmd.Flags().GetString("role") + if err != nil { + return + } + if len(machineRole) == 0 { + machineRole = "any" // default value for empty machine role input + } + + if !componentcfg.IsInputValidComponentName(args[0]) || !componentcfg.IsInputValidEntryName(args[1]) { + err = errors.New(EC_INVALID_ARGS_MSG) + return + } else { + query = &componentcfg.Query{ + Component: args[0], + RunType: runType, + RoleName: machineRole, + EntryKey: args[1], + } + } + } + return +} + +func formatListOutput(cmd *cobra.Command, output []string) (parsedOutput []byte, err error) { + format, err := cmd.Flags().GetString("output") + if err != nil { + return + } + + switch strings.ToLower(format) { + case "json": + parsedOutput, err = json.MarshalIndent(output, "", " ") + case "yaml": + parsedOutput, err = yaml.Marshal(output) + } + if err != nil { + return + } + parsedOutput = bytes.TrimSuffix(parsedOutput, []byte("\n")) + return parsedOutput, nil +} + +func getMaxLenOfKey(keys []string) (maxLen int) { + maxLen = 0 + for _, value := range keys { + if len(value)-len(componentcfg.ConfigComponentsPath) >= maxLen { + maxLen = len(value) - len(componentcfg.ConfigComponentsPath) + } + } + return +} + +func getFileContent(filePath string) (fileContent []byte, err error) { + file, err := os.Open(filePath) + if err != nil { + return + } + defer file.Close() + + fileContentByte, err := ioutil.ReadAll(file) + if err != nil { + return + } + return fileContentByte, nil +} + +func isFileExtensionValid(extension string) bool { + extension = strings.ToUpper(extension) + return extension == "JSON" || extension == "YAML" || extension == "YML" || extension == "INI" || extension == "TOML" +} diff --git a/coconut/control/control.go b/coconut/control/control.go index 4e6eb57dc..51b09ff15 100644 --- a/coconut/control/control.go +++ b/coconut/control/control.go @@ -28,37 +28,58 @@ package control import ( "context" + "encoding/json" "errors" "fmt" - "github.com/xlab/treeprint" "io" "os" + "regexp" + "sort" "strconv" "strings" + "text/template" "time" + commonpb "github.com/AliceO2Group/Control/common/protos" + "github.com/fatih/color" + + "github.com/xlab/treeprint" + + "github.com/AliceO2Group/Control/apricot" "github.com/AliceO2Group/Control/coconut" "github.com/AliceO2Group/Control/coconut/protos" "github.com/AliceO2Group/Control/common/logger" + "github.com/AliceO2Group/Control/common/utils" "github.com/briandowns/spinner" "github.com/olekukonko/tablewriter" + "github.com/rs/xid" "github.com/sirupsen/logrus" "github.com/spf13/cobra" "github.com/spf13/viper" "google.golang.org/grpc" ) -const( - CALL_TIMEOUT = 55*time.Second - SPINNER_TICK = 100*time.Millisecond +const ( + CALL_TIMEOUT = 55 * time.Second + SPINNER_TICK = 100 * time.Millisecond + HLCONFIG_COMPONENT_PREFIX = "COG-v1" + HLCONFIG_PATH_PREFIX = "consul://o2/runtime/" ) var log = logger.New(logrus.StandardLogger(), "coconut") type RunFunc func(*cobra.Command, []string) -type ControlCall func(context.Context, *coconut.RpcClient, *cobra.Command, []string, io.Writer) (error) +type ControlCall func(context.Context, *coconut.RpcClient, *cobra.Command, []string, io.Writer) error +type ConfigurationPayload struct { + Name string `json:"name"` + Workflow string `json:"workflow"` + Revision string `json:"revision"` + Repository string `json:"repository"` + Vars map[string]string `json:"variables"` + Detectors []string `json:"detectors"` +} func WrapCall(call ControlCall) RunFunc { return func(cmd *cobra.Command, args []string) { @@ -68,9 +89,16 @@ func WrapCall(call ControlCall) RunFunc { Debug("initializing gRPC client") s := spinner.New(spinner.CharSets[11], SPINNER_TICK) - _ = s.Color("yellow") - s.Suffix = " working..." - s.Start() + auto, _ := cmd.Flags().GetBool("auto") + if !viper.GetBool("nospinner") && !auto { + _ = s.Color("yellow") + s.Suffix = " working..." + s.Start() + } + + if viper.GetBool("nocolor") { + color.NoColor = true + } cxt, cancel := context.WithTimeout(context.Background(), CALL_TIMEOUT) rpc := coconut.NewClient(cxt, cancel, endpoint) @@ -79,10 +107,16 @@ func WrapCall(call ControlCall) RunFunc { // redirect stdout to null, the only way to output is stdout := os.Stdout - os.Stdout,_ = os.Open(os.DevNull) + if !auto { + os.Stdout, _ = os.Open(os.DevNull) + } err := call(cxt, rpc, cmd, args, &out) os.Stdout = stdout - s.Stop() + + if !viper.GetBool("nospinner") { + s.Stop() + } + fmt.Print(out.String()) if err != nil { @@ -94,7 +128,6 @@ func WrapCall(call ControlCall) RunFunc { } } - func GetInfo(cxt context.Context, rpc *coconut.RpcClient, cmd *cobra.Command, args []string, o io.Writer) (err error) { var response *pb.GetFrameworkInfoReply response, err = rpc.GetFrameworkInfo(cxt, &pb.GetFrameworkInfoRequest{}, grpc.EmptyCallOption{}) @@ -115,28 +148,95 @@ func GetInfo(cxt context.Context, rpc *coconut.RpcClient, cmd *cobra.Command, ar revisionStr = fmt.Sprintf("revision %s", green(revisionStr)) } - _, _ = fmt.Fprintf(o, "instance name: %s\n", response.GetInstanceName()) - _, _ = fmt.Fprintf(o, "endpoint: %s\n", green(viper.GetString("endpoint"))) - _, _ = fmt.Fprintf(o, "core version: %s %s %s\n", response.GetVersion().GetProductName(), versionStr, revisionStr) - _, _ = fmt.Fprintf(o, "framework id: %s\n", response.GetFrameworkId()) - _, _ = fmt.Fprintf(o, "environments count: %s\n", green(response.GetEnvironmentsCount())) - _, _ = fmt.Fprintf(o, "active tasks count: %s\n", green(response.GetTasksCount())) - _, _ = fmt.Fprintf(o, "global state: %s\n", colorGlobalState(response.GetState())) + _, _ = fmt.Fprintf(o, "instance name: %s\n", response.GetInstanceName()) + _, _ = fmt.Fprintf(o, "core version: %s %s %s\n", response.GetVersion().GetProductName(), versionStr, revisionStr) + _, _ = fmt.Fprintf(o, "core endpoint: %s\n", green(viper.GetString("endpoint"))) + _, _ = fmt.Fprintf(o, "configuration endpoint: %s\n", green(response.GetConfigurationEndpoint())) + _, _ = fmt.Fprintf(o, "framework id: %s\n", response.GetFrameworkId()) + _, _ = fmt.Fprintf(o, "environments count: %s\n", green(response.GetEnvironmentsCount())) + _, _ = fmt.Fprintf(o, "active tasks count: %s\n", green(response.GetTasksCount())) + _, _ = fmt.Fprintf(o, "global state: %s\n", colorGlobalState(response.GetState())) + + allDetectors := response.GetDetectorsInInstance() + _, _ = fmt.Fprintf(o, "detectors in instance: %s (%d total)\n", green(strings.Join(allDetectors, " ")), len(allDetectors)) + _, _ = fmt.Fprintf(o, " active (in use): %s\n", yellow(strings.Join(response.GetActiveDetectors(), " "))) + _, _ = fmt.Fprintf(o, " available: %s\n", green(strings.Join(response.GetAvailableDetectors(), " "))) + + // Integrated Services API query + pluginsResponse, err := rpc.GetIntegratedServices(cxt, &pb.Empty{}, grpc.EmptyCallOption{}) + if err != nil { + return + } + services := pluginsResponse.GetServices() + enabledCount := 0 + + for _, svc := range pluginsResponse.GetServices() { + if svc.GetEnabled() { + enabledCount++ + } + } + intservMessage := "none" + if len(services) > 0 { + intservMessage = fmt.Sprintf("%s enabled (out of %d total)", green(enabledCount), len(services)) + } + + _, _ = fmt.Fprintf(o, "integrated services: %s\n", intservMessage) + + sortedSvcIds := make([]string, len(services)) + i := 0 + for svcId := range services { + sortedSvcIds[i] = svcId + i++ + } + sort.Strings(sortedSvcIds) + + for _, svcId := range sortedSvcIds { + svc := services[svcId] + enabledString := dark("disabled") + if svc.Enabled { + enabledString = green("enabled") + } + + _, _ = fmt.Fprintf(o, " %-22s%s\n", svcId+":", enabledString) + if !svc.Enabled { + continue + } + + connectionState := svc.GetConnectionState() + svcEndpoint := svc.GetEndpoint() + switch connectionState { + case "TRANSIENT_FAILURE": + connectionState = red(connectionState) + svcEndpoint = red(svcEndpoint) + case "CONNECTING": + connectionState = yellow(connectionState) + svcEndpoint = yellow(svcEndpoint) + case "READY": + connectionState = green(connectionState) + svcEndpoint = green(svcEndpoint) + } + + _, _ = fmt.Fprintf(o, " %-20s%s\n", "service name:", svc.GetName()) + _, _ = fmt.Fprintf(o, " %-20s%s\n", "endpoint:", svcEndpoint) + _, _ = fmt.Fprintf(o, " %-20s%s\n", "connection state:", connectionState) + if svcData := svc.GetData(); svcData != "{}" { + _, _ = fmt.Fprintf(o, " %-20s%s\n", "data:", svcData) + } + } return nil } - func Teardown(cxt context.Context, rpc *coconut.RpcClient, cmd *cobra.Command, args []string, o io.Writer) (err error) { log.Fatal("not implemented yet") os.Exit(1) return } - func GetEnvironments(cxt context.Context, rpc *coconut.RpcClient, cmd *cobra.Command, args []string, o io.Writer) (err error) { var response *pb.GetEnvironmentsReply - response, err = rpc.GetEnvironments(cxt, &pb.GetEnvironmentsRequest{}, grpc.EmptyCallOption{}) + showAll, _ := cmd.Flags().GetBool("show-all") + response, err = rpc.GetEnvironments(cxt, &pb.GetEnvironmentsRequest{ShowAll: showAll}, grpc.EmptyCallOption{}) if err != nil { return } @@ -145,15 +245,15 @@ func GetEnvironments(cxt context.Context, rpc *coconut.RpcClient, cmd *cobra.Com fmt.Fprintln(o, "no environments running") } else { table := tablewriter.NewWriter(o) - table.SetHeader([]string{"id", "created", "state"}) + table.SetHeader([]string{"id", "workflow template", "created", "state"}) table.SetBorder(false) fg := tablewriter.Colors{tablewriter.Bold, tablewriter.FgYellowColor} - table.SetHeaderColor(fg, fg, fg) + table.SetHeaderColor(fg, fg, fg, fg) data := make([][]string, 0, 0) for _, envi := range response.GetEnvironments() { - formatted := formatTimestamp(envi.GetCreatedWhen()) - data = append(data, []string{envi.GetId(), formatted, colorState(envi.GetState())}) + formatted := formatTimestamp(time.Millisecond*time.Duration(envi.GetCreatedWhen())) + data = append(data, []string{envi.GetId(), envi.GetRootRole(), formatted, colorState(envi.GetState())}) } table.AppendBulk(data) @@ -163,33 +263,243 @@ func GetEnvironments(cxt context.Context, rpc *coconut.RpcClient, cmd *cobra.Com return nil } - func CreateEnvironment(cxt context.Context, rpc *coconut.RpcClient, cmd *cobra.Command, args []string, o io.Writer) (err error) { - wfPath, err := cmd.Flags().GetString("workflow-template") + configPayloadPath, err := cmd.Flags().GetString("configuration") + if err != nil { + return fmt.Errorf("cannot get configuration payload path value: %w", err) + } + userWfPath, err := cmd.Flags().GetString("workflow-template") + if err != nil { + return fmt.Errorf("cannot get workflow template path value: %w", err) + } + if cmd.Flags().Changed("configuration") && len(configPayloadPath) == 0 && cmd.Flags().Changed("workflow-template") && len(userWfPath) == 0 { + return fmt.Errorf("no configuration payload path or workflow template path provided: %w", err) + } + + payloadData := new(ConfigurationPayload) + var wfPath string + if cmd.Flags().Changed("configuration") && len(configPayloadPath) > 0 { + if strings.HasSuffix(strings.ToLower(configPayloadPath), ".json") { + configPayloadDoc, err := os.ReadFile(configPayloadPath) + if err != nil { + return fmt.Errorf("cannot read local configuration file %v: %w", configPayloadPath, err) + } + err = json.Unmarshal(configPayloadDoc, &payloadData) + if err != nil { + return fmt.Errorf("cannot unmarshal local configuration file %v: %w", configPayloadPath, err) + } + } else { + if strings.HasPrefix(configPayloadPath, HLCONFIG_PATH_PREFIX+HLCONFIG_COMPONENT_PREFIX) { + configPayloadPath = strings.TrimPrefix(configPayloadPath, HLCONFIG_PATH_PREFIX+HLCONFIG_COMPONENT_PREFIX) + } + configPayloadDoc, err := apricot.Instance().GetRuntimeEntry(HLCONFIG_COMPONENT_PREFIX, configPayloadPath) + if err != nil { + return fmt.Errorf("cannot retrieve file %v from "+HLCONFIG_PATH_PREFIX+HLCONFIG_COMPONENT_PREFIX+": %w", configPayloadPath, err) + } + err = json.Unmarshal([]byte(configPayloadDoc), &payloadData) + if err != nil { + return fmt.Errorf("cannot unmarshal remote configuration payload %v: %w", configPayloadPath, err) + } + } + + if cmd.Flags().Changed("workflow-template") && len(userWfPath) == 0 { + if len(payloadData.Workflow) > 0 { + wfPath = payloadData.Workflow + } else { + return errors.New("empty workflow template in configuration payload, and empty workflow template path provided") + } + } else if cmd.Flags().Changed("workflow-template") && len(userWfPath) > 0 { + wfPath = userWfPath + } else { + if len(payloadData.Workflow) > 0 { + wfPath = payloadData.Workflow + } else { + return errors.New("no workflow template provided in configuration payload") + } + } + } else { + if cmd.Flags().Changed("workflow-template") && len(userWfPath) > 0 { + wfPath = userWfPath + } else { + return errors.New("empty workflow template path provided") + } + } + + var extraVars string + extraVars, err = cmd.Flags().GetString("extra-vars") if err != nil { return } - if len(wfPath) == 0 { - err = errors.New("cannot create empty environment") + + extraVars = strings.TrimSpace(extraVars) + if cmd.Flags().Changed("extra-vars") && len(extraVars) == 0 { + err = errors.New("empty list of extra-vars supplied") return } + userExtraVarsMap, err := utils.ParseExtraVars(extraVars) + if err != nil { + return + } + + extraVarsMap := make(map[string]string, 0) + for k, v := range userExtraVarsMap { + extraVarsMap[k] = v + } + if cmd.Flags().Changed("configuration") && len(configPayloadPath) > 0 { + for k, v := range payloadData.Vars { + if _, exists := extraVarsMap[k]; !exists { + extraVarsMap[k] = v + } + } + } + + public, _ := cmd.Flags().GetBool("public") + + auto, _ := cmd.Flags().GetBool("auto") + if auto { + // subscribe to core to receive events + id := xid.New().String() + stream, err := rpc.Subscribe(context.TODO(), &pb.SubscribeRequest{Id: id}, grpc.EmptyCallOption{}) + if err != nil { + log.WithPrefix("Subscribe"). + WithError(err). + Fatal("command finished with error") + } + _, err = rpc.NewAutoEnvironment(cxt, &pb.NewAutoEnvironmentRequest{ + WorkflowTemplate: wfPath, + Vars: extraVarsMap, + Id: id, + RequestUser: &commonpb.User{ + Name: getUserAndHost(), + }}, grpc.EmptyCallOption{}) + if err != nil { + return err + } + for { + rcv, err := stream.Recv() + if err != nil { + if err == io.EOF { + break + } + log.WithPrefix("Subscribe"). + WithError(err). + Fatal("command finished with error") + } + if evt := rcv.GetEnvironmentEvent(); evt != nil { + if evt.Error != "" { + if viper.GetBool("verbose") { + log.WithPrefix("Event"). + WithError(fmt.Errorf(evt.Error)). + Error(evt.EnvironmentId) + return nil + } + fmt.Printf("\nEnvironment with id %s failed with error: %s\n", evt.EnvironmentId, evt.Error) + } else { + tmpl, err := template.New("envEvents").Parse("Enviroment {{.EnvironmentId}} {{if .Message}}{{.Message}} {{end}}{{if .State}}changed state to {{.State}}{{end}}{{if .CurrentRunNumber}} with run number {{.CurrentRunNumber}}{{end}}\n") + if err != nil { + return err + } + err = tmpl.Execute(os.Stdout, evt) + if err != nil { + return err + } + } + } + if viper.GetBool("verbose") { + log.WithPrefix("Event"). + Info(rcv) + } + if evt := rcv.GetTaskEvent(); evt != nil { + tmpl, err := template.New("taskEvents").Parse("Task {{.Taskid}} of class {{.ClassName}} changed{{if .State}} state to {{.State}}{{end}}{{if .Status}} status to {{.Status}}{{end}} on machine {{.Hostname}}\n") + if err != nil { + return err + } + err = tmpl.Execute(os.Stdout, evt) + if err != nil { + return err + } + } + } + return nil + } + + // TODO: add support for setting visibility here OCTRL-178 + // TODO: add support for acquiring bot config here OCTRL-177 + + asynchronous, _ := cmd.Flags().GetBool("asynchronous") + var response *pb.NewEnvironmentReply - response, err = rpc.NewEnvironment(cxt, &pb.NewEnvironmentRequest{WorkflowTemplate: wfPath}, grpc.EmptyCallOption{}) + if asynchronous { + response, err = rpc.NewEnvironmentAsync(cxt, &pb.NewEnvironmentRequest{ + WorkflowTemplate: wfPath, + Vars: extraVarsMap, + Public: public, + RequestUser: &commonpb.User{ + Name: getUserAndHost(), + }, + }, grpc.EmptyCallOption{}) + } else { + response, err = rpc.NewEnvironment(cxt, &pb.NewEnvironmentRequest{ + WorkflowTemplate: wfPath, + Vars: extraVarsMap, + Public: public, + RequestUser: &commonpb.User{ + Name: getUserAndHost(), + }, + }, grpc.EmptyCallOption{}) + } if err != nil { return } env := response.GetEnvironment() tasks := env.GetTasks() - fmt.Fprintf(o, "new environment created with %s tasks\n", blue(len(tasks))) - fmt.Fprintf(o, "environment id: %s\n", grey(env.GetId())) - fmt.Fprintf(o, "state: %s\n", colorState(env.GetState())) - fmt.Fprintf(o, "root role: %s\n", env.GetRootRole()) + _, _ = fmt.Fprintf(o, "new environment created with %s tasks\n", blue(len(tasks))) + _, _ = fmt.Fprintf(o, "environment id: %s\n", grey(env.GetId())) + _, _ = fmt.Fprintf(o, "workflow template: %s\n", env.GetRootRole()) + _, _ = fmt.Fprintf(o, "description: %s\n", env.GetDescription()) + _, _ = fmt.Fprintf(o, "state: %s\n", colorState(env.GetState())) + _, _ = fmt.Fprintf(o, "public: %v\n", response.Public) + _, _ = fmt.Fprintf(o, "tasks active: %v\n", env.GetNumberOfActiveTasks()) + _, _ = fmt.Fprintf(o, "tasks inactive: %v\n", env.GetNumberOfInactiveTasks()) + _, _ = fmt.Fprintf(o, "tasks total: %v\n", env.GetNumberOfTasks()) + + var ( + defaultsStr = stringMapToString(env.Defaults, "\t") + varsStr = stringMapToString(env.Vars, "\t") + userVarsStr = stringMapToString(env.UserVars, "\t") + ) + if len(defaultsStr) != 0 { + _, _ = fmt.Fprintf(o, "global defaults:\n%s\n", defaultsStr) + } + if len(varsStr) != 0 { + _, _ = fmt.Fprintf(o, "global variables:\n%s\n", varsStr) + } + if len(userVarsStr) != 0 { + _, _ = fmt.Fprintf(o, "user-provided variables:\n%s\n", userVarsStr) + } return } +func stringMapToString(stringMap map[string]string, indent string) string { + if len(stringMap) == 0 { + return "" + } + accumulator := make(sort.StringSlice, len(stringMap)) + i := 0 + for k, v := range stringMap { + value := v + if len(v) == 0 { + value = "" + } + accumulator[i] = indent + k + ": " + value + i++ + } + accumulator.Sort() + return strings.Join(accumulator, "\n") +} func ShowEnvironment(cxt context.Context, rpc *coconut.RpcClient, cmd *cobra.Command, args []string, o io.Writer) (err error) { if len(args) != 1 { @@ -205,9 +515,13 @@ func ShowEnvironment(cxt context.Context, rpc *coconut.RpcClient, cmd *cobra.Com if err != nil { return } + printIntegratedServicesData, err := cmd.Flags().GetBool("services") + if err != nil { + return + } var response *pb.GetEnvironmentReply - response, err = rpc.GetEnvironment(cxt, &pb.GetEnvironmentRequest{Id: args[0]}, grpc.EmptyCallOption{}) + response, err = rpc.GetEnvironment(cxt, &pb.GetEnvironmentRequest{Id: args[0], ShowWorkflowTree: true}, grpc.EmptyCallOption{}) if err != nil { return } @@ -216,33 +530,66 @@ func ShowEnvironment(cxt context.Context, rpc *coconut.RpcClient, cmd *cobra.Com tasks := env.GetTasks() rnString := formatRunNumber(env.GetCurrentRunNumber()) + var ( + defaultsStr = stringMapToString(env.Defaults, "\t") + varsStr = stringMapToString(env.Vars, "\t") + userVarsStr = stringMapToString(env.UserVars, "\t") + ) + _, _ = fmt.Fprintf(o, "environment id: %s\n", env.GetId()) - _, _ = fmt.Fprintf(o, "created: %s\n", formatTimestamp(env.GetCreatedWhen())) + _, _ = fmt.Fprintf(o, "workflow template: %s\n", env.GetRootRole()) + _, _ = fmt.Fprintf(o, "description: %s\n", env.GetDescription()) + _, _ = fmt.Fprintf(o, "created: %s\n", formatTimestamp(time.Millisecond*time.Duration(env.GetCreatedWhen()))) _, _ = fmt.Fprintf(o, "state: %s\n", colorState(env.GetState())) + if currentTransition := env.GetCurrentTransition(); len(currentTransition) != 0 { + _, _ = fmt.Fprintf(o, "transition: %s\n", currentTransition) + } + _, _ = fmt.Fprintf(o, "public: %t\n", response.Public) _, _ = fmt.Fprintf(o, "run number: %s\n", rnString) + _, _ = fmt.Fprintf(o, "number of FLPs: %s\n", formatNumber(env.GetNumberOfFlps())) + _, _ = fmt.Fprintf(o, "detectors: %s\n", strings.Join(response.GetEnvironment().GetIncludedDetectors(), " ")) + if len(defaultsStr) != 0 { + _, _ = fmt.Fprintf(o, "global defaults:\n%s\n", defaultsStr) + } + if len(varsStr) != 0 { + _, _ = fmt.Fprintf(o, "global variables:\n%s\n", varsStr) + } + if len(userVarsStr) != 0 { + _, _ = fmt.Fprintf(o, "user-provided variables:\n%s\n", userVarsStr) + } if printTasks { - fmt.Fprintln(o, "") + _, _ = fmt.Fprintln(o, "") drawTableShortTaskInfos(tasks, - []string{fmt.Sprintf("task id (%d tasks)", len(tasks)), "class name", "hostname", "status", "state"}, + []string{fmt.Sprintf("task id (%d)", len(tasks)), "class name", "hostname", "crit", "status", "state"}, func(t *pb.ShortTaskInfo) []string { return []string{ t.GetTaskId(), t.GetClassName(), t.GetDeploymentInfo().GetHostname(), + func() string { + if t.GetCritical() { + return "YES" + } + return "NO" + }(), t.GetStatus(), colorState(t.GetState())} }, o) } if printWorkflow { - fmt.Fprintf(o, "\nworkflow:\n") + _, _ = fmt.Fprintf(o, "\nworkflow:\n") drawWorkflow(response.GetWorkflow(), o) } + + if printIntegratedServicesData { + _, _ = fmt.Fprintf(o, "\nintegrated services:\n") + drawIntegratedServicesData(response.GetEnvironment().GetIntegratedServicesData(), o) + } return } - func ControlEnvironment(cxt context.Context, rpc *coconut.RpcClient, cmd *cobra.Command, args []string, o io.Writer) (err error) { if len(args) != 1 { err = errors.New(fmt.Sprintf("accepts 1 arg(s), received %d", len(args))) @@ -253,23 +600,44 @@ func ControlEnvironment(cxt context.Context, rpc *coconut.RpcClient, cmd *cobra. if err != nil { return } + event = strings.ToUpper(event) + switch event { + case "START": + fallthrough + case "STOP": + event = event + "_ACTIVITY" + } var response *pb.ControlEnvironmentReply - response, err = rpc.ControlEnvironment(cxt, &pb.ControlEnvironmentRequest{Id: args[0], Type: pb.ControlEnvironmentRequest_Optype(pb.ControlEnvironmentRequest_Optype_value[event])}, grpc.EmptyCallOption{}) + response, err = rpc.ControlEnvironment(cxt, &pb.ControlEnvironmentRequest{ + Id: args[0], + Type: pb.ControlEnvironmentRequest_Optype(pb.ControlEnvironmentRequest_Optype_value[event]), + RequestUser: &commonpb.User{ + Name: getUserAndHost(), + }, + }, grpc.EmptyCallOption{}) if err != nil { return } rnString := formatRunNumber(response.GetCurrentRunNumber()) + sotTimestamp := time.Unix(0, response.GetStartOfTransition()*int64(time.Millisecond)) + sotFormatted := sotTimestamp.Local().Format("2006-01-02 15:04:05.000000 MST") + eotTimestamp := time.Unix(0, response.GetEndOfTransition()*int64(time.Millisecond)) + eotFormatted := eotTimestamp.Local().Format("2006-01-02 15:04:05.000000 MST") + tdTimestamp := time.Duration(response.GetTransitionDuration() * int64(time.Millisecond)) + _, _ = fmt.Fprintln(o, "transition complete") - _, _ = fmt.Fprintf(o, "environment id: %s\n", response.GetId()) - _, _ = fmt.Fprintf(o, "state: %s\n", colorState(response.GetState())) - _, _ = fmt.Fprintf(o, "run number: %s\n", rnString) + _, _ = fmt.Fprintf(o, "environment id: %s\n", response.GetId()) + _, _ = fmt.Fprintf(o, "state: %s\n", colorState(response.GetState())) + _, _ = fmt.Fprintf(o, "run number: %s\n", rnString) + _, _ = fmt.Fprintf(o, "start of transition: %s\n", grey(sotFormatted)) + _, _ = fmt.Fprintf(o, "end of transition: %s\n", grey(eotFormatted)) + _, _ = fmt.Fprintf(o, "transition duration: %ss\n", grey(strconv.FormatFloat(tdTimestamp.Seconds(), 'f', 6, 64))) return } - func ModifyEnvironment(cxt context.Context, rpc *coconut.RpcClient, cmd *cobra.Command, args []string, o io.Writer) (err error) { if len(args) != 1 { err = errors.New(fmt.Sprintf("accepts 1 arg(s), received %d", len(args))) @@ -298,13 +666,13 @@ func ModifyEnvironment(cxt context.Context, rpc *coconut.RpcClient, cmd *cobra.C ops := make([]*pb.EnvironmentOperation, 0) for _, it := range addRoles { ops = append(ops, &pb.EnvironmentOperation{ - Type: pb.EnvironmentOperation_ADD_ROLE, + Type: pb.EnvironmentOperation_ADD_ROLE, RoleName: it, }) } for _, it := range removeRoles { ops = append(ops, &pb.EnvironmentOperation{ - Type: pb.EnvironmentOperation_REMOVE_ROLE, + Type: pb.EnvironmentOperation_REMOVE_ROLE, RoleName: it, }) } @@ -331,8 +699,8 @@ func ModifyEnvironment(cxt context.Context, rpc *coconut.RpcClient, cmd *cobra.C // Do the request var response *pb.ModifyEnvironmentReply response, err = rpc.ModifyEnvironment(cxt, &pb.ModifyEnvironmentRequest{ - Id: envId, - Operations: ops, + Id: envId, + Operations: ops, ReconfigureAll: reconfigure, }, grpc.EmptyCallOption{}) if err != nil { @@ -350,11 +718,10 @@ func ModifyEnvironment(cxt context.Context, rpc *coconut.RpcClient, cmd *cobra.C } return }() - fmt.Fprintf(o, "failed operations: %s\n", failedOpNames ) + fmt.Fprintf(o, "failed operations: %s\n", failedOpNames) return } - func DestroyEnvironment(cxt context.Context, rpc *coconut.RpcClient, cmd *cobra.Command, args []string, o io.Writer) (err error) { if len(args) != 1 { err = errors.New(fmt.Sprintf("accepts 1 arg(s), received %d", len(args))) @@ -362,26 +729,30 @@ func DestroyEnvironment(cxt context.Context, rpc *coconut.RpcClient, cmd *cobra. } envId := args[0] - // Check current state first - var envResponse *pb.GetEnvironmentReply - envResponse, err = rpc.GetEnvironment(cxt, &pb.GetEnvironmentRequest{Id: envId}, grpc.EmptyCallOption{}) + keepTasks, err := cmd.Flags().GetBool("keep-tasks") if err != nil { - return + keepTasks = false } - allowedState := "CONFIGURED" - if envResponse.GetEnvironment().GetState() != allowedState { - fmt.Fprint(o, "cannot teardown environment\n") - fmt.Fprintf(o, "teardown is allowed in state %s, but environment %s is in state %s\n", allowedState, envId, envResponse.GetEnvironment().GetState()) - return + allowInRunningState, err := cmd.Flags().GetBool("allow-in-running-state") + if err != nil { + keepTasks = false } - keepTasks, err := cmd.Flags().GetBool("keep-tasks") + force, err := cmd.Flags().GetBool("force") if err != nil { keepTasks = false } - _, err = rpc.DestroyEnvironment(cxt, &pb.DestroyEnvironmentRequest{Id: envId, KeepTasks: keepTasks}, grpc.EmptyCallOption{}) + _, err = rpc.DestroyEnvironment(cxt, &pb.DestroyEnvironmentRequest{ + Id: envId, + KeepTasks: keepTasks, + AllowInRunningState: allowInRunningState, + Force: force, + RequestUser: &commonpb.User{ + Name: getUserAndHost(), + }, + }, grpc.EmptyCallOption{}) if err != nil { return } @@ -391,7 +762,6 @@ func DestroyEnvironment(cxt context.Context, rpc *coconut.RpcClient, cmd *cobra. return } - func GetTasks(cxt context.Context, rpc *coconut.RpcClient, cmd *cobra.Command, args []string, o io.Writer) (err error) { var response *pb.GetTasksReply response, err = rpc.GetTasks(cxt, &pb.GetTasksRequest{}, grpc.EmptyCallOption{}) @@ -405,26 +775,34 @@ func GetTasks(cxt context.Context, rpc *coconut.RpcClient, cmd *cobra.Command, a fmt.Fprintln(o, "no tasks running") } else { drawTableShortTaskInfos(tasks, - []string{fmt.Sprintf("task id (%d tasks)", len(tasks)), "class name", "hostname", "locked", "status", "state"}, + []string{fmt.Sprintf("task id (%d)", len(tasks)), "class name", "hostname", "locked", "claimable", "crit", "status", "state", "PID"}, func(t *pb.ShortTaskInfo) []string { return []string{ t.GetTaskId(), t.GetClassName(), t.GetDeploymentInfo().GetHostname(), strconv.FormatBool(t.GetLocked()), + strconv.FormatBool(t.GetClaimable()), + func() string { + if t.GetCritical() { + return "YES" + } + return "NO" + }(), t.GetStatus(), - colorState(t.GetState())} + colorState(t.GetState()), + t.GetPid(), + } }, o) } return nil } - func CleanTasks(cxt context.Context, rpc *coconut.RpcClient, cmd *cobra.Command, args []string, o io.Writer) (err error) { if len(args) > 0 { for _, id := range args { - if !isValidUUID(id) { + if len(id) == 0 { err = errors.New(fmt.Sprintf("%s is not a valid task ID", id)) return } @@ -440,7 +818,7 @@ func CleanTasks(cxt context.Context, rpc *coconut.RpcClient, cmd *cobra.Command, fmt.Fprintln(o, "0 tasks killed") } else { drawTableShortTaskInfos(response.KilledTasks, - []string{fmt.Sprintf("task id (%d tasks killed)", len(response.KilledTasks)), "class name", "hostname"}, + []string{fmt.Sprintf("task id (%d killed)", len(response.KilledTasks)), "class name", "hostname"}, func(t *pb.ShortTaskInfo) []string { return []string{ t.GetTaskId(), @@ -454,7 +832,6 @@ func CleanTasks(cxt context.Context, rpc *coconut.RpcClient, cmd *cobra.Command, return } - func QueryRoles(cxt context.Context, rpc *coconut.RpcClient, cmd *cobra.Command, args []string, o io.Writer) (err error) { if len(args) != 2 { err = errors.New(fmt.Sprintf("accepts 2 arg(s), received %d", len(args))) @@ -475,12 +852,30 @@ func QueryRoles(cxt context.Context, rpc *coconut.RpcClient, cmd *cobra.Command, fmt.Fprintln(o, "no roles found") } else { for i, root := range roots { - fmt.Fprintf(o, "(%s)\n", yellow(i)) - fmt.Fprintf(o, "role path: %s\n", root.GetFullPath()) - fmt.Fprintf(o, "status: %s\n", root.GetStatus()) - fmt.Fprintf(o, "state: %s\n", root.GetState()) - - fmt.Fprintf(o, "subtree:\n") + var ( + defaultsStr = stringMapToString(root.Defaults, "\t") + varsStr = stringMapToString(root.Vars, "\t") + userVarsStr = stringMapToString(root.UserVars, "\t") + consolidatedStr = stringMapToString(root.ConsolidatedStack, "\t") + ) + + _, _ = fmt.Fprintf(o, "(%s)\n", yellow(i)) + _, _ = fmt.Fprintf(o, "role path: %s\n", root.GetFullPath()) + _, _ = fmt.Fprintf(o, "status: %s\n", root.GetStatus()) + _, _ = fmt.Fprintf(o, "state: %s\n", root.GetState()) + if len(defaultsStr) != 0 { + _, _ = fmt.Fprintf(o, "role defaults:\n%s\n", defaultsStr) + } + if len(varsStr) != 0 { + _, _ = fmt.Fprintf(o, "role variables:\n%s\n", varsStr) + } + if len(userVarsStr) != 0 { + _, _ = fmt.Fprintf(o, "user-provided variables:\n%s\n", userVarsStr) + } + if len(consolidatedStr) != 0 { + _, _ = fmt.Fprintf(o, "consolidated stack:\n%s\n", consolidatedStr) + } + _, _ = fmt.Fprintf(o, "subtree:\n") drawWorkflow(root, o) } } @@ -490,13 +885,89 @@ func QueryRoles(cxt context.Context, rpc *coconut.RpcClient, cmd *cobra.Command, // ListWorkflowTemplates lists the available workflow templates and the git repo on which they reside. func ListWorkflowTemplates(cxt context.Context, rpc *coconut.RpcClient, cmd *cobra.Command, args []string, o io.Writer) (err error) { - if len(args) != 0 { - err = errors.New(fmt.Sprintf("accepts no arg(s), received %d", len(args))) - return err + repoPattern := "" + revisionPattern := "" // Let the API take care of defaults + allBranches := false + allTags := false + allWorkflows := false + showDescription := false + + if len(args) == 0 { + repoPattern, err = cmd.Flags().GetString("repository") + if err != nil { + return + } + + revisionPattern, err = cmd.Flags().GetString("revision") + if err != nil { + return + } + + allBranches, err = cmd.Flags().GetBool("all-branches") + if err != nil { + return + } + + allTags, err = cmd.Flags().GetBool("all-tags") + if err != nil { + return + } + + allWorkflows, err = cmd.Flags().GetBool("all-workflows") + if err != nil { + return + } + + showDescription, err = cmd.Flags().GetBool("show-description") + + if allBranches || allTags { + if revisionPattern != "" { + fmt.Fprintln(o, "Ignoring `--all-{branches,tags}` flags, as a valid revision has been specified") + allBranches = false + allTags = false + } + } + + } else if len(args) == 1 { // If we have an argument, give priority over the flags + simpleRepoRegex := regexp.MustCompile("\\A[^@]+\\z") + repoRevisionRegex := regexp.MustCompile("\\A[^@]+@[^@]+\\z") + + if simpleRepoRegex.MatchString(args[0]) { + repoPattern = args[0] + } else if repoRevisionRegex.MatchString(args[0]) { + slicedArgument := strings.Split(args[0], "@") + repoPattern = slicedArgument[0] + revisionPattern = slicedArgument[1] + } else { + err = errors.New("arguments should be in the form of [repo-pattern](@[revision-pattern])") + return + } + + if checkForFlag, _ := cmd.Flags().GetString("repository"); checkForFlag != "*" { // "*" comes from the flag's default value + fmt.Fprintln(o, "Ignoring `--repo` flag, as a valid argument has been passed ") + } + + if checkForFlag, _ := cmd.Flags().GetString("revision"); checkForFlag != "master" { + fmt.Fprintln(o, "Ignoring `--revision` flag, as a valid argument has been passed") + } + + if checkForFlag, _ := cmd.Flags().GetBool("all-branches"); checkForFlag != false { + fmt.Fprintln(o, "Ignoring `--all-branches` flag, as a valid argument has been passed") + } + + if checkForFlag, _ := cmd.Flags().GetBool("all-tags"); checkForFlag != false { + fmt.Fprintln(o, "Ignoring `--all-tags` flag, as a valid argument has been passed") + } + + } else { + err = errors.New(fmt.Sprintf("expecting one argument or a combination of the --repo and --revision flags, %d args received", len(args))) + + return } var response *pb.GetWorkflowTemplatesReply - response, err = rpc.GetWorkflowTemplates(cxt, &pb.GetWorkflowTemplatesRequest{}, grpc.EmptyCallOption{}) + response, err = rpc.GetWorkflowTemplates(cxt, &pb.GetWorkflowTemplatesRequest{RepoPattern: repoPattern, RevisionPattern: revisionPattern, + AllBranches: allBranches, AllTags: allTags, AllWorkflows: allWorkflows}, grpc.EmptyCallOption{}) if err != nil { return err } @@ -506,20 +977,35 @@ func ListWorkflowTemplates(cxt context.Context, rpc *coconut.RpcClient, cmd *cob fmt.Fprintln(o, "No templates found.") } else { var prevRepo string + var prevRevision string aTree := treeprint.New() aTree.SetValue("Available templates in loaded configuration:") + revBranch := treeprint.New() + for _, tmpl := range templates { - if prevRepo != tmpl.GetRepo() { - fmt.Fprintln(o, aTree.String()) + if prevRepo != tmpl.GetRepo() { // Create the root node of the tree w/ the repo name + fmt.Fprint(o, aTree.String()) aTree = treeprint.New() aTree.SetValue(blue(tmpl.GetRepo())) prevRepo = tmpl.GetRepo() + prevRevision = "" // Reinitialize the previous revision + } + + if prevRevision != tmpl.GetRevision() { // Create the first leaf of the root node w/ the revision name + revBranch = aTree.AddBranch(tmpl.GetRevision) + revBranch.SetValue(yellow("[revision] " + tmpl.GetRevision())) // Otherwise the pointer value was set as the branch's value + prevRevision = tmpl.GetRevision() + } + if showDescription { + tmplBranch := revBranch.AddBranch(tmpl.GetTemplate()) + tmplBranch.AddNode(grey(tmpl.GetDescription())) + } else { + revBranch.AddNode(tmpl.GetTemplate()) } - aTree.AddNode(tmpl.GetTemplate()) } - fmt.Fprintln(o, aTree.String()) + fmt.Fprint(o, aTree.String()) } return nil @@ -543,44 +1029,57 @@ func ListRepos(cxt context.Context, rpc *coconut.RpcClient, cmd *cobra.Command, fmt.Fprintln(o, "No repositories found.") } else { table := tablewriter.NewWriter(o) - table.SetHeader([]string{"id", "repository", "default"}) + table.SetHeader([]string{"id", "repository", "default", "default revision"}) table.SetBorder(false) fg := tablewriter.Colors{tablewriter.Bold, tablewriter.FgBlueColor} - table.SetHeaderColor(fg, fg, fg) + table.SetHeaderColor(fg, fg, fg, fg) + + globalDefaultRevision := response.GetGlobalDefaultRevision() for i, root := range roots { defaultTick := "" + if root.GetDefault() { defaultTick = blue("YES") } - table.Append([]string{strconv.Itoa(i), root.GetName(), defaultTick}) + var defaultRevision string + if root.GetDefaultRevision() == globalDefaultRevision { + defaultRevision = red(globalDefaultRevision) + } else { + defaultRevision = root.GetDefaultRevision() + } + table.Append([]string{strconv.Itoa(i), root.GetName(), defaultTick, defaultRevision}) } fmt.Fprintf(o, "Git repositories used as configuration sources:\n\n") table.Render() + fmt.Fprintf(o, "\nGlobal default revision: %s\n", red(globalDefaultRevision)) } return nil } // AddRepo add a new repository to the available git repositories used for configuration and checks it out. -func AddRepo(cxt context.Context, rpc *coconut.RpcClient, cmd *cobra.Command, args []string, o io.Writer) (err error) { +func AddRepo(cxt context.Context, rpc *coconut.RpcClient, cmd *cobra.Command, args []string, o io.Writer) error { + if len(args) != 1 { - err = errors.New(fmt.Sprintf("accepts 1 arg, received %d", len(args))) - return err + return fmt.Errorf("accepts 1 arg, received %d", len(args)) } - var response *pb.AddRepoReply - response, err = rpc.AddRepo(cxt, &pb.AddRepoRequest{Name: args[0]}, grpc.EmptyCallOption{}) + name := args[0] + defaultRevision, err := cmd.Flags().GetString("default-revision") if err != nil { return err } - if response.GetErrorString() == "" { - fmt.Fprintln(o, "Repository succesfully added.") - } else { - fmt.Fprintln(o, "Cannot add repository:", response.GetErrorString()) + response, err := rpc.AddRepo(cxt, &pb.AddRepoRequest{Name: name, DefaultRevision: defaultRevision}, grpc.EmptyCallOption{}) + if err != nil { + fmt.Fprintln(o, "Cannot add repository.") + return err } + fmt.Fprintln(o, "Repository succesfully added.") + fmt.Fprintln(o, response.GetInfo()) + return nil } @@ -603,13 +1102,9 @@ func RemoveRepo(cxt context.Context, rpc *coconut.RpcClient, cmd *cobra.Command, } newDefaultRepo := response.GetNewDefaultRepo() - if response.GetOk() { - fmt.Fprintln(o, "Repository removed succsefully") - if newDefaultRepo != "" { - fmt.Fprintln(o, "New default repo is: " + newDefaultRepo) - } - } else { - fmt.Fprintln(o, "Repository not found") + fmt.Fprintln(o, "Repository removed successfully.") + if newDefaultRepo != "" { + fmt.Fprintln(o, "New default repo is: "+newDefaultRepo) } return nil @@ -622,28 +1117,23 @@ func RefreshRepos(cxt context.Context, rpc *coconut.RpcClient, cmd *cobra.Comman return err } - var response *pb.RefreshReposReply if len(args) == 0 { - response, err = rpc.RefreshRepos(cxt, &pb.RefreshReposRequest{Index: -1}, grpc.EmptyCallOption{}) + _, err = rpc.RefreshRepos(cxt, &pb.RefreshReposRequest{Index: -1}, grpc.EmptyCallOption{}) } else if len(args) == 1 { index, _ := strconv.ParseInt(args[0], 10, 32) - response, err = rpc.RefreshRepos(cxt, &pb.RefreshReposRequest{Index: int32(index)}, grpc.EmptyCallOption{}) + _, err = rpc.RefreshRepos(cxt, &pb.RefreshReposRequest{Index: int32(index)}, grpc.EmptyCallOption{}) } if err != nil { + fmt.Fprintln(o, "Repository refresh operation failed.") return err } - errorString := response.GetErrorString() - if errorString == "" { - if len(args) == 0 { - fmt.Fprintln(o, "Repositories refreshed succesfully") - } else { - fmt.Fprintln(o, "Repository refreshed succesfully") - } + if len(args) == 0 { + fmt.Fprintln(o, "Repositories refreshed succesfully") } else { - fmt.Fprintln(o, "Repository refresh operation failed:", errorString) + fmt.Fprintln(o, "Repository refreshed succesfully") } return nil @@ -652,26 +1142,61 @@ func RefreshRepos(cxt context.Context, rpc *coconut.RpcClient, cmd *cobra.Comman // SetDefaultRepo selects the default repository based on the indexes reported by ListRepos. // It also updates the backend (consul or file) which holds a record for the default repository // which is persistent across core executions. -func SetDefaultRepo(cxt context.Context, rpc *coconut.RpcClient, cmd *cobra.Command, args []string, o io.Writer) (err error) { +func SetDefaultRepo(cxt context.Context, rpc *coconut.RpcClient, cmd *cobra.Command, args []string, o io.Writer) error { if len(args) != 1 { - err = errors.New(fmt.Sprintf("accepts 1 arg, received %d", len(args))) + err := errors.New(fmt.Sprintf("accepts 1 arg, received %d", len(args))) return err } - index, _ := strconv.ParseInt(args[0], 10, 32) + index, err := strconv.ParseInt(args[0], 10, 32) + if err != nil { + fmt.Fprintln(o, "Wrong argument; should be repository's index") + return err + } - var response *pb.SetDefaultRepoReply - response, err = rpc.SetDefaultRepo(cxt, &pb.SetDefaultRepoRequest{Index: int32(index)}, grpc.EmptyCallOption{}) + _, err = rpc.SetDefaultRepo(cxt, &pb.SetDefaultRepoRequest{Index: int32(index)}, grpc.EmptyCallOption{}) if err != nil { + fmt.Fprintln(o, "Operation failed.") return err } - errorString := response.GetErrorString() - if errorString == "" { - fmt.Fprintln(o, "Default repository updated succesfully") + fmt.Fprintln(o, "Default repository update succesfully") + + return nil +} + +// SetDefaultRevision selects the default repository revision. +// This can be done on the global or on the repository level. +func SetDefaultRevision(cxt context.Context, rpc *coconut.RpcClient, cmd *cobra.Command, args []string, o io.Writer) error { + if len(args) == 1 { // Set global default + _, err := rpc.SetGlobalDefaultRevision(cxt, &pb.SetGlobalDefaultRevisionRequest{Revision: args[0]}, grpc.EmptyCallOption{}) + if err != nil { + fmt.Fprintln(o, "Operation failed.") + return err + } + fmt.Fprintln(o, "The global default revision has been succesfuly updated to \""+args[0]+"\".") + } else if len(args) == 2 { // Set per-repo default + index, err := strconv.ParseInt(args[0], 10, 32) + if err != nil { + fmt.Fprintln(o, "Wrong argument; should be repository's index") + return err + } + + var response *pb.SetRepoDefaultRevisionReply + response, err = rpc.SetRepoDefaultRevision(cxt, &pb.SetRepoDefaultRevisionRequest{Index: int32(index), Revision: args[1]}, grpc.EmptyCallOption{}) + if err != nil { + fmt.Fprintln(o, "Operation failed.") + return err + } else if response.GetInfo() != "" { + fmt.Fprintln(o, "Operation failed.\n") + fmt.Fprintln(o, "Available revisions for this repo: \n"+response.GetInfo()) + return errors.New("Could not update the default revision.") + } + fmt.Fprintln(o, "The default revision for this repo has been successfuly updated to \""+args[1]+"\".") } else { - fmt.Fprintln(o, "Operation failed:", errorString) + err := errors.New(fmt.Sprintf("expects 1 or 2 args, received %d", len(args))) + return err } return nil -} \ No newline at end of file +} diff --git a/coconut/control/controlutil.go b/coconut/control/controlutil.go index 1d71c39ff..5d85a5906 100644 --- a/coconut/control/controlutil.go +++ b/coconut/control/controlutil.go @@ -25,24 +25,29 @@ package control import ( + "encoding/json" "fmt" "io" + "os" + "os/user" "strconv" + "strings" "time" "github.com/AliceO2Group/Control/coconut/protos" "github.com/fatih/color" - "github.com/google/uuid" "github.com/olekukonko/tablewriter" "github.com/xlab/treeprint" + "gopkg.in/yaml.v3" ) -var( - blue = color.New(color.FgHiBlue).SprintFunc() - green = color.New(color.FgHiGreen).SprintFunc() +var ( + blue = color.New(color.FgHiBlue).SprintFunc() + green = color.New(color.FgHiGreen).SprintFunc() yellow = color.New(color.FgHiYellow).SprintFunc() - red = color.New(color.FgHiRed).SprintFunc() - grey = color.New(color.FgWhite).SprintFunc() + red = color.New(color.FgHiRed).SprintFunc() + grey = color.New(color.FgWhite).SprintFunc() + dark = color.New(color.FgHiBlack).SprintFunc() ) func formatRunNumber(rn uint32) string { @@ -79,7 +84,6 @@ func colorGlobalState(st string) string { } } - func colorStateFromNode(node *pb.RoleInfo) string { return colorState(node.GetState()) } @@ -88,7 +92,7 @@ func buildTree(tree *treeprint.Tree, node *pb.RoleInfo, level int) { if len(node.Roles) != 0 { branch := (*tree).AddMetaBranch(colorStateFromNode(node), node.GetName()) for _, n := range node.Roles { - buildTree(&branch, n, level + 1) + buildTree(&branch, n, level+1) } } else { var nodeText string @@ -118,7 +122,38 @@ func drawWorkflow(root *pb.RoleInfo, o io.Writer) { buildTree(&tree, n, 0) } } - fmt.Fprint(o, tree.String()) + _, _ = fmt.Fprint(o, tree.String()) +} + +func drawIntegratedServicesData(services map[string]string, o io.Writer) { + if len(services) == 0 { + return + } + + for serviceName, serviceData := range services { + data := strings.TrimSpace(serviceData) + if len(data) == 0 || data == "null" || data == "{}" { + continue + } + + var serviceDataMap map[string]interface{} + err := json.Unmarshal([]byte(serviceData), &serviceDataMap) + if err != nil { + _, _ = fmt.Fprintf(o, "%s: %s\n", serviceName, serviceData) + + continue + } + + dataYaml, err := yaml.Marshal(serviceDataMap) + if err != nil { + _, _ = fmt.Fprintf(o, "%s:\n%s\n", serviceName, serviceData) + } + + dataYamlLines := strings.Split(string(dataYaml), "\n") + for _, line := range dataYamlLines { + _, _ = fmt.Fprintf(o, "%s%s\n", " ", line) + } + } } type linePrintFunc func(t *pb.ShortTaskInfo) []string @@ -143,18 +178,34 @@ func drawTableShortTaskInfos(tasks []*pb.ShortTaskInfo, headers []string, linePr table.Render() } -func formatTimestamp(rfc3339timestamp string) string { - timestamp, err := time.Parse(time.RFC3339, rfc3339timestamp) - var formatted string - if err == nil { - formatted = timestamp.Local().Format("2006-01-02 15:04:05 MST") +func formatTimestamp(timestamp time.Duration) string { + time := time.Unix(int64(timestamp.Seconds()), 0) + return time.Local().Format("2006-01-02 15:04:05 MST") +} + +func formatNumber(numberOfMachines int32) string { + nMString := strconv.FormatInt(int64(numberOfMachines), 10) + if numberOfMachines == 0 { + nMString = grey("none") } else { - formatted = "unknown" + nMString = green(nMString) } - return formatted + return nMString } -func isValidUUID(u string) bool { - _, err := uuid.Parse(u) - return err == nil +func getUserAndHost() string { + userName := "unknown" + hostName := "unknown" + + currentUser, err := user.Current() + if err == nil { + userName = currentUser.Username + } + + currentHost, err := os.Hostname() + if err == nil { + hostName = currentHost + } + + return fmt.Sprintf("%s@%s", userName, hostName) } diff --git a/coconut/doc/coconut.md b/coconut/doc/coconut.md index 68d26a33d..697d659a9 100644 --- a/coconut/doc/coconut.md +++ b/coconut/doc/coconut.md @@ -13,9 +13,11 @@ For more information on the available commands, see the individual documentation ``` --config string optional configuration file for coconut (default $HOME/.config/coconut/settings.yaml) - --config_endpoint string configuration endpoint used by AliECS core as PROTO://HOST:PORT (default "consul://127.0.0.1:8500") - --endpoint string AliECS core endpoint as HOST:PORT (default "127.0.0.1:47102") + --config_endpoint string configuration endpoint used by AliECS core as PROTO://HOST:PORT (default "apricot://127.0.0.1:32101") + --endpoint string AliECS core endpoint as HOST:PORT (default "127.0.0.1:32102") -h, --help help for coconut + --nocolor disable colors in output + --nospinner disable animations in output -v, --verbose show verbose output for debug purposes ``` @@ -30,4 +32,4 @@ For more information on the available commands, see the individual documentation * [coconut task](coconut_task.md) - manage active tasks * [coconut template](coconut_template.md) - query available workflow templates in configuration repositories -###### Auto generated by spf13/cobra on 26-Aug-2019 +###### Auto generated by spf13/cobra on 27-Nov-2024 diff --git a/coconut/doc/coconut_about.md b/coconut/doc/coconut_about.md index 719c1d476..8c0730d53 100644 --- a/coconut/doc/coconut_about.md +++ b/coconut/doc/coconut_about.md @@ -20,8 +20,10 @@ coconut about [flags] ``` --config string optional configuration file for coconut (default $HOME/.config/coconut/settings.yaml) - --config_endpoint string configuration endpoint used by AliECS core as PROTO://HOST:PORT (default "consul://127.0.0.1:8500") - --endpoint string AliECS core endpoint as HOST:PORT (default "127.0.0.1:47102") + --config_endpoint string configuration endpoint used by AliECS core as PROTO://HOST:PORT (default "apricot://127.0.0.1:32101") + --endpoint string AliECS core endpoint as HOST:PORT (default "127.0.0.1:32102") + --nocolor disable colors in output + --nospinner disable animations in output -v, --verbose show verbose output for debug purposes ``` @@ -29,4 +31,4 @@ coconut about [flags] * [coconut](coconut.md) - O² Control and Configuration Utility -###### Auto generated by spf13/cobra on 26-Aug-2019 +###### Auto generated by spf13/cobra on 27-Nov-2024 diff --git a/coconut/doc/coconut_configuration.md b/coconut/doc/coconut_configuration.md index e9befee57..133d4db44 100644 --- a/coconut/doc/coconut_configuration.md +++ b/coconut/doc/coconut_configuration.md @@ -17,8 +17,10 @@ configuration store. ``` --config string optional configuration file for coconut (default $HOME/.config/coconut/settings.yaml) - --config_endpoint string configuration endpoint used by AliECS core as PROTO://HOST:PORT (default "consul://127.0.0.1:8500") - --endpoint string AliECS core endpoint as HOST:PORT (default "127.0.0.1:47102") + --config_endpoint string configuration endpoint used by AliECS core as PROTO://HOST:PORT (default "apricot://127.0.0.1:32101") + --endpoint string AliECS core endpoint as HOST:PORT (default "127.0.0.1:32102") + --nocolor disable colors in output + --nospinner disable animations in output -v, --verbose show verbose output for debug purposes ``` @@ -26,5 +28,8 @@ configuration store. * [coconut](coconut.md) - O² Control and Configuration Utility * [coconut configuration dump](coconut_configuration_dump.md) - dump configuration subtree +* [coconut configuration import](coconut_configuration_import.md) - Import a configuration file for the specified component and entry +* [coconut configuration list](coconut_configuration_list.md) - List all existing O² components in Consul +* [coconut configuration show](coconut_configuration_show.md) - Show configuration for the component and entry specified -###### Auto generated by spf13/cobra on 26-Aug-2019 +###### Auto generated by spf13/cobra on 27-Nov-2024 diff --git a/coconut/doc/coconut_configuration_dump.md b/coconut/doc/coconut_configuration_dump.md index 57dc53399..d49696b8c 100644 --- a/coconut/doc/coconut_configuration_dump.md +++ b/coconut/doc/coconut_configuration_dump.md @@ -6,7 +6,9 @@ dump configuration subtree The configuration dump command requests from O² Configuration a subtree of key-values, and dumps it to standard output in the specified -format. +format. This command has full read access to the O² Configuration store +and performs a raw query with no additional processing or access control +semantics. ``` coconut configuration dump [key] [flags] @@ -23,8 +25,10 @@ coconut configuration dump [key] [flags] ``` --config string optional configuration file for coconut (default $HOME/.config/coconut/settings.yaml) - --config_endpoint string configuration endpoint used by AliECS core as PROTO://HOST:PORT (default "consul://127.0.0.1:8500") - --endpoint string AliECS core endpoint as HOST:PORT (default "127.0.0.1:47102") + --config_endpoint string configuration endpoint used by AliECS core as PROTO://HOST:PORT (default "apricot://127.0.0.1:32101") + --endpoint string AliECS core endpoint as HOST:PORT (default "127.0.0.1:32102") + --nocolor disable colors in output + --nospinner disable animations in output -v, --verbose show verbose output for debug purposes ``` @@ -32,4 +36,4 @@ coconut configuration dump [key] [flags] * [coconut configuration](coconut_configuration.md) - view or modify O² configuration -###### Auto generated by spf13/cobra on 26-Aug-2019 +###### Auto generated by spf13/cobra on 27-Nov-2024 diff --git a/coconut/doc/coconut_configuration_import.md b/coconut/doc/coconut_configuration_import.md new file mode 100644 index 000000000..c811015c2 --- /dev/null +++ b/coconut/doc/coconut_configuration_import.md @@ -0,0 +1,54 @@ +## coconut configuration import + +Import a configuration file for the specified component and entry + +### Synopsis + +The configuration import command generates a timestamp and saves +the configuration file to Consul under the / path. +Supported configuration file types are JSON, YAML, TOML and INI, +and their file extensions are recognized automatically. + +``` +coconut configuration import [flags] +``` + +### Examples + +``` +coconut conf import +coconut conf import /// +coconut conf import --new-component +coconut conf import /// --format=json +coconut conf import .json +coconut conf import +coconut conf import --new-component + +``` + +### Options + +``` + -f, --format string force a specific configuration file type, overriding any file extension + -h, --help help for import + -n, --new-component create a new configuration component while importing entry + -l, --role string request configuration for this O² machine role + -r, --runtype string request configuration for this run type (e.g. PHYSICS, TECHNICAL, etc.) +``` + +### Options inherited from parent commands + +``` + --config string optional configuration file for coconut (default $HOME/.config/coconut/settings.yaml) + --config_endpoint string configuration endpoint used by AliECS core as PROTO://HOST:PORT (default "apricot://127.0.0.1:32101") + --endpoint string AliECS core endpoint as HOST:PORT (default "127.0.0.1:32102") + --nocolor disable colors in output + --nospinner disable animations in output + -v, --verbose show verbose output for debug purposes +``` + +### SEE ALSO + +* [coconut configuration](coconut_configuration.md) - view or modify O² configuration + +###### Auto generated by spf13/cobra on 27-Nov-2024 diff --git a/coconut/doc/coconut_configuration_list.md b/coconut/doc/coconut_configuration_list.md new file mode 100644 index 000000000..65c1d6feb --- /dev/null +++ b/coconut/doc/coconut_configuration_list.md @@ -0,0 +1,45 @@ +## coconut configuration list + +List all existing O² components in Consul + +### Synopsis + +The configuration list command requests all components +from O² Configuration as a list and displays it on the standard output + +``` +coconut configuration list [component] [flags] +``` + +### Examples + +``` +coconut conf list +coconut conf list +coconut conf list -t +``` + +### Options + +``` + -h, --help help for list + -o, --output string output format for the configuration list (yaml/json) (default "yaml") + -t, --timestamp display latest timestamp entries for the requested component +``` + +### Options inherited from parent commands + +``` + --config string optional configuration file for coconut (default $HOME/.config/coconut/settings.yaml) + --config_endpoint string configuration endpoint used by AliECS core as PROTO://HOST:PORT (default "apricot://127.0.0.1:32101") + --endpoint string AliECS core endpoint as HOST:PORT (default "127.0.0.1:32102") + --nocolor disable colors in output + --nospinner disable animations in output + -v, --verbose show verbose output for debug purposes +``` + +### SEE ALSO + +* [coconut configuration](coconut_configuration.md) - view or modify O² configuration + +###### Auto generated by spf13/cobra on 27-Nov-2024 diff --git a/coconut/doc/coconut_configuration_show.md b/coconut/doc/coconut_configuration_show.md new file mode 100644 index 000000000..0776f4cbb --- /dev/null +++ b/coconut/doc/coconut_configuration_show.md @@ -0,0 +1,53 @@ +## coconut configuration show + +Show configuration for the component and entry specified + +### Synopsis + +The configuration show command returns the most recent +configuration revision for the specified component and entry. +It can also return a specific revision, requested with the --timestamp/-t flag + +``` +coconut configuration show [flags] +``` + +### Examples + +``` +coconut conf show +coconut conf show -t +coconut conf show /// +coconut conf show /// -t +coconut conf show ///@ +coconut conf show -r -l -t ' +coconut conf show -s -e '{"key1": "value1", "key2": "value2"}' +``` + +### Options + +``` + -e, --extra-vars key1=val1,key2=val2 values passed using key=value CSV or JSON syntax, interpreted as strings key1=val1,key2=val2 or `{"key1": "value1", "key2": "value2"}` + -h, --help help for show + -l, --role string request configuration for this O² machine role + -r, --runtype string request configuration for this run type (e.g. PHYSICS, TECHNICAL, etc.) + -s, --simulate simulate runtime template processing on the configuration payload + -t, --timestamp string request configuration for this timestamp +``` + +### Options inherited from parent commands + +``` + --config string optional configuration file for coconut (default $HOME/.config/coconut/settings.yaml) + --config_endpoint string configuration endpoint used by AliECS core as PROTO://HOST:PORT (default "apricot://127.0.0.1:32101") + --endpoint string AliECS core endpoint as HOST:PORT (default "127.0.0.1:32102") + --nocolor disable colors in output + --nospinner disable animations in output + -v, --verbose show verbose output for debug purposes +``` + +### SEE ALSO + +* [coconut configuration](coconut_configuration.md) - view or modify O² configuration + +###### Auto generated by spf13/cobra on 27-Nov-2024 diff --git a/coconut/doc/coconut_environment.md b/coconut/doc/coconut_environment.md index b26d6947e..e1575dd80 100644 --- a/coconut/doc/coconut_environment.md +++ b/coconut/doc/coconut_environment.md @@ -37,8 +37,10 @@ For more information on the behavior of coconut environments, see the subcommand ``` --config string optional configuration file for coconut (default $HOME/.config/coconut/settings.yaml) - --config_endpoint string configuration endpoint used by AliECS core as PROTO://HOST:PORT (default "consul://127.0.0.1:8500") - --endpoint string AliECS core endpoint as HOST:PORT (default "127.0.0.1:47102") + --config_endpoint string configuration endpoint used by AliECS core as PROTO://HOST:PORT (default "apricot://127.0.0.1:32101") + --endpoint string AliECS core endpoint as HOST:PORT (default "127.0.0.1:32102") + --nocolor disable colors in output + --nospinner disable animations in output -v, --verbose show verbose output for debug purposes ``` @@ -51,4 +53,4 @@ For more information on the behavior of coconut environments, see the subcommand * [coconut environment list](coconut_environment_list.md) - list environments * [coconut environment show](coconut_environment_show.md) - show environment information -###### Auto generated by spf13/cobra on 26-Aug-2019 +###### Auto generated by spf13/cobra on 27-Nov-2024 diff --git a/coconut/doc/coconut_environment_control.md b/coconut/doc/coconut_environment_control.md index 7d9d766d5..57417d5a7 100644 --- a/coconut/doc/coconut_environment_control.md +++ b/coconut/doc/coconut_environment_control.md @@ -30,8 +30,10 @@ coconut environment control [environment id] [flags] ``` --config string optional configuration file for coconut (default $HOME/.config/coconut/settings.yaml) - --config_endpoint string configuration endpoint used by AliECS core as PROTO://HOST:PORT (default "consul://127.0.0.1:8500") - --endpoint string AliECS core endpoint as HOST:PORT (default "127.0.0.1:47102") + --config_endpoint string configuration endpoint used by AliECS core as PROTO://HOST:PORT (default "apricot://127.0.0.1:32101") + --endpoint string AliECS core endpoint as HOST:PORT (default "127.0.0.1:32102") + --nocolor disable colors in output + --nospinner disable animations in output -v, --verbose show verbose output for debug purposes ``` @@ -39,4 +41,4 @@ coconut environment control [environment id] [flags] * [coconut environment](coconut_environment.md) - create, destroy and manage AliECS environments -###### Auto generated by spf13/cobra on 26-Aug-2019 +###### Auto generated by spf13/cobra on 27-Nov-2024 diff --git a/coconut/doc/coconut_environment_create.md b/coconut/doc/coconut_environment_create.md index a1f04d05d..62ec30471 100644 --- a/coconut/doc/coconut_environment_create.md +++ b/coconut/doc/coconut_environment_create.md @@ -13,10 +13,14 @@ A valid workflow template (sometimes called simply "workflow" for brevity) must Workflows and tasks are managed with a git based configuration system, so the workflow template may be provided simply by name or with repository and branch/tag/hash constraints. Examples: + * `coconut env create -w myworkflow` - loads workflow `myworkflow` from default configuration repository at HEAD of master branch * `coconut env create -w github.com/AliceO2Group/MyConfRepo/myworkflow` - loads a workflow from a specific git repository, HEAD of master branch - * `myworkflow@rev` - loads a workflow from default repository, on branch, tag or revision `rev` + * `coconut env create -w myworkflow@rev` - loads a workflow from default repository, on branch, tag or revision `rev` * `coconut env create -w github.com/AliceO2Group/MyConfRepo/myworkflow@rev` - loads a workflow from a specific git repository, on branch, tag or revision `rev` + * `coconut env create -c /home/myrepo/myconfigfile.json -e '{"hosts":"[\"my-test-machine\"]"}'` + * `coconut env create -c consul:///o2/runtime/COG-v1/TECHNICAL -w readout-dataflow@myBranch -e '{"hosts":"[\"my-test-machine\"]"}'` + * `coconut env create -c TECHNICAL -e '{"hosts":"[\"my-test-machine\"]"}'` For more information on the AliECS workflow configuration system, see documentation for the `coconut repository` command. @@ -27,16 +31,23 @@ coconut environment create [flags] ### Options ``` - -h, --help help for create - -w, --workflow-template string workflow to be loaded in the new environment + -y, --asynchronous use asynchronous mode for environment creation + -a, --auto create an autorun environment + -c, --configuration string high-level configuration payload to be loaded for the new environment + -e, --extra-vars key1=val1,key2=val2 values passed using key=value CSV or JSON syntax, interpreted as strings key1=val1,key2=val2 or `{"key1": "value1", "key2": "value2"}` + -h, --help help for create + -p, --public control public rights of the environment (default true) + -w, --workflow-template string workflow to be loaded in the new environment ``` ### Options inherited from parent commands ``` --config string optional configuration file for coconut (default $HOME/.config/coconut/settings.yaml) - --config_endpoint string configuration endpoint used by AliECS core as PROTO://HOST:PORT (default "consul://127.0.0.1:8500") - --endpoint string AliECS core endpoint as HOST:PORT (default "127.0.0.1:47102") + --config_endpoint string configuration endpoint used by AliECS core as PROTO://HOST:PORT (default "apricot://127.0.0.1:32101") + --endpoint string AliECS core endpoint as HOST:PORT (default "127.0.0.1:32102") + --nocolor disable colors in output + --nospinner disable animations in output -v, --verbose show verbose output for debug purposes ``` @@ -44,4 +55,4 @@ coconut environment create [flags] * [coconut environment](coconut_environment.md) - create, destroy and manage AliECS environments -###### Auto generated by spf13/cobra on 26-Aug-2019 +###### Auto generated by spf13/cobra on 27-Nov-2024 diff --git a/coconut/doc/coconut_environment_destroy.md b/coconut/doc/coconut_environment_destroy.md index e4b4611a5..c0747e824 100644 --- a/coconut/doc/coconut_environment_destroy.md +++ b/coconut/doc/coconut_environment_destroy.md @@ -17,16 +17,20 @@ coconut environment destroy [environment id] [flags] ### Options ``` - -h, --help help for destroy - -k, --keep-tasks keep tasks active after destroying the environment + -a, --allow-in-running-state allows destroying an environment while in Running state + -f, --force force destroy of an environment + -h, --help help for destroy + -k, --keep-tasks keep tasks active after destroying the environment ``` ### Options inherited from parent commands ``` --config string optional configuration file for coconut (default $HOME/.config/coconut/settings.yaml) - --config_endpoint string configuration endpoint used by AliECS core as PROTO://HOST:PORT (default "consul://127.0.0.1:8500") - --endpoint string AliECS core endpoint as HOST:PORT (default "127.0.0.1:47102") + --config_endpoint string configuration endpoint used by AliECS core as PROTO://HOST:PORT (default "apricot://127.0.0.1:32101") + --endpoint string AliECS core endpoint as HOST:PORT (default "127.0.0.1:32102") + --nocolor disable colors in output + --nospinner disable animations in output -v, --verbose show verbose output for debug purposes ``` @@ -34,4 +38,4 @@ coconut environment destroy [environment id] [flags] * [coconut environment](coconut_environment.md) - create, destroy and manage AliECS environments -###### Auto generated by spf13/cobra on 26-Aug-2019 +###### Auto generated by spf13/cobra on 27-Nov-2024 diff --git a/coconut/doc/coconut_environment_list.md b/coconut/doc/coconut_environment_list.md index d83dd0d18..17a5da988 100644 --- a/coconut/doc/coconut_environment_list.md +++ b/coconut/doc/coconut_environment_list.md @@ -14,15 +14,18 @@ coconut environment list [flags] ### Options ``` - -h, --help help for list + -h, --help help for list + -s, --show-all print public and non-public environments ``` ### Options inherited from parent commands ``` --config string optional configuration file for coconut (default $HOME/.config/coconut/settings.yaml) - --config_endpoint string configuration endpoint used by AliECS core as PROTO://HOST:PORT (default "consul://127.0.0.1:8500") - --endpoint string AliECS core endpoint as HOST:PORT (default "127.0.0.1:47102") + --config_endpoint string configuration endpoint used by AliECS core as PROTO://HOST:PORT (default "apricot://127.0.0.1:32101") + --endpoint string AliECS core endpoint as HOST:PORT (default "127.0.0.1:32102") + --nocolor disable colors in output + --nospinner disable animations in output -v, --verbose show verbose output for debug purposes ``` @@ -30,4 +33,4 @@ coconut environment list [flags] * [coconut environment](coconut_environment.md) - create, destroy and manage AliECS environments -###### Auto generated by spf13/cobra on 26-Aug-2019 +###### Auto generated by spf13/cobra on 27-Nov-2024 diff --git a/coconut/doc/coconut_environment_show.md b/coconut/doc/coconut_environment_show.md index 59dcabc27..d33f8e4c8 100644 --- a/coconut/doc/coconut_environment_show.md +++ b/coconut/doc/coconut_environment_show.md @@ -15,6 +15,7 @@ coconut environment show [environment id] [flags] ``` -h, --help help for show + -s, --services print information on this environment from integrated services -t, --tasks print a list of tasks in this environment -w, --workflow print the workflow tree ``` @@ -23,8 +24,10 @@ coconut environment show [environment id] [flags] ``` --config string optional configuration file for coconut (default $HOME/.config/coconut/settings.yaml) - --config_endpoint string configuration endpoint used by AliECS core as PROTO://HOST:PORT (default "consul://127.0.0.1:8500") - --endpoint string AliECS core endpoint as HOST:PORT (default "127.0.0.1:47102") + --config_endpoint string configuration endpoint used by AliECS core as PROTO://HOST:PORT (default "apricot://127.0.0.1:32101") + --endpoint string AliECS core endpoint as HOST:PORT (default "127.0.0.1:32102") + --nocolor disable colors in output + --nospinner disable animations in output -v, --verbose show verbose output for debug purposes ``` @@ -32,4 +35,4 @@ coconut environment show [environment id] [flags] * [coconut environment](coconut_environment.md) - create, destroy and manage AliECS environments -###### Auto generated by spf13/cobra on 26-Aug-2019 +###### Auto generated by spf13/cobra on 27-Nov-2024 diff --git a/coconut/doc/coconut_info.md b/coconut/doc/coconut_info.md index a7b3d3314..0f45e3d5e 100644 --- a/coconut/doc/coconut_info.md +++ b/coconut/doc/coconut_info.md @@ -21,8 +21,10 @@ coconut info [flags] ``` --config string optional configuration file for coconut (default $HOME/.config/coconut/settings.yaml) - --config_endpoint string configuration endpoint used by AliECS core as PROTO://HOST:PORT (default "consul://127.0.0.1:8500") - --endpoint string AliECS core endpoint as HOST:PORT (default "127.0.0.1:47102") + --config_endpoint string configuration endpoint used by AliECS core as PROTO://HOST:PORT (default "apricot://127.0.0.1:32101") + --endpoint string AliECS core endpoint as HOST:PORT (default "127.0.0.1:32102") + --nocolor disable colors in output + --nospinner disable animations in output -v, --verbose show verbose output for debug purposes ``` @@ -30,4 +32,4 @@ coconut info [flags] * [coconut](coconut.md) - O² Control and Configuration Utility -###### Auto generated by spf13/cobra on 26-Aug-2019 +###### Auto generated by spf13/cobra on 27-Nov-2024 diff --git a/coconut/doc/coconut_repository.md b/coconut/doc/coconut_repository.md index 45c855c0e..7ba96cc14 100644 --- a/coconut/doc/coconut_repository.md +++ b/coconut/doc/coconut_repository.md @@ -8,25 +8,33 @@ The repository command performs operations on the repositories used for task and A valid workflow configuration repository must contain the directories `tasks` and `workflows` in its `master` branch. -When referencing a repository, the clone method should never be prepended. Examples of valid repository identifiers: +When referencing a repository, the clone method should never be prepended. Supported repo backends and their expected format are: + +- https: [hostname]/[repo_path] +- ssh: [hostname]:[repo_path] +- local [repo_path] (local repo entries are ephemeral and will not survive a core restart) + +Examples of valid repository identifiers: ``` -github.com/AliceO2Group/ControlWorkflows -gitlab.cern.ch/tmrnjava/AliECS_conf/ +github.com/AliceO2Group/ControlWorkflows (https) +gitlab.cern.ch/tmrnjava/AliECS_conf/ (https) +alio2-cr1-hv-gw01.cern.ch:/opt/git/ControlWorkflows (ssh) +/home/flp/git/ControlWorkflows (local filesystem - (*entry does not survive a core restart*)) ``` By default, all short task and workflow names are assumed to be in the default repository (see `coconut repo list` command). -Any workflow from any repository can be loaded by providing a full and unique path (the `workflows` directory is omitted from the loading path as it's name and existence is enforced by convention when we know we're dealing with workflows), e.g. the following two are different workflows: +Any workflow from any repository can be loaded by providing a full and unique path, e.g. the following two are different workflows: ``` -github.com/AliceO2Group/ControlWorkflows/readout-qc-1 -gitlab.cern.ch/tmrnjava/AliECS_conf/readout-qc-1 +github.com/AliceO2Group/ControlWorkflows/workflows/readout-qc-1 +gitlab.cern.ch/tmrnjava/AliECS_conf/workflows/readout-qc-1 ``` By default a workflow is loaded from its state at HEAD in the master branch. A request to load a workflow can further be qualified with a branch, tag or commit hash: ``` readout-qc-1@readout-testing -gitlab.cern.ch/tmrnjava/AliECS_conf/readout-qc-1@5c7f1c1f +gitlab.cern.ch/tmrnjava/AliECS_conf/workflows/readout-qc-1@5c7f1c1f ``` Make sure to run `coconut repo refresh` if you make changes to a configuration repository. @@ -41,8 +49,10 @@ Make sure to run `coconut repo refresh` if you make changes to a configuration r ``` --config string optional configuration file for coconut (default $HOME/.config/coconut/settings.yaml) - --config_endpoint string configuration endpoint used by AliECS core as PROTO://HOST:PORT (default "consul://127.0.0.1:8500") - --endpoint string AliECS core endpoint as HOST:PORT (default "127.0.0.1:47102") + --config_endpoint string configuration endpoint used by AliECS core as PROTO://HOST:PORT (default "apricot://127.0.0.1:32101") + --endpoint string AliECS core endpoint as HOST:PORT (default "127.0.0.1:32102") + --nocolor disable colors in output + --nospinner disable animations in output -v, --verbose show verbose output for debug purposes ``` @@ -51,8 +61,9 @@ Make sure to run `coconut repo refresh` if you make changes to a configuration r * [coconut](coconut.md) - O² Control and Configuration Utility * [coconut repository add](coconut_repository_add.md) - add a new git repository * [coconut repository default](coconut_repository_default.md) - set a git repository as default +* [coconut repository default-revision](coconut_repository_default-revision.md) - set default global and per-repository revision * [coconut repository list](coconut_repository_list.md) - list git repositories * [coconut repository refresh](coconut_repository_refresh.md) - refresh git repositories * [coconut repository remove](coconut_repository_remove.md) - remove a git repository -###### Auto generated by spf13/cobra on 26-Aug-2019 +###### Auto generated by spf13/cobra on 27-Nov-2024 diff --git a/coconut/doc/coconut_repository_add.md b/coconut/doc/coconut_repository_add.md index c47038b83..7b24c4f80 100644 --- a/coconut/doc/coconut_repository_add.md +++ b/coconut/doc/coconut_repository_add.md @@ -4,25 +4,52 @@ add a new git repository ### Synopsis -The repository add command adds a git repository -to the catalogue of repositories used for task and workflow configuration. +The repository add command adds a git repository to the catalogue of repositories used for task and workflow configuration. +The default revision of the repository may be explicitly specified by passing the flag `--default-revision` . In any case, +the ensuing list is followed until a valid revision has been identified: + +- explicitly set default revision (optional) +- global default revision +- `master` +- `main` + +Exhaustion of the aforementioned list results in a repo add failure. + +`coconut repo add` can be called with + +1) a repository identifier +2) a repository identifier coupled with the `--default-revision` flag (see examples below) + +The protocol prefix should always be omitted. + +``` +coconut repository add [flags] +``` + +### Examples ``` -coconut repository add [flags] + * `coconut repo add github.com/AliceO2Group/ControlWorkflows` + * `coconut repo add github.com/AliceO2Group/ControlWorkflows --default-revision custom-rev` + * `coconut repo add alio2-cr1-hv-gw01.cern.ch:/opt/git/ControlWorkflows --default-revision custom-rev` + * `coconut repo add /home/flp/git/ControlWorkflows` ``` ### Options ``` - -h, --help help for add + -d, --default-revision string default revision for repository to add + -h, --help help for add ``` ### Options inherited from parent commands ``` --config string optional configuration file for coconut (default $HOME/.config/coconut/settings.yaml) - --config_endpoint string configuration endpoint used by AliECS core as PROTO://HOST:PORT (default "consul://127.0.0.1:8500") - --endpoint string AliECS core endpoint as HOST:PORT (default "127.0.0.1:47102") + --config_endpoint string configuration endpoint used by AliECS core as PROTO://HOST:PORT (default "apricot://127.0.0.1:32101") + --endpoint string AliECS core endpoint as HOST:PORT (default "127.0.0.1:32102") + --nocolor disable colors in output + --nospinner disable animations in output -v, --verbose show verbose output for debug purposes ``` @@ -30,4 +57,4 @@ coconut repository add [flags] * [coconut repository](coconut_repository.md) - manage git repositories for task and workflow configuration -###### Auto generated by spf13/cobra on 26-Aug-2019 +###### Auto generated by spf13/cobra on 27-Nov-2024 diff --git a/coconut/doc/coconut_repository_default-revision.md b/coconut/doc/coconut_repository_default-revision.md new file mode 100644 index 000000000..0a934c080 --- /dev/null +++ b/coconut/doc/coconut_repository_default-revision.md @@ -0,0 +1,44 @@ +## coconut repository default-revision + +set default global and per-repository revision + +### Synopsis + +The repository default-revision command sets the global default repository revision.' + +To set a per repository default revision, the default revision specified needs to be preceded by the repository index (not its name), as is reported by `coconut repo list`. + +``` +coconut repository default-revision [flags] +``` + +### Examples + +``` + * `coconut repo default-revision basic-tasks` Sets `basic-tasks`as the global default-revision + * `coconut repo default-revision 0 master` Sets `master`as the default-revision for repo with index 0 + * `coconut repo default-revision 2 vs-sftb` Sets `vs-sftb`as the default-revision for repo with index 2 +``` + +### Options + +``` + -h, --help help for default-revision +``` + +### Options inherited from parent commands + +``` + --config string optional configuration file for coconut (default $HOME/.config/coconut/settings.yaml) + --config_endpoint string configuration endpoint used by AliECS core as PROTO://HOST:PORT (default "apricot://127.0.0.1:32101") + --endpoint string AliECS core endpoint as HOST:PORT (default "127.0.0.1:32102") + --nocolor disable colors in output + --nospinner disable animations in output + -v, --verbose show verbose output for debug purposes +``` + +### SEE ALSO + +* [coconut repository](coconut_repository.md) - manage git repositories for task and workflow configuration + +###### Auto generated by spf13/cobra on 27-Nov-2024 diff --git a/coconut/doc/coconut_repository_default.md b/coconut/doc/coconut_repository_default.md index 1af3fb0dd..c4aec3298 100644 --- a/coconut/doc/coconut_repository_default.md +++ b/coconut/doc/coconut_repository_default.md @@ -4,11 +4,17 @@ set a git repository as default ### Synopsis -The repository default command sets a git repository -as the default repository for incoming workflow deployment requests. +The repository default command sets a git repository as the default repository for incoming workflow deployment requests. +A repository is referenced through its repo id, as reported by `coconut repo list`. ``` -coconut repository default [flags] +coconut repository default [flags] +``` + +### Examples + +``` + * `coconut repo default 2` ``` ### Options @@ -21,8 +27,10 @@ coconut repository default [flags] ``` --config string optional configuration file for coconut (default $HOME/.config/coconut/settings.yaml) - --config_endpoint string configuration endpoint used by AliECS core as PROTO://HOST:PORT (default "consul://127.0.0.1:8500") - --endpoint string AliECS core endpoint as HOST:PORT (default "127.0.0.1:47102") + --config_endpoint string configuration endpoint used by AliECS core as PROTO://HOST:PORT (default "apricot://127.0.0.1:32101") + --endpoint string AliECS core endpoint as HOST:PORT (default "127.0.0.1:32102") + --nocolor disable colors in output + --nospinner disable animations in output -v, --verbose show verbose output for debug purposes ``` @@ -30,4 +38,4 @@ coconut repository default [flags] * [coconut repository](coconut_repository.md) - manage git repositories for task and workflow configuration -###### Auto generated by spf13/cobra on 26-Aug-2019 +###### Auto generated by spf13/cobra on 27-Nov-2024 diff --git a/coconut/doc/coconut_repository_list.md b/coconut/doc/coconut_repository_list.md index d948a815f..4878ac9b8 100644 --- a/coconut/doc/coconut_repository_list.md +++ b/coconut/doc/coconut_repository_list.md @@ -4,8 +4,7 @@ list git repositories ### Synopsis -The repostory list command lists available git repositories -to be used for task and workflow configuration. +The repostory list command lists available git repositories to be used for task and workflow configuration. ``` coconut repository list [flags] @@ -21,8 +20,10 @@ coconut repository list [flags] ``` --config string optional configuration file for coconut (default $HOME/.config/coconut/settings.yaml) - --config_endpoint string configuration endpoint used by AliECS core as PROTO://HOST:PORT (default "consul://127.0.0.1:8500") - --endpoint string AliECS core endpoint as HOST:PORT (default "127.0.0.1:47102") + --config_endpoint string configuration endpoint used by AliECS core as PROTO://HOST:PORT (default "apricot://127.0.0.1:32101") + --endpoint string AliECS core endpoint as HOST:PORT (default "127.0.0.1:32102") + --nocolor disable colors in output + --nospinner disable animations in output -v, --verbose show verbose output for debug purposes ``` @@ -30,4 +31,4 @@ coconut repository list [flags] * [coconut repository](coconut_repository.md) - manage git repositories for task and workflow configuration -###### Auto generated by spf13/cobra on 26-Aug-2019 +###### Auto generated by spf13/cobra on 27-Nov-2024 diff --git a/coconut/doc/coconut_repository_refresh.md b/coconut/doc/coconut_repository_refresh.md index 1b18a1bb6..a6486cbb4 100644 --- a/coconut/doc/coconut_repository_refresh.md +++ b/coconut/doc/coconut_repository_refresh.md @@ -4,11 +4,18 @@ refresh git repositories ### Synopsis -The repository refresh command makes sure -all git repositories used for task and workflow configuration are up to date. +The repository refresh command makes sure all git repositories used for task and workflow configuration are up to date. +It can optionally be supplied with a repo id, to only refresh a specific repo. Repo ids are reported by `coconut repo list`. ``` -coconut repository refresh [flags] +coconut repository refresh [repo id] [flags] +``` + +### Examples + +``` + * `coconut repo refresh` + * `coconut repo refresh 1` ``` ### Options @@ -21,8 +28,10 @@ coconut repository refresh [flags] ``` --config string optional configuration file for coconut (default $HOME/.config/coconut/settings.yaml) - --config_endpoint string configuration endpoint used by AliECS core as PROTO://HOST:PORT (default "consul://127.0.0.1:8500") - --endpoint string AliECS core endpoint as HOST:PORT (default "127.0.0.1:47102") + --config_endpoint string configuration endpoint used by AliECS core as PROTO://HOST:PORT (default "apricot://127.0.0.1:32101") + --endpoint string AliECS core endpoint as HOST:PORT (default "127.0.0.1:32102") + --nocolor disable colors in output + --nospinner disable animations in output -v, --verbose show verbose output for debug purposes ``` @@ -30,4 +39,4 @@ coconut repository refresh [flags] * [coconut repository](coconut_repository.md) - manage git repositories for task and workflow configuration -###### Auto generated by spf13/cobra on 26-Aug-2019 +###### Auto generated by spf13/cobra on 27-Nov-2024 diff --git a/coconut/doc/coconut_repository_remove.md b/coconut/doc/coconut_repository_remove.md index d9ce35142..f69c18429 100644 --- a/coconut/doc/coconut_repository_remove.md +++ b/coconut/doc/coconut_repository_remove.md @@ -4,11 +4,18 @@ remove a git repository ### Synopsis -The repository remove command removes a git repository -from the catalogue of workflow configuration sources. +The repository remove command removes a git repository from the catalogue of workflow configuration sources. +A repository is referenced by its repo id, as reported by`coconut repo list` ``` -coconut repository remove [flags] +coconut repository remove [flags] +``` + +### Examples + +``` + * `coconut repo remove 1` + * `coconut repo del 2` ``` ### Options @@ -21,8 +28,10 @@ coconut repository remove [flags] ``` --config string optional configuration file for coconut (default $HOME/.config/coconut/settings.yaml) - --config_endpoint string configuration endpoint used by AliECS core as PROTO://HOST:PORT (default "consul://127.0.0.1:8500") - --endpoint string AliECS core endpoint as HOST:PORT (default "127.0.0.1:47102") + --config_endpoint string configuration endpoint used by AliECS core as PROTO://HOST:PORT (default "apricot://127.0.0.1:32101") + --endpoint string AliECS core endpoint as HOST:PORT (default "127.0.0.1:32102") + --nocolor disable colors in output + --nospinner disable animations in output -v, --verbose show verbose output for debug purposes ``` @@ -30,4 +39,4 @@ coconut repository remove [flags] * [coconut repository](coconut_repository.md) - manage git repositories for task and workflow configuration -###### Auto generated by spf13/cobra on 26-Aug-2019 +###### Auto generated by spf13/cobra on 27-Nov-2024 diff --git a/coconut/doc/coconut_role.md b/coconut/doc/coconut_role.md index 67554b5f1..160374748 100644 --- a/coconut/doc/coconut_role.md +++ b/coconut/doc/coconut_role.md @@ -17,8 +17,10 @@ display information on active roles. ``` --config string optional configuration file for coconut (default $HOME/.config/coconut/settings.yaml) - --config_endpoint string configuration endpoint used by AliECS core as PROTO://HOST:PORT (default "consul://127.0.0.1:8500") - --endpoint string AliECS core endpoint as HOST:PORT (default "127.0.0.1:47102") + --config_endpoint string configuration endpoint used by AliECS core as PROTO://HOST:PORT (default "apricot://127.0.0.1:32101") + --endpoint string AliECS core endpoint as HOST:PORT (default "127.0.0.1:32102") + --nocolor disable colors in output + --nospinner disable animations in output -v, --verbose show verbose output for debug purposes ``` @@ -27,4 +29,4 @@ display information on active roles. * [coconut](coconut.md) - O² Control and Configuration Utility * [coconut role query](coconut_role_query.md) - query O² roles -###### Auto generated by spf13/cobra on 26-Aug-2019 +###### Auto generated by spf13/cobra on 27-Nov-2024 diff --git a/coconut/doc/coconut_role_query.md b/coconut/doc/coconut_role_query.md index 088b28b9d..ee3759272 100644 --- a/coconut/doc/coconut_role_query.md +++ b/coconut/doc/coconut_role_query.md @@ -4,7 +4,25 @@ query O² roles ### Synopsis -The role query command returns one or more role trees. +The role query command returns one or more role subtrees within a given environment. + +It allows the user to inspect the environment's workflow at runtime, after template processing operations, including +iterator expansion, just-in-time subworkflow inclusion, and variable substitution, have taken place. + +For the target role or roles, it also prints the locally defined variables, as well as the consolidated variable +stack from the point of view of that role. + +The role query command accepts two arguments: the environment ID and the query path. The query path is a dot-separated +walk through the role tree of the given environment, starting from the root role. Wildcard expressions are allowed, as +per https://github.com/gobwas/glob syntax. + +Examples: + + * `coconut role query 2rE9AV3m1HL readout-dataflow` - queries the role `readout-dataflow` in environment `2rE9AV3m1HL`, prints the full tree, along with the variables defined in the root role + * `coconut role query 2rE9AV3m1HL readout-dataflow.host-aido2-bld4-lab102` - queries the role `readout-dataflow.host-aido2-bld4-lab102`, prints the subtree of that role, along with the variables defined in it + * `coconut role query 2rE9AV3m1HL readout-dataflow.host-aido2-bld4-lab102.data-distribution.stfs` - queries the role at the given path, it is a task role so there is no subtree, prints the variables defined in that role + * `coconut role query 2rE9AV3m1HL readout-dataflow.host-aido2-bld4-lab*` - queries the roles matching the given glob expression, prints all the subtrees and variables + * `coconut role query 2rE9AV3m1HL readout-dataflow.host-aido2-bld4-lab*.data-distribution.*` - queries the task roles matching the given glob expression, prints all the variables ``` coconut role query [environment id] [query path] [flags] @@ -20,8 +38,10 @@ coconut role query [environment id] [query path] [flags] ``` --config string optional configuration file for coconut (default $HOME/.config/coconut/settings.yaml) - --config_endpoint string configuration endpoint used by AliECS core as PROTO://HOST:PORT (default "consul://127.0.0.1:8500") - --endpoint string AliECS core endpoint as HOST:PORT (default "127.0.0.1:47102") + --config_endpoint string configuration endpoint used by AliECS core as PROTO://HOST:PORT (default "apricot://127.0.0.1:32101") + --endpoint string AliECS core endpoint as HOST:PORT (default "127.0.0.1:32102") + --nocolor disable colors in output + --nospinner disable animations in output -v, --verbose show verbose output for debug purposes ``` @@ -29,4 +49,4 @@ coconut role query [environment id] [query path] [flags] * [coconut role](coconut_role.md) - query roles in an environment -###### Auto generated by spf13/cobra on 26-Aug-2019 +###### Auto generated by spf13/cobra on 27-Nov-2024 diff --git a/coconut/doc/coconut_task.md b/coconut/doc/coconut_task.md index eb829f56f..f5261acb9 100644 --- a/coconut/doc/coconut_task.md +++ b/coconut/doc/coconut_task.md @@ -17,8 +17,10 @@ manage active tasks. ``` --config string optional configuration file for coconut (default $HOME/.config/coconut/settings.yaml) - --config_endpoint string configuration endpoint used by AliECS core as PROTO://HOST:PORT (default "consul://127.0.0.1:8500") - --endpoint string AliECS core endpoint as HOST:PORT (default "127.0.0.1:47102") + --config_endpoint string configuration endpoint used by AliECS core as PROTO://HOST:PORT (default "apricot://127.0.0.1:32101") + --endpoint string AliECS core endpoint as HOST:PORT (default "127.0.0.1:32102") + --nocolor disable colors in output + --nospinner disable animations in output -v, --verbose show verbose output for debug purposes ``` @@ -28,4 +30,4 @@ manage active tasks. * [coconut task clean](coconut_task_clean.md) - clean up idle O² tasks * [coconut task list](coconut_task_list.md) - list O² tasks -###### Auto generated by spf13/cobra on 26-Aug-2019 +###### Auto generated by spf13/cobra on 27-Nov-2024 diff --git a/coconut/doc/coconut_task_clean.md b/coconut/doc/coconut_task_clean.md index 49c8f6177..531754489 100644 --- a/coconut/doc/coconut_task_clean.md +++ b/coconut/doc/coconut_task_clean.md @@ -22,8 +22,10 @@ coconut task clean [flags] ``` --config string optional configuration file for coconut (default $HOME/.config/coconut/settings.yaml) - --config_endpoint string configuration endpoint used by AliECS core as PROTO://HOST:PORT (default "consul://127.0.0.1:8500") - --endpoint string AliECS core endpoint as HOST:PORT (default "127.0.0.1:47102") + --config_endpoint string configuration endpoint used by AliECS core as PROTO://HOST:PORT (default "apricot://127.0.0.1:32101") + --endpoint string AliECS core endpoint as HOST:PORT (default "127.0.0.1:32102") + --nocolor disable colors in output + --nospinner disable animations in output -v, --verbose show verbose output for debug purposes ``` @@ -31,4 +33,4 @@ coconut task clean [flags] * [coconut task](coconut_task.md) - manage active tasks -###### Auto generated by spf13/cobra on 26-Aug-2019 +###### Auto generated by spf13/cobra on 27-Nov-2024 diff --git a/coconut/doc/coconut_task_list.md b/coconut/doc/coconut_task_list.md index 1dc5e7201..65cbad070 100644 --- a/coconut/doc/coconut_task_list.md +++ b/coconut/doc/coconut_task_list.md @@ -21,8 +21,10 @@ coconut task list [flags] ``` --config string optional configuration file for coconut (default $HOME/.config/coconut/settings.yaml) - --config_endpoint string configuration endpoint used by AliECS core as PROTO://HOST:PORT (default "consul://127.0.0.1:8500") - --endpoint string AliECS core endpoint as HOST:PORT (default "127.0.0.1:47102") + --config_endpoint string configuration endpoint used by AliECS core as PROTO://HOST:PORT (default "apricot://127.0.0.1:32101") + --endpoint string AliECS core endpoint as HOST:PORT (default "127.0.0.1:32102") + --nocolor disable colors in output + --nospinner disable animations in output -v, --verbose show verbose output for debug purposes ``` @@ -30,4 +32,4 @@ coconut task list [flags] * [coconut task](coconut_task.md) - manage active tasks -###### Auto generated by spf13/cobra on 26-Aug-2019 +###### Auto generated by spf13/cobra on 27-Nov-2024 diff --git a/coconut/doc/coconut_template.md b/coconut/doc/coconut_template.md index 5aca6ec2d..efdcb6aa1 100644 --- a/coconut/doc/coconut_template.md +++ b/coconut/doc/coconut_template.md @@ -17,8 +17,10 @@ display information on available workflow templates. ``` --config string optional configuration file for coconut (default $HOME/.config/coconut/settings.yaml) - --config_endpoint string configuration endpoint used by AliECS core as PROTO://HOST:PORT (default "consul://127.0.0.1:8500") - --endpoint string AliECS core endpoint as HOST:PORT (default "127.0.0.1:47102") + --config_endpoint string configuration endpoint used by AliECS core as PROTO://HOST:PORT (default "apricot://127.0.0.1:32101") + --endpoint string AliECS core endpoint as HOST:PORT (default "127.0.0.1:32102") + --nocolor disable colors in output + --nospinner disable animations in output -v, --verbose show verbose output for debug purposes ``` @@ -27,4 +29,4 @@ display information on available workflow templates. * [coconut](coconut.md) - O² Control and Configuration Utility * [coconut template list](coconut_template_list.md) - list available workflow templates -###### Auto generated by spf13/cobra on 26-Aug-2019 +###### Auto generated by spf13/cobra on 27-Nov-2024 diff --git a/coconut/doc/coconut_template_list.md b/coconut/doc/coconut_template_list.md index 964def551..7d2fdda29 100644 --- a/coconut/doc/coconut_template_list.md +++ b/coconut/doc/coconut_template_list.md @@ -7,22 +7,50 @@ list available workflow templates The template list command shows a list of available workflow templates. These workflow templates can then be loaded to create an environment. +`coconut templ list` can be called with + +1) a combination of the `--repo` , `--revision` , `--all-branches` , `--all-tags` , `--all-workflows` flags, or with +2) an argument in the form of [repo-pattern]@[revision-pattern], where the patterns are globbing. + ``` coconut template list [flags] ``` +### Examples + +``` + * `coconut templ list` lists templates from the HEAD of master for all git repositories + * `coconut templ list --all-workflows` lists all templates (including non-public ones) from the HEAD of master for all git repositories + * `coconut templ list '*AliceO2Group*'` lists all templates coming from the HEAD of master of git repositories that match the pattern *AliceO2Group* + * `coconut templ list '*@v*'` lists templates coming from revisions matching the `v*`pattern for all git repositories + * `coconut templ list --repository='*AliceO2Group*'` lists all templates coming from the HEAD of master of git repositories that match the pattern *AliceO2Group* + * `coconut templ list --revision='dev*'` lists templates coming from revisions matching the `dev*`pattern for all git repositories + * `coconut templ list --repository='*gitlab.cern.ch*' --revision=master` lists templates for revisions `master`for git repositories matching `*gitlab.cern.ch*` + * `coconut templ list --all-branches` lists templates from all branches for all git repositories + * `coconut templ list --repository='*github.com*' --all-tags` lists templates from all tags for git repositories which match the *github.com* pattern + * `coconut templ list --revision=5c7f1c1fded1b87243998579ed876c8035a08377 ` lists templates from the commit corresponding to the hash for all git repositories +``` + ### Options ``` - -h, --help help for list + -b, --all-branches list templates from all branches + -t, --all-tags list templates from all tags + -a, --all-workflows list all templates, even non-public ones + -h, --help help for list + -r, --repository string repositories to list templates from + -i, --revision string revisions (branches/tags) to list templates from + -d, --show-description show the description of each template ``` ### Options inherited from parent commands ``` --config string optional configuration file for coconut (default $HOME/.config/coconut/settings.yaml) - --config_endpoint string configuration endpoint used by AliECS core as PROTO://HOST:PORT (default "consul://127.0.0.1:8500") - --endpoint string AliECS core endpoint as HOST:PORT (default "127.0.0.1:47102") + --config_endpoint string configuration endpoint used by AliECS core as PROTO://HOST:PORT (default "apricot://127.0.0.1:32101") + --endpoint string AliECS core endpoint as HOST:PORT (default "127.0.0.1:32102") + --nocolor disable colors in output + --nospinner disable animations in output -v, --verbose show verbose output for debug purposes ``` @@ -30,4 +58,4 @@ coconut template list [flags] * [coconut template](coconut_template.md) - query available workflow templates in configuration repositories -###### Auto generated by spf13/cobra on 26-Aug-2019 +###### Auto generated by spf13/cobra on 27-Nov-2024 diff --git a/coconut/doc/generate.go b/coconut/doc/generate.go index 4c336cef9..48af65ff3 100644 --- a/coconut/doc/generate.go +++ b/coconut/doc/generate.go @@ -24,13 +24,12 @@ package main - import ( "github.com/AliceO2Group/Control/coconut/app" + "github.com/AliceO2Group/Control/coconut/cmd" "github.com/AliceO2Group/Control/common/logger" "github.com/sirupsen/logrus" "github.com/spf13/cobra/doc" - "github.com/AliceO2Group/Control/coconut/cmd" ) var log = logger.New(logrus.StandardLogger(), app.NAME) @@ -41,4 +40,4 @@ func main() { if err != nil { log.Fatal(err) } -} \ No newline at end of file +} diff --git a/coconut/protos/o2control.pb.go b/coconut/protos/o2control.pb.go index db73e9e78..4124ea0a0 100644 --- a/coconut/protos/o2control.pb.go +++ b/coconut/protos/o2control.pb.go @@ -1,58 +1,85 @@ -// Code generated by protoc-gen-gogo. DO NOT EDIT. +// +// === This file is part of ALICE O² === +// +// Copyright 2018 CERN and copyright holders of ALICE O². +// Author: Teo Mrnjavac +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// In applying this license CERN does not waive the privileges and +// immunities granted to it by virtue of its status as an +// Intergovernmental Organization or submit itself to any jurisdiction. + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.34.1 +// protoc v5.29.3 // source: protos/o2control.proto package pb import ( - context "context" - fmt "fmt" - proto "github.com/golang/protobuf/proto" - grpc "google.golang.org/grpc" - io "io" - math "math" + protos "github.com/AliceO2Group/Control/common/protos" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" ) -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package - -type StatusUpdate_Level int32 - const ( - StatusUpdate_DEBUG StatusUpdate_Level = 0 - StatusUpdate_INFO StatusUpdate_Level = 1 - StatusUpdate_WARNING StatusUpdate_Level = 2 - StatusUpdate_ERROR StatusUpdate_Level = 3 + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) -var StatusUpdate_Level_name = map[int32]string{ - 0: "DEBUG", - 1: "INFO", - 2: "WARNING", - 3: "ERROR", -} - -var StatusUpdate_Level_value = map[string]int32{ - "DEBUG": 0, - "INFO": 1, - "WARNING": 2, - "ERROR": 3, -} - -func (x StatusUpdate_Level) String() string { - return proto.EnumName(StatusUpdate_Level_name, int32(x)) -} - -func (StatusUpdate_Level) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{3, 0} -} +// Symbols defined in public import of protos/events.proto. + +type OpStatus = protos.OpStatus + +const OpStatus_NULL = protos.OpStatus_NULL +const OpStatus_STARTED = protos.OpStatus_STARTED +const OpStatus_ONGOING = protos.OpStatus_ONGOING +const OpStatus_DONE_OK = protos.OpStatus_DONE_OK +const OpStatus_DONE_ERROR = protos.OpStatus_DONE_ERROR +const OpStatus_DONE_TIMEOUT = protos.OpStatus_DONE_TIMEOUT + +var OpStatus_name = protos.OpStatus_name +var OpStatus_value = protos.OpStatus_value + +type Ev_MetaEvent_MesosHeartbeat = protos.Ev_MetaEvent_MesosHeartbeat +type Ev_MetaEvent_CoreStart = protos.Ev_MetaEvent_CoreStart +type Ev_MetaEvent_FrameworkEvent = protos.Ev_MetaEvent_FrameworkEvent +type Ev_EnvironmentEvent = protos.Ev_EnvironmentEvent +type Traits = protos.Traits +type Ev_TaskEvent = protos.Ev_TaskEvent +type Ev_CallEvent = protos.Ev_CallEvent +type Ev_RoleEvent = protos.Ev_RoleEvent +type Ev_IntegratedServiceEvent = protos.Ev_IntegratedServiceEvent +type Ev_RunEvent = protos.Ev_RunEvent +type Ev_BeamModeEvent = protos.Ev_BeamModeEvent +type Event = protos.Event +type Event_EnvironmentEvent = protos.Event_EnvironmentEvent +type Event_TaskEvent = protos.Event_TaskEvent +type Event_RoleEvent = protos.Event_RoleEvent +type Event_CallEvent = protos.Event_CallEvent +type Event_IntegratedServiceEvent = protos.Event_IntegratedServiceEvent +type Event_RunEvent = protos.Event_RunEvent +type Event_FrameworkEvent = protos.Event_FrameworkEvent +type Event_MesosHeartbeatEvent = protos.Event_MesosHeartbeatEvent +type Event_CoreStartEvent = protos.Event_CoreStartEvent +type Event_BeamModeEvent = protos.Event_BeamModeEvent type ControlEnvironmentRequest_Optype int32 @@ -63,32 +90,56 @@ const ( ControlEnvironmentRequest_CONFIGURE ControlEnvironmentRequest_Optype = 3 ControlEnvironmentRequest_RESET ControlEnvironmentRequest_Optype = 4 ControlEnvironmentRequest_GO_ERROR ControlEnvironmentRequest_Optype = 5 + ControlEnvironmentRequest_DEPLOY ControlEnvironmentRequest_Optype = 6 ) -var ControlEnvironmentRequest_Optype_name = map[int32]string{ - 0: "NOOP", - 1: "START_ACTIVITY", - 2: "STOP_ACTIVITY", - 3: "CONFIGURE", - 4: "RESET", - 5: "GO_ERROR", -} +// Enum value maps for ControlEnvironmentRequest_Optype. +var ( + ControlEnvironmentRequest_Optype_name = map[int32]string{ + 0: "NOOP", + 1: "START_ACTIVITY", + 2: "STOP_ACTIVITY", + 3: "CONFIGURE", + 4: "RESET", + 5: "GO_ERROR", + 6: "DEPLOY", + } + ControlEnvironmentRequest_Optype_value = map[string]int32{ + "NOOP": 0, + "START_ACTIVITY": 1, + "STOP_ACTIVITY": 2, + "CONFIGURE": 3, + "RESET": 4, + "GO_ERROR": 5, + "DEPLOY": 6, + } +) -var ControlEnvironmentRequest_Optype_value = map[string]int32{ - "NOOP": 0, - "START_ACTIVITY": 1, - "STOP_ACTIVITY": 2, - "CONFIGURE": 3, - "RESET": 4, - "GO_ERROR": 5, +func (x ControlEnvironmentRequest_Optype) Enum() *ControlEnvironmentRequest_Optype { + p := new(ControlEnvironmentRequest_Optype) + *p = x + return p } func (x ControlEnvironmentRequest_Optype) String() string { - return proto.EnumName(ControlEnvironmentRequest_Optype_name, int32(x)) + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (ControlEnvironmentRequest_Optype) Descriptor() protoreflect.EnumDescriptor { + return file_protos_o2control_proto_enumTypes[0].Descriptor() +} + +func (ControlEnvironmentRequest_Optype) Type() protoreflect.EnumType { + return &file_protos_o2control_proto_enumTypes[0] } +func (x ControlEnvironmentRequest_Optype) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use ControlEnvironmentRequest_Optype.Descriptor instead. func (ControlEnvironmentRequest_Optype) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{16, 0} + return file_protos_o2control_proto_rawDescGZIP(), []int{15, 0} } type EnvironmentOperation_Optype int32 @@ -99,12971 +150,5960 @@ const ( EnvironmentOperation_ADD_ROLE EnvironmentOperation_Optype = 4 ) -var EnvironmentOperation_Optype_name = map[int32]string{ - 0: "NOOP", - 3: "REMOVE_ROLE", - 4: "ADD_ROLE", -} +// Enum value maps for EnvironmentOperation_Optype. +var ( + EnvironmentOperation_Optype_name = map[int32]string{ + 0: "NOOP", + 3: "REMOVE_ROLE", + 4: "ADD_ROLE", + } + EnvironmentOperation_Optype_value = map[string]int32{ + "NOOP": 0, + "REMOVE_ROLE": 3, + "ADD_ROLE": 4, + } +) -var EnvironmentOperation_Optype_value = map[string]int32{ - "NOOP": 0, - "REMOVE_ROLE": 3, - "ADD_ROLE": 4, +func (x EnvironmentOperation_Optype) Enum() *EnvironmentOperation_Optype { + p := new(EnvironmentOperation_Optype) + *p = x + return p } func (x EnvironmentOperation_Optype) String() string { - return proto.EnumName(EnvironmentOperation_Optype_name, int32(x)) + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } -func (EnvironmentOperation_Optype) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{19, 0} +func (EnvironmentOperation_Optype) Descriptor() protoreflect.EnumDescriptor { + return file_protos_o2control_proto_enumTypes[1].Descriptor() } -type Event_MesosHeartbeat struct { - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` +func (EnvironmentOperation_Optype) Type() protoreflect.EnumType { + return &file_protos_o2control_proto_enumTypes[1] } -func (m *Event_MesosHeartbeat) Reset() { *m = Event_MesosHeartbeat{} } -func (m *Event_MesosHeartbeat) String() string { return proto.CompactTextString(m) } -func (*Event_MesosHeartbeat) ProtoMessage() {} -func (*Event_MesosHeartbeat) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{0} +func (x EnvironmentOperation_Optype) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) } -func (m *Event_MesosHeartbeat) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *Event_MesosHeartbeat) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_Event_MesosHeartbeat.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err - } - return b[:n], nil - } -} -func (m *Event_MesosHeartbeat) XXX_Merge(src proto.Message) { - xxx_messageInfo_Event_MesosHeartbeat.Merge(m, src) -} -func (m *Event_MesosHeartbeat) XXX_Size() int { - return m.Size() -} -func (m *Event_MesosHeartbeat) XXX_DiscardUnknown() { - xxx_messageInfo_Event_MesosHeartbeat.DiscardUnknown(m) + +// Deprecated: Use EnvironmentOperation_Optype.Descriptor instead. +func (EnvironmentOperation_Optype) EnumDescriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{18, 0} } -var xxx_messageInfo_Event_MesosHeartbeat proto.InternalMessageInfo +type VarSpecMessage_UiWidget int32 -//////////////////////////////////////// -// Global status -//////////////////////////////////////// -type StatusRequest struct { - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} +const ( + VarSpecMessage_editBox VarSpecMessage_UiWidget = 0 // plain string input line, can accept types number (like a spinBox) and string + VarSpecMessage_slider VarSpecMessage_UiWidget = 1 // input widget exclusively for numbers, range allowedValues[0]-[1] + VarSpecMessage_listBox VarSpecMessage_UiWidget = 2 // displays a list of items, can accept types number, string or list; if number/string ==> single selection, otherwise multiple selection allowed + VarSpecMessage_dropDownBox VarSpecMessage_UiWidget = 3 + VarSpecMessage_comboBox VarSpecMessage_UiWidget = 4 + VarSpecMessage_radioButtonBox VarSpecMessage_UiWidget = 5 + VarSpecMessage_checkBox VarSpecMessage_UiWidget = 6 +) -func (m *StatusRequest) Reset() { *m = StatusRequest{} } -func (m *StatusRequest) String() string { return proto.CompactTextString(m) } -func (*StatusRequest) ProtoMessage() {} -func (*StatusRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{1} -} -func (m *StatusRequest) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *StatusRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_StatusRequest.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err - } - return b[:n], nil +// Enum value maps for VarSpecMessage_UiWidget. +var ( + VarSpecMessage_UiWidget_name = map[int32]string{ + 0: "editBox", + 1: "slider", + 2: "listBox", + 3: "dropDownBox", + 4: "comboBox", + 5: "radioButtonBox", + 6: "checkBox", + } + VarSpecMessage_UiWidget_value = map[string]int32{ + "editBox": 0, + "slider": 1, + "listBox": 2, + "dropDownBox": 3, + "comboBox": 4, + "radioButtonBox": 5, + "checkBox": 6, } -} -func (m *StatusRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_StatusRequest.Merge(m, src) -} -func (m *StatusRequest) XXX_Size() int { - return m.Size() -} -func (m *StatusRequest) XXX_DiscardUnknown() { - xxx_messageInfo_StatusRequest.DiscardUnknown(m) -} - -var xxx_messageInfo_StatusRequest proto.InternalMessageInfo +) -type StatusReply struct { - State string `protobuf:"bytes,1,opt,name=state,proto3" json:"state,omitempty"` - StatusUpdates []*StatusUpdate `protobuf:"bytes,2,rep,name=statusUpdates,proto3" json:"statusUpdates,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` +func (x VarSpecMessage_UiWidget) Enum() *VarSpecMessage_UiWidget { + p := new(VarSpecMessage_UiWidget) + *p = x + return p } -func (m *StatusReply) Reset() { *m = StatusReply{} } -func (m *StatusReply) String() string { return proto.CompactTextString(m) } -func (*StatusReply) ProtoMessage() {} -func (*StatusReply) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{2} -} -func (m *StatusReply) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) +func (x VarSpecMessage_UiWidget) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } -func (m *StatusReply) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_StatusReply.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err - } - return b[:n], nil - } + +func (VarSpecMessage_UiWidget) Descriptor() protoreflect.EnumDescriptor { + return file_protos_o2control_proto_enumTypes[2].Descriptor() } -func (m *StatusReply) XXX_Merge(src proto.Message) { - xxx_messageInfo_StatusReply.Merge(m, src) + +func (VarSpecMessage_UiWidget) Type() protoreflect.EnumType { + return &file_protos_o2control_proto_enumTypes[2] } -func (m *StatusReply) XXX_Size() int { - return m.Size() + +func (x VarSpecMessage_UiWidget) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) } -func (m *StatusReply) XXX_DiscardUnknown() { - xxx_messageInfo_StatusReply.DiscardUnknown(m) + +// Deprecated: Use VarSpecMessage_UiWidget.Descriptor instead. +func (VarSpecMessage_UiWidget) EnumDescriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{43, 0} } -var xxx_messageInfo_StatusReply proto.InternalMessageInfo +type VarSpecMessage_Type int32 -func (m *StatusReply) GetState() string { - if m != nil { - return m.State - } - return "" -} +const ( + VarSpecMessage_string VarSpecMessage_Type = 0 + VarSpecMessage_number VarSpecMessage_Type = 1 + VarSpecMessage_bool VarSpecMessage_Type = 2 + VarSpecMessage_list VarSpecMessage_Type = 3 + VarSpecMessage_map VarSpecMessage_Type = 4 +) -func (m *StatusReply) GetStatusUpdates() []*StatusUpdate { - if m != nil { - return m.StatusUpdates +// Enum value maps for VarSpecMessage_Type. +var ( + VarSpecMessage_Type_name = map[int32]string{ + 0: "string", + 1: "number", + 2: "bool", + 3: "list", + 4: "map", + } + VarSpecMessage_Type_value = map[string]int32{ + "string": 0, + "number": 1, + "bool": 2, + "list": 3, + "map": 4, } - return nil -} +) -type StatusUpdate struct { - Level StatusUpdate_Level `protobuf:"varint,1,opt,name=level,proto3,enum=o2control.StatusUpdate_Level" json:"level,omitempty"` - // Types that are valid to be assigned to Event: - // *StatusUpdate_MesosHeartbeat - Event isStatusUpdate_Event `protobuf_oneof:"Event"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *StatusUpdate) Reset() { *m = StatusUpdate{} } -func (m *StatusUpdate) String() string { return proto.CompactTextString(m) } -func (*StatusUpdate) ProtoMessage() {} -func (*StatusUpdate) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{3} -} -func (m *StatusUpdate) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *StatusUpdate) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_StatusUpdate.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err - } - return b[:n], nil - } -} -func (m *StatusUpdate) XXX_Merge(src proto.Message) { - xxx_messageInfo_StatusUpdate.Merge(m, src) +func (x VarSpecMessage_Type) Enum() *VarSpecMessage_Type { + p := new(VarSpecMessage_Type) + *p = x + return p } -func (m *StatusUpdate) XXX_Size() int { - return m.Size() + +func (x VarSpecMessage_Type) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } -func (m *StatusUpdate) XXX_DiscardUnknown() { - xxx_messageInfo_StatusUpdate.DiscardUnknown(m) + +func (VarSpecMessage_Type) Descriptor() protoreflect.EnumDescriptor { + return file_protos_o2control_proto_enumTypes[3].Descriptor() } -var xxx_messageInfo_StatusUpdate proto.InternalMessageInfo +func (VarSpecMessage_Type) Type() protoreflect.EnumType { + return &file_protos_o2control_proto_enumTypes[3] +} -type isStatusUpdate_Event interface { - isStatusUpdate_Event() - MarshalTo([]byte) (int, error) - Size() int +func (x VarSpecMessage_Type) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) } -type StatusUpdate_MesosHeartbeat struct { - MesosHeartbeat *Event_MesosHeartbeat `protobuf:"bytes,2,opt,name=mesosHeartbeat,proto3,oneof"` +// Deprecated: Use VarSpecMessage_Type.Descriptor instead. +func (VarSpecMessage_Type) EnumDescriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{43, 1} } -func (*StatusUpdate_MesosHeartbeat) isStatusUpdate_Event() {} +type SubscribeRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields -func (m *StatusUpdate) GetEvent() isStatusUpdate_Event { - if m != nil { - return m.Event - } - return nil + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` } -func (m *StatusUpdate) GetLevel() StatusUpdate_Level { - if m != nil { - return m.Level +func (x *SubscribeRequest) Reset() { + *x = SubscribeRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return StatusUpdate_DEBUG } -func (m *StatusUpdate) GetMesosHeartbeat() *Event_MesosHeartbeat { - if x, ok := m.GetEvent().(*StatusUpdate_MesosHeartbeat); ok { - return x.MesosHeartbeat - } - return nil +func (x *SubscribeRequest) String() string { + return protoimpl.X.MessageStringOf(x) } -// XXX_OneofFuncs is for the internal use of the proto package. -func (*StatusUpdate) XXX_OneofFuncs() (func(msg proto.Message, b *proto.Buffer) error, func(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error), func(msg proto.Message) (n int), []interface{}) { - return _StatusUpdate_OneofMarshaler, _StatusUpdate_OneofUnmarshaler, _StatusUpdate_OneofSizer, []interface{}{ - (*StatusUpdate_MesosHeartbeat)(nil), - } -} +func (*SubscribeRequest) ProtoMessage() {} -func _StatusUpdate_OneofMarshaler(msg proto.Message, b *proto.Buffer) error { - m := msg.(*StatusUpdate) - // Event - switch x := m.Event.(type) { - case *StatusUpdate_MesosHeartbeat: - _ = b.EncodeVarint(2<<3 | proto.WireBytes) - if err := b.EncodeMessage(x.MesosHeartbeat); err != nil { - return err +func (x *SubscribeRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) } - case nil: - default: - return fmt.Errorf("StatusUpdate.Event has unexpected type %T", x) + return ms } - return nil + return mi.MessageOf(x) } -func _StatusUpdate_OneofUnmarshaler(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error) { - m := msg.(*StatusUpdate) - switch tag { - case 2: // Event.mesosHeartbeat - if wire != proto.WireBytes { - return true, proto.ErrInternalBadWireType - } - msg := new(Event_MesosHeartbeat) - err := b.DecodeMessage(msg) - m.Event = &StatusUpdate_MesosHeartbeat{msg} - return true, err - default: - return false, nil - } +// Deprecated: Use SubscribeRequest.ProtoReflect.Descriptor instead. +func (*SubscribeRequest) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{0} } -func _StatusUpdate_OneofSizer(msg proto.Message) (n int) { - m := msg.(*StatusUpdate) - // Event - switch x := m.Event.(type) { - case *StatusUpdate_MesosHeartbeat: - s := proto.Size(x.MesosHeartbeat) - n += 1 // tag and wire - n += proto.SizeVarint(uint64(s)) - n += s - case nil: - default: - panic(fmt.Sprintf("proto: unexpected type %T in oneof", x)) +func (x *SubscribeRequest) GetId() string { + if x != nil { + return x.Id } - return n + return "" } -//////////////////////////////////////// +// ////////////////////////////////////// // Framework -//////////////////////////////////////// +// ////////////////////////////////////// type GetFrameworkInfoRequest struct { - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields } -func (m *GetFrameworkInfoRequest) Reset() { *m = GetFrameworkInfoRequest{} } -func (m *GetFrameworkInfoRequest) String() string { return proto.CompactTextString(m) } -func (*GetFrameworkInfoRequest) ProtoMessage() {} -func (*GetFrameworkInfoRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{4} -} -func (m *GetFrameworkInfoRequest) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *GetFrameworkInfoRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_GetFrameworkInfoRequest.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err - } - return b[:n], nil +func (x *GetFrameworkInfoRequest) Reset() { + *x = GetFrameworkInfoRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } } -func (m *GetFrameworkInfoRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_GetFrameworkInfoRequest.Merge(m, src) -} -func (m *GetFrameworkInfoRequest) XXX_Size() int { - return m.Size() -} -func (m *GetFrameworkInfoRequest) XXX_DiscardUnknown() { - xxx_messageInfo_GetFrameworkInfoRequest.DiscardUnknown(m) + +func (x *GetFrameworkInfoRequest) String() string { + return protoimpl.X.MessageStringOf(x) } -var xxx_messageInfo_GetFrameworkInfoRequest proto.InternalMessageInfo +func (*GetFrameworkInfoRequest) ProtoMessage() {} -type Version struct { - Major int32 `protobuf:"varint,1,opt,name=major,proto3" json:"major,omitempty"` - Minor int32 `protobuf:"varint,2,opt,name=minor,proto3" json:"minor,omitempty"` - Patch int32 `protobuf:"varint,3,opt,name=patch,proto3" json:"patch,omitempty"` - Build string `protobuf:"bytes,4,opt,name=build,proto3" json:"build,omitempty"` - ProductName string `protobuf:"bytes,5,opt,name=productName,proto3" json:"productName,omitempty"` - VersionStr string `protobuf:"bytes,6,opt,name=versionStr,proto3" json:"versionStr,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *Version) Reset() { *m = Version{} } -func (m *Version) String() string { return proto.CompactTextString(m) } -func (*Version) ProtoMessage() {} -func (*Version) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{5} -} -func (m *Version) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *Version) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_Version.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err +func (x *GetFrameworkInfoRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) } - return b[:n], nil + return ms } + return mi.MessageOf(x) +} + +// Deprecated: Use GetFrameworkInfoRequest.ProtoReflect.Descriptor instead. +func (*GetFrameworkInfoRequest) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{1} +} + +type Version struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Major int32 `protobuf:"varint,1,opt,name=major,proto3" json:"major,omitempty"` + Minor int32 `protobuf:"varint,2,opt,name=minor,proto3" json:"minor,omitempty"` + Patch int32 `protobuf:"varint,3,opt,name=patch,proto3" json:"patch,omitempty"` + Build string `protobuf:"bytes,4,opt,name=build,proto3" json:"build,omitempty"` + ProductName string `protobuf:"bytes,5,opt,name=productName,proto3" json:"productName,omitempty"` + VersionStr string `protobuf:"bytes,6,opt,name=versionStr,proto3" json:"versionStr,omitempty"` } -func (m *Version) XXX_Merge(src proto.Message) { - xxx_messageInfo_Version.Merge(m, src) + +func (x *Version) Reset() { + *x = Version{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *Version) XXX_Size() int { - return m.Size() + +func (x *Version) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *Version) XXX_DiscardUnknown() { - xxx_messageInfo_Version.DiscardUnknown(m) + +func (*Version) ProtoMessage() {} + +func (x *Version) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_Version proto.InternalMessageInfo +// Deprecated: Use Version.ProtoReflect.Descriptor instead. +func (*Version) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{2} +} -func (m *Version) GetMajor() int32 { - if m != nil { - return m.Major +func (x *Version) GetMajor() int32 { + if x != nil { + return x.Major } return 0 } -func (m *Version) GetMinor() int32 { - if m != nil { - return m.Minor +func (x *Version) GetMinor() int32 { + if x != nil { + return x.Minor } return 0 } -func (m *Version) GetPatch() int32 { - if m != nil { - return m.Patch +func (x *Version) GetPatch() int32 { + if x != nil { + return x.Patch } return 0 } -func (m *Version) GetBuild() string { - if m != nil { - return m.Build +func (x *Version) GetBuild() string { + if x != nil { + return x.Build } return "" } -func (m *Version) GetProductName() string { - if m != nil { - return m.ProductName +func (x *Version) GetProductName() string { + if x != nil { + return x.ProductName } return "" } -func (m *Version) GetVersionStr() string { - if m != nil { - return m.VersionStr +func (x *Version) GetVersionStr() string { + if x != nil { + return x.VersionStr } return "" } type GetFrameworkInfoReply struct { - FrameworkId string `protobuf:"bytes,1,opt,name=frameworkId,proto3" json:"frameworkId,omitempty"` - EnvironmentsCount int32 `protobuf:"varint,2,opt,name=environmentsCount,proto3" json:"environmentsCount,omitempty"` - TasksCount int32 `protobuf:"varint,3,opt,name=tasksCount,proto3" json:"tasksCount,omitempty"` - State string `protobuf:"bytes,4,opt,name=state,proto3" json:"state,omitempty"` - HostsCount int32 `protobuf:"varint,5,opt,name=hostsCount,proto3" json:"hostsCount,omitempty"` - InstanceName string `protobuf:"bytes,6,opt,name=instanceName,proto3" json:"instanceName,omitempty"` - Version *Version `protobuf:"bytes,7,opt,name=version,proto3" json:"version,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *GetFrameworkInfoReply) Reset() { *m = GetFrameworkInfoReply{} } -func (m *GetFrameworkInfoReply) String() string { return proto.CompactTextString(m) } -func (*GetFrameworkInfoReply) ProtoMessage() {} -func (*GetFrameworkInfoReply) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{6} -} -func (m *GetFrameworkInfoReply) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *GetFrameworkInfoReply) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_GetFrameworkInfoReply.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err - } - return b[:n], nil - } + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + FrameworkId string `protobuf:"bytes,1,opt,name=frameworkId,proto3" json:"frameworkId,omitempty"` + EnvironmentsCount int32 `protobuf:"varint,2,opt,name=environmentsCount,proto3" json:"environmentsCount,omitempty"` + TasksCount int32 `protobuf:"varint,3,opt,name=tasksCount,proto3" json:"tasksCount,omitempty"` + State string `protobuf:"bytes,4,opt,name=state,proto3" json:"state,omitempty"` + HostsCount int32 `protobuf:"varint,5,opt,name=hostsCount,proto3" json:"hostsCount,omitempty"` + InstanceName string `protobuf:"bytes,6,opt,name=instanceName,proto3" json:"instanceName,omitempty"` + Version *Version `protobuf:"bytes,7,opt,name=version,proto3" json:"version,omitempty"` + ConfigurationEndpoint string `protobuf:"bytes,8,opt,name=configurationEndpoint,proto3" json:"configurationEndpoint,omitempty"` + DetectorsInInstance []string `protobuf:"bytes,9,rep,name=detectorsInInstance,proto3" json:"detectorsInInstance,omitempty"` + ActiveDetectors []string `protobuf:"bytes,10,rep,name=activeDetectors,proto3" json:"activeDetectors,omitempty"` + AvailableDetectors []string `protobuf:"bytes,11,rep,name=availableDetectors,proto3" json:"availableDetectors,omitempty"` + Timestamp int64 `protobuf:"varint,12,opt,name=timestamp,proto3" json:"timestamp,omitempty"` // timestamp of when this object was sent in unix milliseconds } -func (m *GetFrameworkInfoReply) XXX_Merge(src proto.Message) { - xxx_messageInfo_GetFrameworkInfoReply.Merge(m, src) + +func (x *GetFrameworkInfoReply) Reset() { + *x = GetFrameworkInfoReply{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *GetFrameworkInfoReply) XXX_Size() int { - return m.Size() + +func (x *GetFrameworkInfoReply) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *GetFrameworkInfoReply) XXX_DiscardUnknown() { - xxx_messageInfo_GetFrameworkInfoReply.DiscardUnknown(m) + +func (*GetFrameworkInfoReply) ProtoMessage() {} + +func (x *GetFrameworkInfoReply) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_GetFrameworkInfoReply proto.InternalMessageInfo +// Deprecated: Use GetFrameworkInfoReply.ProtoReflect.Descriptor instead. +func (*GetFrameworkInfoReply) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{3} +} -func (m *GetFrameworkInfoReply) GetFrameworkId() string { - if m != nil { - return m.FrameworkId +func (x *GetFrameworkInfoReply) GetFrameworkId() string { + if x != nil { + return x.FrameworkId } return "" } -func (m *GetFrameworkInfoReply) GetEnvironmentsCount() int32 { - if m != nil { - return m.EnvironmentsCount +func (x *GetFrameworkInfoReply) GetEnvironmentsCount() int32 { + if x != nil { + return x.EnvironmentsCount } return 0 } -func (m *GetFrameworkInfoReply) GetTasksCount() int32 { - if m != nil { - return m.TasksCount +func (x *GetFrameworkInfoReply) GetTasksCount() int32 { + if x != nil { + return x.TasksCount } return 0 } -func (m *GetFrameworkInfoReply) GetState() string { - if m != nil { - return m.State +func (x *GetFrameworkInfoReply) GetState() string { + if x != nil { + return x.State } return "" } -func (m *GetFrameworkInfoReply) GetHostsCount() int32 { - if m != nil { - return m.HostsCount +func (x *GetFrameworkInfoReply) GetHostsCount() int32 { + if x != nil { + return x.HostsCount } return 0 } -func (m *GetFrameworkInfoReply) GetInstanceName() string { - if m != nil { - return m.InstanceName +func (x *GetFrameworkInfoReply) GetInstanceName() string { + if x != nil { + return x.InstanceName } return "" } -func (m *GetFrameworkInfoReply) GetVersion() *Version { - if m != nil { - return m.Version +func (x *GetFrameworkInfoReply) GetVersion() *Version { + if x != nil { + return x.Version } return nil } -// Not implemented yet -type TeardownRequest struct { - Reason string `protobuf:"bytes,1,opt,name=reason,proto3" json:"reason,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` +func (x *GetFrameworkInfoReply) GetConfigurationEndpoint() string { + if x != nil { + return x.ConfigurationEndpoint + } + return "" } -func (m *TeardownRequest) Reset() { *m = TeardownRequest{} } -func (m *TeardownRequest) String() string { return proto.CompactTextString(m) } -func (*TeardownRequest) ProtoMessage() {} -func (*TeardownRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{7} -} -func (m *TeardownRequest) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *TeardownRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_TeardownRequest.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err - } - return b[:n], nil +func (x *GetFrameworkInfoReply) GetDetectorsInInstance() []string { + if x != nil { + return x.DetectorsInInstance } + return nil } -func (m *TeardownRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_TeardownRequest.Merge(m, src) + +func (x *GetFrameworkInfoReply) GetActiveDetectors() []string { + if x != nil { + return x.ActiveDetectors + } + return nil } -func (m *TeardownRequest) XXX_Size() int { - return m.Size() + +func (x *GetFrameworkInfoReply) GetAvailableDetectors() []string { + if x != nil { + return x.AvailableDetectors + } + return nil } -func (m *TeardownRequest) XXX_DiscardUnknown() { - xxx_messageInfo_TeardownRequest.DiscardUnknown(m) + +func (x *GetFrameworkInfoReply) GetTimestamp() int64 { + if x != nil { + return x.Timestamp + } + return 0 } -var xxx_messageInfo_TeardownRequest proto.InternalMessageInfo +// Not implemented yet +type TeardownRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Reason string `protobuf:"bytes,1,opt,name=reason,proto3" json:"reason,omitempty"` +} -func (m *TeardownRequest) GetReason() string { - if m != nil { - return m.Reason +func (x *TeardownRequest) Reset() { + *x = TeardownRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return "" } -type TeardownReply struct { - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` +func (x *TeardownRequest) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *TeardownReply) Reset() { *m = TeardownReply{} } -func (m *TeardownReply) String() string { return proto.CompactTextString(m) } -func (*TeardownReply) ProtoMessage() {} -func (*TeardownReply) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{8} -} -func (m *TeardownReply) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *TeardownReply) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_TeardownReply.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err +func (*TeardownRequest) ProtoMessage() {} + +func (x *TeardownRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) } - return b[:n], nil + return ms } + return mi.MessageOf(x) } -func (m *TeardownReply) XXX_Merge(src proto.Message) { - xxx_messageInfo_TeardownReply.Merge(m, src) + +// Deprecated: Use TeardownRequest.ProtoReflect.Descriptor instead. +func (*TeardownRequest) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{4} } -func (m *TeardownReply) XXX_Size() int { - return m.Size() + +func (x *TeardownRequest) GetReason() string { + if x != nil { + return x.Reason + } + return "" } -func (m *TeardownReply) XXX_DiscardUnknown() { - xxx_messageInfo_TeardownReply.DiscardUnknown(m) + +type TeardownReply struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields } -var xxx_messageInfo_TeardownReply proto.InternalMessageInfo +func (x *TeardownReply) Reset() { + *x = TeardownReply{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} -//////////////////////////////////////// -// Environment -//////////////////////////////////////// -type GetEnvironmentsRequest struct { - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` +func (x *TeardownReply) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *GetEnvironmentsRequest) Reset() { *m = GetEnvironmentsRequest{} } -func (m *GetEnvironmentsRequest) String() string { return proto.CompactTextString(m) } -func (*GetEnvironmentsRequest) ProtoMessage() {} -func (*GetEnvironmentsRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{9} -} -func (m *GetEnvironmentsRequest) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *GetEnvironmentsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_GetEnvironmentsRequest.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err +func (*TeardownReply) ProtoMessage() {} + +func (x *TeardownReply) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) } - return b[:n], nil + return ms } + return mi.MessageOf(x) } -func (m *GetEnvironmentsRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_GetEnvironmentsRequest.Merge(m, src) -} -func (m *GetEnvironmentsRequest) XXX_Size() int { - return m.Size() -} -func (m *GetEnvironmentsRequest) XXX_DiscardUnknown() { - xxx_messageInfo_GetEnvironmentsRequest.DiscardUnknown(m) + +// Deprecated: Use TeardownReply.ProtoReflect.Descriptor instead. +func (*TeardownReply) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{5} } -var xxx_messageInfo_GetEnvironmentsRequest proto.InternalMessageInfo +// ////////////////////////////////////// +// Environment +// ////////////////////////////////////// +type GetEnvironmentsRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields -type GetEnvironmentsReply struct { - FrameworkId string `protobuf:"bytes,1,opt,name=frameworkId,proto3" json:"frameworkId,omitempty"` - Environments []*EnvironmentInfo `protobuf:"bytes,2,rep,name=environments,proto3" json:"environments,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + ShowAll bool `protobuf:"varint,1,opt,name=showAll,proto3" json:"showAll,omitempty"` + ShowTaskInfos bool `protobuf:"varint,2,opt,name=showTaskInfos,proto3" json:"showTaskInfos,omitempty"` + ShowDetailedIntegratedServices bool `protobuf:"varint,3,opt,name=showDetailedIntegratedServices,proto3" json:"showDetailedIntegratedServices,omitempty"` // integratedServices are returned everytime, setting this flag gives detailed report } -func (m *GetEnvironmentsReply) Reset() { *m = GetEnvironmentsReply{} } -func (m *GetEnvironmentsReply) String() string { return proto.CompactTextString(m) } -func (*GetEnvironmentsReply) ProtoMessage() {} -func (*GetEnvironmentsReply) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{10} -} -func (m *GetEnvironmentsReply) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *GetEnvironmentsReply) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_GetEnvironmentsReply.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err - } - return b[:n], nil +func (x *GetEnvironmentsRequest) Reset() { + *x = GetEnvironmentsRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } } -func (m *GetEnvironmentsReply) XXX_Merge(src proto.Message) { - xxx_messageInfo_GetEnvironmentsReply.Merge(m, src) -} -func (m *GetEnvironmentsReply) XXX_Size() int { - return m.Size() + +func (x *GetEnvironmentsRequest) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *GetEnvironmentsReply) XXX_DiscardUnknown() { - xxx_messageInfo_GetEnvironmentsReply.DiscardUnknown(m) + +func (*GetEnvironmentsRequest) ProtoMessage() {} + +func (x *GetEnvironmentsRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_GetEnvironmentsReply proto.InternalMessageInfo +// Deprecated: Use GetEnvironmentsRequest.ProtoReflect.Descriptor instead. +func (*GetEnvironmentsRequest) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{6} +} -func (m *GetEnvironmentsReply) GetFrameworkId() string { - if m != nil { - return m.FrameworkId +func (x *GetEnvironmentsRequest) GetShowAll() bool { + if x != nil { + return x.ShowAll } - return "" + return false } -func (m *GetEnvironmentsReply) GetEnvironments() []*EnvironmentInfo { - if m != nil { - return m.Environments +func (x *GetEnvironmentsRequest) GetShowTaskInfos() bool { + if x != nil { + return x.ShowTaskInfos } - return nil + return false } -type EnvironmentInfo struct { - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - CreatedWhen string `protobuf:"bytes,2,opt,name=createdWhen,proto3" json:"createdWhen,omitempty"` - State string `protobuf:"bytes,3,opt,name=state,proto3" json:"state,omitempty"` - Tasks []*ShortTaskInfo `protobuf:"bytes,4,rep,name=tasks,proto3" json:"tasks,omitempty"` - RootRole string `protobuf:"bytes,5,opt,name=rootRole,proto3" json:"rootRole,omitempty"` - CurrentRunNumber uint32 `protobuf:"varint,6,opt,name=currentRunNumber,proto3" json:"currentRunNumber,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *EnvironmentInfo) Reset() { *m = EnvironmentInfo{} } -func (m *EnvironmentInfo) String() string { return proto.CompactTextString(m) } -func (*EnvironmentInfo) ProtoMessage() {} -func (*EnvironmentInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{11} -} -func (m *EnvironmentInfo) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *EnvironmentInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_EnvironmentInfo.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err - } - return b[:n], nil +func (x *GetEnvironmentsRequest) GetShowDetailedIntegratedServices() bool { + if x != nil { + return x.ShowDetailedIntegratedServices } + return false } -func (m *EnvironmentInfo) XXX_Merge(src proto.Message) { - xxx_messageInfo_EnvironmentInfo.Merge(m, src) + +type GetEnvironmentsReply struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + FrameworkId string `protobuf:"bytes,1,opt,name=frameworkId,proto3" json:"frameworkId,omitempty"` + Environments []*EnvironmentInfo `protobuf:"bytes,2,rep,name=environments,proto3" json:"environments,omitempty"` + Timestamp int64 `protobuf:"varint,3,opt,name=timestamp,proto3" json:"timestamp,omitempty"` // timestamp of when this object was sent in unix milliseconds } -func (m *EnvironmentInfo) XXX_Size() int { - return m.Size() + +func (x *GetEnvironmentsReply) Reset() { + *x = GetEnvironmentsReply{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *EnvironmentInfo) XXX_DiscardUnknown() { - xxx_messageInfo_EnvironmentInfo.DiscardUnknown(m) + +func (x *GetEnvironmentsReply) String() string { + return protoimpl.X.MessageStringOf(x) } -var xxx_messageInfo_EnvironmentInfo proto.InternalMessageInfo +func (*GetEnvironmentsReply) ProtoMessage() {} -func (m *EnvironmentInfo) GetId() string { - if m != nil { - return m.Id +func (x *GetEnvironmentsReply) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms } - return "" + return mi.MessageOf(x) } -func (m *EnvironmentInfo) GetCreatedWhen() string { - if m != nil { - return m.CreatedWhen - } - return "" +// Deprecated: Use GetEnvironmentsReply.ProtoReflect.Descriptor instead. +func (*GetEnvironmentsReply) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{7} } -func (m *EnvironmentInfo) GetState() string { - if m != nil { - return m.State +func (x *GetEnvironmentsReply) GetFrameworkId() string { + if x != nil { + return x.FrameworkId } return "" } -func (m *EnvironmentInfo) GetTasks() []*ShortTaskInfo { - if m != nil { - return m.Tasks +func (x *GetEnvironmentsReply) GetEnvironments() []*EnvironmentInfo { + if x != nil { + return x.Environments } return nil } -func (m *EnvironmentInfo) GetRootRole() string { - if m != nil { - return m.RootRole - } - return "" -} - -func (m *EnvironmentInfo) GetCurrentRunNumber() uint32 { - if m != nil { - return m.CurrentRunNumber +func (x *GetEnvironmentsReply) GetTimestamp() int64 { + if x != nil { + return x.Timestamp } return 0 } -type NewEnvironmentRequest struct { - WorkflowTemplate string `protobuf:"bytes,1,opt,name=workflowTemplate,proto3" json:"workflowTemplate,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` +type EnvironmentInfo struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + CreatedWhen int64 `protobuf:"varint,2,opt,name=createdWhen,proto3" json:"createdWhen,omitempty"` // msec + State string `protobuf:"bytes,3,opt,name=state,proto3" json:"state,omitempty"` + Tasks []*ShortTaskInfo `protobuf:"bytes,4,rep,name=tasks,proto3" json:"tasks,omitempty"` + RootRole string `protobuf:"bytes,5,opt,name=rootRole,proto3" json:"rootRole,omitempty"` + CurrentRunNumber uint32 `protobuf:"varint,6,opt,name=currentRunNumber,proto3" json:"currentRunNumber,omitempty"` + Defaults map[string]string `protobuf:"bytes,7,rep,name=defaults,proto3" json:"defaults,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Vars map[string]string `protobuf:"bytes,8,rep,name=vars,proto3" json:"vars,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + UserVars map[string]string `protobuf:"bytes,9,rep,name=userVars,proto3" json:"userVars,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + NumberOfFlps int32 `protobuf:"varint,10,opt,name=numberOfFlps,proto3" json:"numberOfFlps,omitempty"` + IncludedDetectors []string `protobuf:"bytes,11,rep,name=includedDetectors,proto3" json:"includedDetectors,omitempty"` + Description string `protobuf:"bytes,12,opt,name=description,proto3" json:"description,omitempty"` + NumberOfHosts int32 `protobuf:"varint,13,opt,name=numberOfHosts,proto3" json:"numberOfHosts,omitempty"` + IntegratedServicesData map[string]string `protobuf:"bytes,14,rep,name=integratedServicesData,proto3" json:"integratedServicesData,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + NumberOfTasks int32 `protobuf:"varint,15,opt,name=numberOfTasks,proto3" json:"numberOfTasks,omitempty"` + CurrentTransition string `protobuf:"bytes,16,opt,name=currentTransition,proto3" json:"currentTransition,omitempty"` + NumberOfActiveTasks int32 `protobuf:"varint,17,opt,name=numberOfActiveTasks,proto3" json:"numberOfActiveTasks,omitempty"` + NumberOfInactiveTasks int32 `protobuf:"varint,18,opt,name=numberOfInactiveTasks,proto3" json:"numberOfInactiveTasks,omitempty"` } -func (m *NewEnvironmentRequest) Reset() { *m = NewEnvironmentRequest{} } -func (m *NewEnvironmentRequest) String() string { return proto.CompactTextString(m) } -func (*NewEnvironmentRequest) ProtoMessage() {} -func (*NewEnvironmentRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{12} -} -func (m *NewEnvironmentRequest) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *NewEnvironmentRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_NewEnvironmentRequest.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err - } - return b[:n], nil +func (x *EnvironmentInfo) Reset() { + *x = EnvironmentInfo{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } } -func (m *NewEnvironmentRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_NewEnvironmentRequest.Merge(m, src) -} -func (m *NewEnvironmentRequest) XXX_Size() int { - return m.Size() -} -func (m *NewEnvironmentRequest) XXX_DiscardUnknown() { - xxx_messageInfo_NewEnvironmentRequest.DiscardUnknown(m) + +func (x *EnvironmentInfo) String() string { + return protoimpl.X.MessageStringOf(x) } -var xxx_messageInfo_NewEnvironmentRequest proto.InternalMessageInfo +func (*EnvironmentInfo) ProtoMessage() {} -func (m *NewEnvironmentRequest) GetWorkflowTemplate() string { - if m != nil { - return m.WorkflowTemplate +func (x *EnvironmentInfo) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[8] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms } - return "" + return mi.MessageOf(x) } -type NewEnvironmentReply struct { - Environment *EnvironmentInfo `protobuf:"bytes,1,opt,name=environment,proto3" json:"environment,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` +// Deprecated: Use EnvironmentInfo.ProtoReflect.Descriptor instead. +func (*EnvironmentInfo) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{8} } -func (m *NewEnvironmentReply) Reset() { *m = NewEnvironmentReply{} } -func (m *NewEnvironmentReply) String() string { return proto.CompactTextString(m) } -func (*NewEnvironmentReply) ProtoMessage() {} -func (*NewEnvironmentReply) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{13} -} -func (m *NewEnvironmentReply) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *NewEnvironmentReply) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_NewEnvironmentReply.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err - } - return b[:n], nil +func (x *EnvironmentInfo) GetId() string { + if x != nil { + return x.Id } -} -func (m *NewEnvironmentReply) XXX_Merge(src proto.Message) { - xxx_messageInfo_NewEnvironmentReply.Merge(m, src) -} -func (m *NewEnvironmentReply) XXX_Size() int { - return m.Size() -} -func (m *NewEnvironmentReply) XXX_DiscardUnknown() { - xxx_messageInfo_NewEnvironmentReply.DiscardUnknown(m) + return "" } -var xxx_messageInfo_NewEnvironmentReply proto.InternalMessageInfo - -func (m *NewEnvironmentReply) GetEnvironment() *EnvironmentInfo { - if m != nil { - return m.Environment +func (x *EnvironmentInfo) GetCreatedWhen() int64 { + if x != nil { + return x.CreatedWhen } - return nil + return 0 } -type GetEnvironmentRequest struct { - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` +func (x *EnvironmentInfo) GetState() string { + if x != nil { + return x.State + } + return "" } -func (m *GetEnvironmentRequest) Reset() { *m = GetEnvironmentRequest{} } -func (m *GetEnvironmentRequest) String() string { return proto.CompactTextString(m) } -func (*GetEnvironmentRequest) ProtoMessage() {} -func (*GetEnvironmentRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{14} -} -func (m *GetEnvironmentRequest) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *GetEnvironmentRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_GetEnvironmentRequest.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err - } - return b[:n], nil +func (x *EnvironmentInfo) GetTasks() []*ShortTaskInfo { + if x != nil { + return x.Tasks } -} -func (m *GetEnvironmentRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_GetEnvironmentRequest.Merge(m, src) -} -func (m *GetEnvironmentRequest) XXX_Size() int { - return m.Size() -} -func (m *GetEnvironmentRequest) XXX_DiscardUnknown() { - xxx_messageInfo_GetEnvironmentRequest.DiscardUnknown(m) + return nil } -var xxx_messageInfo_GetEnvironmentRequest proto.InternalMessageInfo - -func (m *GetEnvironmentRequest) GetId() string { - if m != nil { - return m.Id +func (x *EnvironmentInfo) GetRootRole() string { + if x != nil { + return x.RootRole } return "" } -type GetEnvironmentReply struct { - Environment *EnvironmentInfo `protobuf:"bytes,1,opt,name=environment,proto3" json:"environment,omitempty"` - Workflow *RoleInfo `protobuf:"bytes,2,opt,name=workflow,proto3" json:"workflow,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` +func (x *EnvironmentInfo) GetCurrentRunNumber() uint32 { + if x != nil { + return x.CurrentRunNumber + } + return 0 } -func (m *GetEnvironmentReply) Reset() { *m = GetEnvironmentReply{} } -func (m *GetEnvironmentReply) String() string { return proto.CompactTextString(m) } -func (*GetEnvironmentReply) ProtoMessage() {} -func (*GetEnvironmentReply) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{15} -} -func (m *GetEnvironmentReply) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *GetEnvironmentReply) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_GetEnvironmentReply.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err - } - return b[:n], nil +func (x *EnvironmentInfo) GetDefaults() map[string]string { + if x != nil { + return x.Defaults } -} -func (m *GetEnvironmentReply) XXX_Merge(src proto.Message) { - xxx_messageInfo_GetEnvironmentReply.Merge(m, src) -} -func (m *GetEnvironmentReply) XXX_Size() int { - return m.Size() -} -func (m *GetEnvironmentReply) XXX_DiscardUnknown() { - xxx_messageInfo_GetEnvironmentReply.DiscardUnknown(m) + return nil } -var xxx_messageInfo_GetEnvironmentReply proto.InternalMessageInfo - -func (m *GetEnvironmentReply) GetEnvironment() *EnvironmentInfo { - if m != nil { - return m.Environment +func (x *EnvironmentInfo) GetVars() map[string]string { + if x != nil { + return x.Vars } return nil } -func (m *GetEnvironmentReply) GetWorkflow() *RoleInfo { - if m != nil { - return m.Workflow +func (x *EnvironmentInfo) GetUserVars() map[string]string { + if x != nil { + return x.UserVars } return nil } -type ControlEnvironmentRequest struct { - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - Type ControlEnvironmentRequest_Optype `protobuf:"varint,2,opt,name=type,proto3,enum=o2control.ControlEnvironmentRequest_Optype" json:"type,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` +func (x *EnvironmentInfo) GetNumberOfFlps() int32 { + if x != nil { + return x.NumberOfFlps + } + return 0 } -func (m *ControlEnvironmentRequest) Reset() { *m = ControlEnvironmentRequest{} } -func (m *ControlEnvironmentRequest) String() string { return proto.CompactTextString(m) } -func (*ControlEnvironmentRequest) ProtoMessage() {} -func (*ControlEnvironmentRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{16} -} -func (m *ControlEnvironmentRequest) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *ControlEnvironmentRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_ControlEnvironmentRequest.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err - } - return b[:n], nil +func (x *EnvironmentInfo) GetIncludedDetectors() []string { + if x != nil { + return x.IncludedDetectors } -} -func (m *ControlEnvironmentRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_ControlEnvironmentRequest.Merge(m, src) -} -func (m *ControlEnvironmentRequest) XXX_Size() int { - return m.Size() -} -func (m *ControlEnvironmentRequest) XXX_DiscardUnknown() { - xxx_messageInfo_ControlEnvironmentRequest.DiscardUnknown(m) + return nil } -var xxx_messageInfo_ControlEnvironmentRequest proto.InternalMessageInfo - -func (m *ControlEnvironmentRequest) GetId() string { - if m != nil { - return m.Id +func (x *EnvironmentInfo) GetDescription() string { + if x != nil { + return x.Description } return "" } -func (m *ControlEnvironmentRequest) GetType() ControlEnvironmentRequest_Optype { - if m != nil { - return m.Type +func (x *EnvironmentInfo) GetNumberOfHosts() int32 { + if x != nil { + return x.NumberOfHosts } - return ControlEnvironmentRequest_NOOP + return 0 } -type ControlEnvironmentReply struct { - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - State string `protobuf:"bytes,2,opt,name=state,proto3" json:"state,omitempty"` - CurrentRunNumber uint32 `protobuf:"varint,3,opt,name=currentRunNumber,proto3" json:"currentRunNumber,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` +func (x *EnvironmentInfo) GetIntegratedServicesData() map[string]string { + if x != nil { + return x.IntegratedServicesData + } + return nil } -func (m *ControlEnvironmentReply) Reset() { *m = ControlEnvironmentReply{} } -func (m *ControlEnvironmentReply) String() string { return proto.CompactTextString(m) } -func (*ControlEnvironmentReply) ProtoMessage() {} -func (*ControlEnvironmentReply) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{17} -} -func (m *ControlEnvironmentReply) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *ControlEnvironmentReply) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_ControlEnvironmentReply.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err - } - return b[:n], nil +func (x *EnvironmentInfo) GetNumberOfTasks() int32 { + if x != nil { + return x.NumberOfTasks } -} -func (m *ControlEnvironmentReply) XXX_Merge(src proto.Message) { - xxx_messageInfo_ControlEnvironmentReply.Merge(m, src) -} -func (m *ControlEnvironmentReply) XXX_Size() int { - return m.Size() -} -func (m *ControlEnvironmentReply) XXX_DiscardUnknown() { - xxx_messageInfo_ControlEnvironmentReply.DiscardUnknown(m) + return 0 } -var xxx_messageInfo_ControlEnvironmentReply proto.InternalMessageInfo - -func (m *ControlEnvironmentReply) GetId() string { - if m != nil { - return m.Id +func (x *EnvironmentInfo) GetCurrentTransition() string { + if x != nil { + return x.CurrentTransition } return "" } -func (m *ControlEnvironmentReply) GetState() string { - if m != nil { - return m.State +func (x *EnvironmentInfo) GetNumberOfActiveTasks() int32 { + if x != nil { + return x.NumberOfActiveTasks } - return "" + return 0 } -func (m *ControlEnvironmentReply) GetCurrentRunNumber() uint32 { - if m != nil { - return m.CurrentRunNumber +func (x *EnvironmentInfo) GetNumberOfInactiveTasks() int32 { + if x != nil { + return x.NumberOfInactiveTasks } return 0 } -type ModifyEnvironmentRequest struct { - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - Operations []*EnvironmentOperation `protobuf:"bytes,2,rep,name=operations,proto3" json:"operations,omitempty"` - ReconfigureAll bool `protobuf:"varint,3,opt,name=reconfigureAll,proto3" json:"reconfigureAll,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` +type NewEnvironmentRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + WorkflowTemplate string `protobuf:"bytes,1,opt,name=workflowTemplate,proto3" json:"workflowTemplate,omitempty"` + Vars map[string]string `protobuf:"bytes,2,rep,name=vars,proto3" json:"vars,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Public bool `protobuf:"varint,3,opt,name=public,proto3" json:"public,omitempty"` + AutoTransition bool `protobuf:"varint,4,opt,name=autoTransition,proto3" json:"autoTransition,omitempty"` + RequestUser *protos.User `protobuf:"bytes,5,opt,name=requestUser,proto3" json:"requestUser,omitempty"` } -func (m *ModifyEnvironmentRequest) Reset() { *m = ModifyEnvironmentRequest{} } -func (m *ModifyEnvironmentRequest) String() string { return proto.CompactTextString(m) } -func (*ModifyEnvironmentRequest) ProtoMessage() {} -func (*ModifyEnvironmentRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{18} -} -func (m *ModifyEnvironmentRequest) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *ModifyEnvironmentRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_ModifyEnvironmentRequest.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err - } - return b[:n], nil +func (x *NewEnvironmentRequest) Reset() { + *x = NewEnvironmentRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } } -func (m *ModifyEnvironmentRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_ModifyEnvironmentRequest.Merge(m, src) -} -func (m *ModifyEnvironmentRequest) XXX_Size() int { - return m.Size() + +func (x *NewEnvironmentRequest) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *ModifyEnvironmentRequest) XXX_DiscardUnknown() { - xxx_messageInfo_ModifyEnvironmentRequest.DiscardUnknown(m) + +func (*NewEnvironmentRequest) ProtoMessage() {} + +func (x *NewEnvironmentRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[9] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_ModifyEnvironmentRequest proto.InternalMessageInfo +// Deprecated: Use NewEnvironmentRequest.ProtoReflect.Descriptor instead. +func (*NewEnvironmentRequest) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{9} +} -func (m *ModifyEnvironmentRequest) GetId() string { - if m != nil { - return m.Id +func (x *NewEnvironmentRequest) GetWorkflowTemplate() string { + if x != nil { + return x.WorkflowTemplate } return "" } -func (m *ModifyEnvironmentRequest) GetOperations() []*EnvironmentOperation { - if m != nil { - return m.Operations +func (x *NewEnvironmentRequest) GetVars() map[string]string { + if x != nil { + return x.Vars } return nil } -func (m *ModifyEnvironmentRequest) GetReconfigureAll() bool { - if m != nil { - return m.ReconfigureAll +func (x *NewEnvironmentRequest) GetPublic() bool { + if x != nil { + return x.Public } return false } -type EnvironmentOperation struct { - Type EnvironmentOperation_Optype `protobuf:"varint,1,opt,name=type,proto3,enum=o2control.EnvironmentOperation_Optype" json:"type,omitempty"` - RoleName string `protobuf:"bytes,2,opt,name=roleName,proto3" json:"roleName,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` +func (x *NewEnvironmentRequest) GetAutoTransition() bool { + if x != nil { + return x.AutoTransition + } + return false } -func (m *EnvironmentOperation) Reset() { *m = EnvironmentOperation{} } -func (m *EnvironmentOperation) String() string { return proto.CompactTextString(m) } -func (*EnvironmentOperation) ProtoMessage() {} -func (*EnvironmentOperation) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{19} -} -func (m *EnvironmentOperation) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *EnvironmentOperation) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_EnvironmentOperation.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err - } - return b[:n], nil +func (x *NewEnvironmentRequest) GetRequestUser() *protos.User { + if x != nil { + return x.RequestUser } -} -func (m *EnvironmentOperation) XXX_Merge(src proto.Message) { - xxx_messageInfo_EnvironmentOperation.Merge(m, src) -} -func (m *EnvironmentOperation) XXX_Size() int { - return m.Size() -} -func (m *EnvironmentOperation) XXX_DiscardUnknown() { - xxx_messageInfo_EnvironmentOperation.DiscardUnknown(m) + return nil } -var xxx_messageInfo_EnvironmentOperation proto.InternalMessageInfo +type NewEnvironmentReply struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields -func (m *EnvironmentOperation) GetType() EnvironmentOperation_Optype { - if m != nil { - return m.Type - } - return EnvironmentOperation_NOOP + Environment *EnvironmentInfo `protobuf:"bytes,1,opt,name=environment,proto3" json:"environment,omitempty"` + Public bool `protobuf:"varint,2,opt,name=public,proto3" json:"public,omitempty"` + Timestamp int64 `protobuf:"varint,3,opt,name=timestamp,proto3" json:"timestamp,omitempty"` // timestamp of when this object was sent in unix milliseconds } -func (m *EnvironmentOperation) GetRoleName() string { - if m != nil { - return m.RoleName +func (x *NewEnvironmentReply) Reset() { + *x = NewEnvironmentReply{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return "" } -type ModifyEnvironmentReply struct { - FailedOperations []*EnvironmentOperation `protobuf:"bytes,1,rep,name=failedOperations,proto3" json:"failedOperations,omitempty"` - Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"` - State string `protobuf:"bytes,3,opt,name=state,proto3" json:"state,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` +func (x *NewEnvironmentReply) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *ModifyEnvironmentReply) Reset() { *m = ModifyEnvironmentReply{} } -func (m *ModifyEnvironmentReply) String() string { return proto.CompactTextString(m) } -func (*ModifyEnvironmentReply) ProtoMessage() {} -func (*ModifyEnvironmentReply) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{20} -} -func (m *ModifyEnvironmentReply) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *ModifyEnvironmentReply) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_ModifyEnvironmentReply.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err +func (*NewEnvironmentReply) ProtoMessage() {} + +func (x *NewEnvironmentReply) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[10] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) } - return b[:n], nil + return ms } -} -func (m *ModifyEnvironmentReply) XXX_Merge(src proto.Message) { - xxx_messageInfo_ModifyEnvironmentReply.Merge(m, src) -} -func (m *ModifyEnvironmentReply) XXX_Size() int { - return m.Size() -} -func (m *ModifyEnvironmentReply) XXX_DiscardUnknown() { - xxx_messageInfo_ModifyEnvironmentReply.DiscardUnknown(m) + return mi.MessageOf(x) } -var xxx_messageInfo_ModifyEnvironmentReply proto.InternalMessageInfo +// Deprecated: Use NewEnvironmentReply.ProtoReflect.Descriptor instead. +func (*NewEnvironmentReply) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{10} +} -func (m *ModifyEnvironmentReply) GetFailedOperations() []*EnvironmentOperation { - if m != nil { - return m.FailedOperations +func (x *NewEnvironmentReply) GetEnvironment() *EnvironmentInfo { + if x != nil { + return x.Environment } return nil } -func (m *ModifyEnvironmentReply) GetId() string { - if m != nil { - return m.Id +func (x *NewEnvironmentReply) GetPublic() bool { + if x != nil { + return x.Public } - return "" + return false } -func (m *ModifyEnvironmentReply) GetState() string { - if m != nil { - return m.State +func (x *NewEnvironmentReply) GetTimestamp() int64 { + if x != nil { + return x.Timestamp } - return "" + return 0 } -type DestroyEnvironmentRequest struct { - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - KeepTasks bool `protobuf:"varint,2,opt,name=keepTasks,proto3" json:"keepTasks,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` +type NewAutoEnvironmentRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + WorkflowTemplate string `protobuf:"bytes,1,opt,name=workflowTemplate,proto3" json:"workflowTemplate,omitempty"` + Vars map[string]string `protobuf:"bytes,2,rep,name=vars,proto3" json:"vars,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Id string `protobuf:"bytes,3,opt,name=id,proto3" json:"id,omitempty"` + RequestUser *protos.User `protobuf:"bytes,4,opt,name=requestUser,proto3" json:"requestUser,omitempty"` } -func (m *DestroyEnvironmentRequest) Reset() { *m = DestroyEnvironmentRequest{} } -func (m *DestroyEnvironmentRequest) String() string { return proto.CompactTextString(m) } -func (*DestroyEnvironmentRequest) ProtoMessage() {} -func (*DestroyEnvironmentRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{21} -} -func (m *DestroyEnvironmentRequest) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *DestroyEnvironmentRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_DestroyEnvironmentRequest.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err - } - return b[:n], nil +func (x *NewAutoEnvironmentRequest) Reset() { + *x = NewAutoEnvironmentRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } } -func (m *DestroyEnvironmentRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_DestroyEnvironmentRequest.Merge(m, src) -} -func (m *DestroyEnvironmentRequest) XXX_Size() int { - return m.Size() + +func (x *NewAutoEnvironmentRequest) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *DestroyEnvironmentRequest) XXX_DiscardUnknown() { - xxx_messageInfo_DestroyEnvironmentRequest.DiscardUnknown(m) + +func (*NewAutoEnvironmentRequest) ProtoMessage() {} + +func (x *NewAutoEnvironmentRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[11] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_DestroyEnvironmentRequest proto.InternalMessageInfo +// Deprecated: Use NewAutoEnvironmentRequest.ProtoReflect.Descriptor instead. +func (*NewAutoEnvironmentRequest) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{11} +} -func (m *DestroyEnvironmentRequest) GetId() string { - if m != nil { - return m.Id +func (x *NewAutoEnvironmentRequest) GetWorkflowTemplate() string { + if x != nil { + return x.WorkflowTemplate } return "" } -func (m *DestroyEnvironmentRequest) GetKeepTasks() bool { - if m != nil { - return m.KeepTasks +func (x *NewAutoEnvironmentRequest) GetVars() map[string]string { + if x != nil { + return x.Vars } - return false + return nil } -type DestroyEnvironmentReply struct { - CleanupTasksReply *CleanupTasksReply `protobuf:"bytes,1,opt,name=cleanupTasksReply,proto3" json:"cleanupTasksReply,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` +func (x *NewAutoEnvironmentRequest) GetId() string { + if x != nil { + return x.Id + } + return "" } -func (m *DestroyEnvironmentReply) Reset() { *m = DestroyEnvironmentReply{} } -func (m *DestroyEnvironmentReply) String() string { return proto.CompactTextString(m) } -func (*DestroyEnvironmentReply) ProtoMessage() {} -func (*DestroyEnvironmentReply) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{22} -} -func (m *DestroyEnvironmentReply) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *DestroyEnvironmentReply) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_DestroyEnvironmentReply.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err - } - return b[:n], nil +func (x *NewAutoEnvironmentRequest) GetRequestUser() *protos.User { + if x != nil { + return x.RequestUser } + return nil } -func (m *DestroyEnvironmentReply) XXX_Merge(src proto.Message) { - xxx_messageInfo_DestroyEnvironmentReply.Merge(m, src) + +type NewAutoEnvironmentReply struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Timestamp int64 `protobuf:"varint,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"` // timestamp of when this object was sent in unix milliseconds } -func (m *DestroyEnvironmentReply) XXX_Size() int { - return m.Size() + +func (x *NewAutoEnvironmentReply) Reset() { + *x = NewAutoEnvironmentReply{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *DestroyEnvironmentReply) XXX_DiscardUnknown() { - xxx_messageInfo_DestroyEnvironmentReply.DiscardUnknown(m) + +func (x *NewAutoEnvironmentReply) String() string { + return protoimpl.X.MessageStringOf(x) } -var xxx_messageInfo_DestroyEnvironmentReply proto.InternalMessageInfo +func (*NewAutoEnvironmentReply) ProtoMessage() {} -func (m *DestroyEnvironmentReply) GetCleanupTasksReply() *CleanupTasksReply { - if m != nil { - return m.CleanupTasksReply +func (x *NewAutoEnvironmentReply) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[12] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms } - return nil + return mi.MessageOf(x) } -//////////////////////////////////////// -// Tasks -//////////////////////////////////////// -type ShortTaskInfo struct { - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - Locked bool `protobuf:"varint,2,opt,name=locked,proto3" json:"locked,omitempty"` - TaskId string `protobuf:"bytes,3,opt,name=taskId,proto3" json:"taskId,omitempty"` - Status string `protobuf:"bytes,4,opt,name=status,proto3" json:"status,omitempty"` - State string `protobuf:"bytes,5,opt,name=state,proto3" json:"state,omitempty"` - ClassName string `protobuf:"bytes,6,opt,name=className,proto3" json:"className,omitempty"` - DeploymentInfo *TaskDeploymentInfo `protobuf:"bytes,7,opt,name=deploymentInfo,proto3" json:"deploymentInfo,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *ShortTaskInfo) Reset() { *m = ShortTaskInfo{} } -func (m *ShortTaskInfo) String() string { return proto.CompactTextString(m) } -func (*ShortTaskInfo) ProtoMessage() {} -func (*ShortTaskInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{23} -} -func (m *ShortTaskInfo) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *ShortTaskInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_ShortTaskInfo.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err - } - return b[:n], nil +// Deprecated: Use NewAutoEnvironmentReply.ProtoReflect.Descriptor instead. +func (*NewAutoEnvironmentReply) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{12} +} + +func (x *NewAutoEnvironmentReply) GetTimestamp() int64 { + if x != nil { + return x.Timestamp } + return 0 } -func (m *ShortTaskInfo) XXX_Merge(src proto.Message) { - xxx_messageInfo_ShortTaskInfo.Merge(m, src) + +type GetEnvironmentRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + ShowWorkflowTree bool `protobuf:"varint,2,opt,name=showWorkflowTree,proto3" json:"showWorkflowTree,omitempty"` } -func (m *ShortTaskInfo) XXX_Size() int { - return m.Size() + +func (x *GetEnvironmentRequest) Reset() { + *x = GetEnvironmentRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *ShortTaskInfo) XXX_DiscardUnknown() { - xxx_messageInfo_ShortTaskInfo.DiscardUnknown(m) + +func (x *GetEnvironmentRequest) String() string { + return protoimpl.X.MessageStringOf(x) } -var xxx_messageInfo_ShortTaskInfo proto.InternalMessageInfo +func (*GetEnvironmentRequest) ProtoMessage() {} -func (m *ShortTaskInfo) GetName() string { - if m != nil { - return m.Name +func (x *GetEnvironmentRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[13] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms } - return "" + return mi.MessageOf(x) } -func (m *ShortTaskInfo) GetLocked() bool { - if m != nil { - return m.Locked - } - return false +// Deprecated: Use GetEnvironmentRequest.ProtoReflect.Descriptor instead. +func (*GetEnvironmentRequest) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{13} } -func (m *ShortTaskInfo) GetTaskId() string { - if m != nil { - return m.TaskId +func (x *GetEnvironmentRequest) GetId() string { + if x != nil { + return x.Id } return "" } -func (m *ShortTaskInfo) GetStatus() string { - if m != nil { - return m.Status +func (x *GetEnvironmentRequest) GetShowWorkflowTree() bool { + if x != nil { + return x.ShowWorkflowTree } - return "" + return false } -func (m *ShortTaskInfo) GetState() string { - if m != nil { - return m.State - } - return "" +type GetEnvironmentReply struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Environment *EnvironmentInfo `protobuf:"bytes,1,opt,name=environment,proto3" json:"environment,omitempty"` + Workflow *RoleInfo `protobuf:"bytes,2,opt,name=workflow,proto3" json:"workflow,omitempty"` + Public bool `protobuf:"varint,3,opt,name=public,proto3" json:"public,omitempty"` + Timestamp int64 `protobuf:"varint,4,opt,name=timestamp,proto3" json:"timestamp,omitempty"` // timestamp of when this object was sent in unix milliseconds } -func (m *ShortTaskInfo) GetClassName() string { - if m != nil { - return m.ClassName +func (x *GetEnvironmentReply) Reset() { + *x = GetEnvironmentReply{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return "" } -func (m *ShortTaskInfo) GetDeploymentInfo() *TaskDeploymentInfo { - if m != nil { - return m.DeploymentInfo - } - return nil +func (x *GetEnvironmentReply) String() string { + return protoimpl.X.MessageStringOf(x) } -type TaskDeploymentInfo struct { - Hostname string `protobuf:"bytes,1,opt,name=hostname,proto3" json:"hostname,omitempty"` - AgentId string `protobuf:"bytes,2,opt,name=agentId,proto3" json:"agentId,omitempty"` - OfferId string `protobuf:"bytes,3,opt,name=offerId,proto3" json:"offerId,omitempty"` - ExecutorId string `protobuf:"bytes,4,opt,name=executorId,proto3" json:"executorId,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *TaskDeploymentInfo) Reset() { *m = TaskDeploymentInfo{} } -func (m *TaskDeploymentInfo) String() string { return proto.CompactTextString(m) } -func (*TaskDeploymentInfo) ProtoMessage() {} -func (*TaskDeploymentInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{24} -} -func (m *TaskDeploymentInfo) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *TaskDeploymentInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_TaskDeploymentInfo.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err +func (*GetEnvironmentReply) ProtoMessage() {} + +func (x *GetEnvironmentReply) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[14] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) } - return b[:n], nil + return ms } -} -func (m *TaskDeploymentInfo) XXX_Merge(src proto.Message) { - xxx_messageInfo_TaskDeploymentInfo.Merge(m, src) -} -func (m *TaskDeploymentInfo) XXX_Size() int { - return m.Size() -} -func (m *TaskDeploymentInfo) XXX_DiscardUnknown() { - xxx_messageInfo_TaskDeploymentInfo.DiscardUnknown(m) + return mi.MessageOf(x) } -var xxx_messageInfo_TaskDeploymentInfo proto.InternalMessageInfo +// Deprecated: Use GetEnvironmentReply.ProtoReflect.Descriptor instead. +func (*GetEnvironmentReply) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{14} +} -func (m *TaskDeploymentInfo) GetHostname() string { - if m != nil { - return m.Hostname +func (x *GetEnvironmentReply) GetEnvironment() *EnvironmentInfo { + if x != nil { + return x.Environment } - return "" + return nil } -func (m *TaskDeploymentInfo) GetAgentId() string { - if m != nil { - return m.AgentId +func (x *GetEnvironmentReply) GetWorkflow() *RoleInfo { + if x != nil { + return x.Workflow } - return "" + return nil } -func (m *TaskDeploymentInfo) GetOfferId() string { - if m != nil { - return m.OfferId +func (x *GetEnvironmentReply) GetPublic() bool { + if x != nil { + return x.Public } - return "" + return false } -func (m *TaskDeploymentInfo) GetExecutorId() string { - if m != nil { - return m.ExecutorId +func (x *GetEnvironmentReply) GetTimestamp() int64 { + if x != nil { + return x.Timestamp } - return "" + return 0 } -type GetTasksRequest struct { - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` +type ControlEnvironmentRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Type ControlEnvironmentRequest_Optype `protobuf:"varint,2,opt,name=type,proto3,enum=o2control.ControlEnvironmentRequest_Optype" json:"type,omitempty"` + RequestUser *protos.User `protobuf:"bytes,3,opt,name=requestUser,proto3" json:"requestUser,omitempty"` } -func (m *GetTasksRequest) Reset() { *m = GetTasksRequest{} } -func (m *GetTasksRequest) String() string { return proto.CompactTextString(m) } -func (*GetTasksRequest) ProtoMessage() {} -func (*GetTasksRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{25} -} -func (m *GetTasksRequest) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *GetTasksRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_GetTasksRequest.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err - } - return b[:n], nil +func (x *ControlEnvironmentRequest) Reset() { + *x = ControlEnvironmentRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[15] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } } -func (m *GetTasksRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_GetTasksRequest.Merge(m, src) -} -func (m *GetTasksRequest) XXX_Size() int { - return m.Size() -} -func (m *GetTasksRequest) XXX_DiscardUnknown() { - xxx_messageInfo_GetTasksRequest.DiscardUnknown(m) -} -var xxx_messageInfo_GetTasksRequest proto.InternalMessageInfo - -type GetTasksReply struct { - Tasks []*ShortTaskInfo `protobuf:"bytes,1,rep,name=tasks,proto3" json:"tasks,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` +func (x *ControlEnvironmentRequest) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *GetTasksReply) Reset() { *m = GetTasksReply{} } -func (m *GetTasksReply) String() string { return proto.CompactTextString(m) } -func (*GetTasksReply) ProtoMessage() {} -func (*GetTasksReply) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{26} -} -func (m *GetTasksReply) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *GetTasksReply) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_GetTasksReply.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err +func (*ControlEnvironmentRequest) ProtoMessage() {} + +func (x *ControlEnvironmentRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[15] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) } - return b[:n], nil + return ms } + return mi.MessageOf(x) } -func (m *GetTasksReply) XXX_Merge(src proto.Message) { - xxx_messageInfo_GetTasksReply.Merge(m, src) -} -func (m *GetTasksReply) XXX_Size() int { - return m.Size() + +// Deprecated: Use ControlEnvironmentRequest.ProtoReflect.Descriptor instead. +func (*ControlEnvironmentRequest) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{15} } -func (m *GetTasksReply) XXX_DiscardUnknown() { - xxx_messageInfo_GetTasksReply.DiscardUnknown(m) + +func (x *ControlEnvironmentRequest) GetId() string { + if x != nil { + return x.Id + } + return "" } -var xxx_messageInfo_GetTasksReply proto.InternalMessageInfo +func (x *ControlEnvironmentRequest) GetType() ControlEnvironmentRequest_Optype { + if x != nil { + return x.Type + } + return ControlEnvironmentRequest_NOOP +} -func (m *GetTasksReply) GetTasks() []*ShortTaskInfo { - if m != nil { - return m.Tasks +func (x *ControlEnvironmentRequest) GetRequestUser() *protos.User { + if x != nil { + return x.RequestUser } return nil } -type GetTaskRequest struct { - TaskId string `protobuf:"bytes,1,opt,name=taskId,proto3" json:"taskId,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` +type ControlEnvironmentReply struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + State string `protobuf:"bytes,2,opt,name=state,proto3" json:"state,omitempty"` + CurrentRunNumber uint32 `protobuf:"varint,3,opt,name=currentRunNumber,proto3" json:"currentRunNumber,omitempty"` + // All times are in milliseconds + StartOfTransition int64 `protobuf:"varint,4,opt,name=startOfTransition,proto3" json:"startOfTransition,omitempty"` + EndOfTransition int64 `protobuf:"varint,5,opt,name=endOfTransition,proto3" json:"endOfTransition,omitempty"` + TransitionDuration int64 `protobuf:"varint,6,opt,name=transitionDuration,proto3" json:"transitionDuration,omitempty"` + Timestamp int64 `protobuf:"varint,7,opt,name=timestamp,proto3" json:"timestamp,omitempty"` // timestamp of when this object was sent in unix milliseconds } -func (m *GetTaskRequest) Reset() { *m = GetTaskRequest{} } -func (m *GetTaskRequest) String() string { return proto.CompactTextString(m) } -func (*GetTaskRequest) ProtoMessage() {} -func (*GetTaskRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{27} -} -func (m *GetTaskRequest) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *GetTaskRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_GetTaskRequest.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err - } - return b[:n], nil +func (x *ControlEnvironmentReply) Reset() { + *x = ControlEnvironmentReply{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[16] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } } -func (m *GetTaskRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_GetTaskRequest.Merge(m, src) -} -func (m *GetTaskRequest) XXX_Size() int { - return m.Size() -} -func (m *GetTaskRequest) XXX_DiscardUnknown() { - xxx_messageInfo_GetTaskRequest.DiscardUnknown(m) + +func (x *ControlEnvironmentReply) String() string { + return protoimpl.X.MessageStringOf(x) } -var xxx_messageInfo_GetTaskRequest proto.InternalMessageInfo +func (*ControlEnvironmentReply) ProtoMessage() {} -func (m *GetTaskRequest) GetTaskId() string { - if m != nil { - return m.TaskId +func (x *ControlEnvironmentReply) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[16] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms } - return "" + return mi.MessageOf(x) } -type GetTaskReply struct { - Task *TaskInfo `protobuf:"bytes,1,opt,name=task,proto3" json:"task,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` +// Deprecated: Use ControlEnvironmentReply.ProtoReflect.Descriptor instead. +func (*ControlEnvironmentReply) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{16} } -func (m *GetTaskReply) Reset() { *m = GetTaskReply{} } -func (m *GetTaskReply) String() string { return proto.CompactTextString(m) } -func (*GetTaskReply) ProtoMessage() {} -func (*GetTaskReply) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{28} -} -func (m *GetTaskReply) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *GetTaskReply) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_GetTaskReply.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err - } - return b[:n], nil +func (x *ControlEnvironmentReply) GetId() string { + if x != nil { + return x.Id } + return "" } -func (m *GetTaskReply) XXX_Merge(src proto.Message) { - xxx_messageInfo_GetTaskReply.Merge(m, src) -} -func (m *GetTaskReply) XXX_Size() int { - return m.Size() -} -func (m *GetTaskReply) XXX_DiscardUnknown() { - xxx_messageInfo_GetTaskReply.DiscardUnknown(m) -} - -var xxx_messageInfo_GetTaskReply proto.InternalMessageInfo -func (m *GetTaskReply) GetTask() *TaskInfo { - if m != nil { - return m.Task +func (x *ControlEnvironmentReply) GetState() string { + if x != nil { + return x.State } - return nil + return "" } -type TaskClassInfo struct { - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - ControlMode string `protobuf:"bytes,2,opt,name=controlMode,proto3" json:"controlMode,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` +func (x *ControlEnvironmentReply) GetCurrentRunNumber() uint32 { + if x != nil { + return x.CurrentRunNumber + } + return 0 } -func (m *TaskClassInfo) Reset() { *m = TaskClassInfo{} } -func (m *TaskClassInfo) String() string { return proto.CompactTextString(m) } -func (*TaskClassInfo) ProtoMessage() {} -func (*TaskClassInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{29} -} -func (m *TaskClassInfo) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *TaskClassInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_TaskClassInfo.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err - } - return b[:n], nil +func (x *ControlEnvironmentReply) GetStartOfTransition() int64 { + if x != nil { + return x.StartOfTransition } + return 0 } -func (m *TaskClassInfo) XXX_Merge(src proto.Message) { - xxx_messageInfo_TaskClassInfo.Merge(m, src) -} -func (m *TaskClassInfo) XXX_Size() int { - return m.Size() -} -func (m *TaskClassInfo) XXX_DiscardUnknown() { - xxx_messageInfo_TaskClassInfo.DiscardUnknown(m) -} - -var xxx_messageInfo_TaskClassInfo proto.InternalMessageInfo -func (m *TaskClassInfo) GetName() string { - if m != nil { - return m.Name +func (x *ControlEnvironmentReply) GetEndOfTransition() int64 { + if x != nil { + return x.EndOfTransition } - return "" + return 0 } -func (m *TaskClassInfo) GetControlMode() string { - if m != nil { - return m.ControlMode +func (x *ControlEnvironmentReply) GetTransitionDuration() int64 { + if x != nil { + return x.TransitionDuration } - return "" + return 0 } -type CommandInfo struct { - Env []string `protobuf:"bytes,1,rep,name=env,proto3" json:"env,omitempty"` - Shell bool `protobuf:"varint,2,opt,name=shell,proto3" json:"shell,omitempty"` - Value string `protobuf:"bytes,3,opt,name=value,proto3" json:"value,omitempty"` - Arguments []string `protobuf:"bytes,4,rep,name=arguments,proto3" json:"arguments,omitempty"` - User string `protobuf:"bytes,5,opt,name=user,proto3" json:"user,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *CommandInfo) Reset() { *m = CommandInfo{} } -func (m *CommandInfo) String() string { return proto.CompactTextString(m) } -func (*CommandInfo) ProtoMessage() {} -func (*CommandInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{30} -} -func (m *CommandInfo) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *CommandInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_CommandInfo.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err - } - return b[:n], nil +func (x *ControlEnvironmentReply) GetTimestamp() int64 { + if x != nil { + return x.Timestamp } + return 0 } -func (m *CommandInfo) XXX_Merge(src proto.Message) { - xxx_messageInfo_CommandInfo.Merge(m, src) + +type ModifyEnvironmentRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Operations []*EnvironmentOperation `protobuf:"bytes,2,rep,name=operations,proto3" json:"operations,omitempty"` + ReconfigureAll bool `protobuf:"varint,3,opt,name=reconfigureAll,proto3" json:"reconfigureAll,omitempty"` } -func (m *CommandInfo) XXX_Size() int { - return m.Size() + +func (x *ModifyEnvironmentRequest) Reset() { + *x = ModifyEnvironmentRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[17] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *CommandInfo) XXX_DiscardUnknown() { - xxx_messageInfo_CommandInfo.DiscardUnknown(m) + +func (x *ModifyEnvironmentRequest) String() string { + return protoimpl.X.MessageStringOf(x) } -var xxx_messageInfo_CommandInfo proto.InternalMessageInfo +func (*ModifyEnvironmentRequest) ProtoMessage() {} -func (m *CommandInfo) GetEnv() []string { - if m != nil { - return m.Env +func (x *ModifyEnvironmentRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[17] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms } - return nil + return mi.MessageOf(x) } -func (m *CommandInfo) GetShell() bool { - if m != nil { - return m.Shell - } - return false +// Deprecated: Use ModifyEnvironmentRequest.ProtoReflect.Descriptor instead. +func (*ModifyEnvironmentRequest) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{17} } -func (m *CommandInfo) GetValue() string { - if m != nil { - return m.Value +func (x *ModifyEnvironmentRequest) GetId() string { + if x != nil { + return x.Id } return "" } -func (m *CommandInfo) GetArguments() []string { - if m != nil { - return m.Arguments +func (x *ModifyEnvironmentRequest) GetOperations() []*EnvironmentOperation { + if x != nil { + return x.Operations } return nil } -func (m *CommandInfo) GetUser() string { - if m != nil { - return m.User +func (x *ModifyEnvironmentRequest) GetReconfigureAll() bool { + if x != nil { + return x.ReconfigureAll } - return "" + return false } -type ChannelInfo struct { - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - Type string `protobuf:"bytes,2,opt,name=type,proto3" json:"type,omitempty"` - Target string `protobuf:"bytes,3,opt,name=target,proto3" json:"target,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` +type EnvironmentOperation struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Type EnvironmentOperation_Optype `protobuf:"varint,1,opt,name=type,proto3,enum=o2control.EnvironmentOperation_Optype" json:"type,omitempty"` + RoleName string `protobuf:"bytes,2,opt,name=roleName,proto3" json:"roleName,omitempty"` } -func (m *ChannelInfo) Reset() { *m = ChannelInfo{} } -func (m *ChannelInfo) String() string { return proto.CompactTextString(m) } -func (*ChannelInfo) ProtoMessage() {} -func (*ChannelInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{31} -} -func (m *ChannelInfo) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *ChannelInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_ChannelInfo.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err - } - return b[:n], nil +func (x *EnvironmentOperation) Reset() { + *x = EnvironmentOperation{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[18] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } } -func (m *ChannelInfo) XXX_Merge(src proto.Message) { - xxx_messageInfo_ChannelInfo.Merge(m, src) -} -func (m *ChannelInfo) XXX_Size() int { - return m.Size() -} -func (m *ChannelInfo) XXX_DiscardUnknown() { - xxx_messageInfo_ChannelInfo.DiscardUnknown(m) + +func (x *EnvironmentOperation) String() string { + return protoimpl.X.MessageStringOf(x) } -var xxx_messageInfo_ChannelInfo proto.InternalMessageInfo +func (*EnvironmentOperation) ProtoMessage() {} -func (m *ChannelInfo) GetName() string { - if m != nil { - return m.Name +func (x *EnvironmentOperation) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[18] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms } - return "" + return mi.MessageOf(x) } -func (m *ChannelInfo) GetType() string { - if m != nil { - return m.Type - } - return "" +// Deprecated: Use EnvironmentOperation.ProtoReflect.Descriptor instead. +func (*EnvironmentOperation) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{18} } -func (m *ChannelInfo) GetTarget() string { - if m != nil { - return m.Target +func (x *EnvironmentOperation) GetType() EnvironmentOperation_Optype { + if x != nil { + return x.Type } - return "" + return EnvironmentOperation_NOOP } -type TaskInfo struct { - ShortInfo *ShortTaskInfo `protobuf:"bytes,1,opt,name=shortInfo,proto3" json:"shortInfo,omitempty"` - ClassInfo *TaskClassInfo `protobuf:"bytes,2,opt,name=classInfo,proto3" json:"classInfo,omitempty"` - InboundChannels []*ChannelInfo `protobuf:"bytes,3,rep,name=inboundChannels,proto3" json:"inboundChannels,omitempty"` - OutboundChannels []*ChannelInfo `protobuf:"bytes,4,rep,name=outboundChannels,proto3" json:"outboundChannels,omitempty"` - CommandInfo *CommandInfo `protobuf:"bytes,5,opt,name=commandInfo,proto3" json:"commandInfo,omitempty"` - TaskPath string `protobuf:"bytes,6,opt,name=taskPath,proto3" json:"taskPath,omitempty"` - EnvId string `protobuf:"bytes,7,opt,name=envId,proto3" json:"envId,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *TaskInfo) Reset() { *m = TaskInfo{} } -func (m *TaskInfo) String() string { return proto.CompactTextString(m) } -func (*TaskInfo) ProtoMessage() {} -func (*TaskInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{32} -} -func (m *TaskInfo) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *TaskInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_TaskInfo.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err - } - return b[:n], nil +func (x *EnvironmentOperation) GetRoleName() string { + if x != nil { + return x.RoleName } -} -func (m *TaskInfo) XXX_Merge(src proto.Message) { - xxx_messageInfo_TaskInfo.Merge(m, src) -} -func (m *TaskInfo) XXX_Size() int { - return m.Size() -} -func (m *TaskInfo) XXX_DiscardUnknown() { - xxx_messageInfo_TaskInfo.DiscardUnknown(m) + return "" } -var xxx_messageInfo_TaskInfo proto.InternalMessageInfo +type ModifyEnvironmentReply struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields -func (m *TaskInfo) GetShortInfo() *ShortTaskInfo { - if m != nil { - return m.ShortInfo - } - return nil + FailedOperations []*EnvironmentOperation `protobuf:"bytes,1,rep,name=failedOperations,proto3" json:"failedOperations,omitempty"` + Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"` + State string `protobuf:"bytes,3,opt,name=state,proto3" json:"state,omitempty"` } -func (m *TaskInfo) GetClassInfo() *TaskClassInfo { - if m != nil { - return m.ClassInfo +func (x *ModifyEnvironmentReply) Reset() { + *x = ModifyEnvironmentReply{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[19] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return nil } -func (m *TaskInfo) GetInboundChannels() []*ChannelInfo { - if m != nil { - return m.InboundChannels - } - return nil +func (x *ModifyEnvironmentReply) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *TaskInfo) GetOutboundChannels() []*ChannelInfo { - if m != nil { - return m.OutboundChannels +func (*ModifyEnvironmentReply) ProtoMessage() {} + +func (x *ModifyEnvironmentReply) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[19] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms } - return nil + return mi.MessageOf(x) +} + +// Deprecated: Use ModifyEnvironmentReply.ProtoReflect.Descriptor instead. +func (*ModifyEnvironmentReply) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{19} } -func (m *TaskInfo) GetCommandInfo() *CommandInfo { - if m != nil { - return m.CommandInfo +func (x *ModifyEnvironmentReply) GetFailedOperations() []*EnvironmentOperation { + if x != nil { + return x.FailedOperations } return nil } -func (m *TaskInfo) GetTaskPath() string { - if m != nil { - return m.TaskPath +func (x *ModifyEnvironmentReply) GetId() string { + if x != nil { + return x.Id } return "" } -func (m *TaskInfo) GetEnvId() string { - if m != nil { - return m.EnvId +func (x *ModifyEnvironmentReply) GetState() string { + if x != nil { + return x.State } return "" } -type CleanupTasksRequest struct { - TaskIds []string `protobuf:"bytes,1,rep,name=taskIds,proto3" json:"taskIds,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` +type DestroyEnvironmentRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + KeepTasks bool `protobuf:"varint,2,opt,name=keepTasks,proto3" json:"keepTasks,omitempty"` + AllowInRunningState bool `protobuf:"varint,3,opt,name=allowInRunningState,proto3" json:"allowInRunningState,omitempty"` + Force bool `protobuf:"varint,4,opt,name=force,proto3" json:"force,omitempty"` + RequestUser *protos.User `protobuf:"bytes,5,opt,name=requestUser,proto3" json:"requestUser,omitempty"` } -func (m *CleanupTasksRequest) Reset() { *m = CleanupTasksRequest{} } -func (m *CleanupTasksRequest) String() string { return proto.CompactTextString(m) } -func (*CleanupTasksRequest) ProtoMessage() {} -func (*CleanupTasksRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{33} -} -func (m *CleanupTasksRequest) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *CleanupTasksRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_CleanupTasksRequest.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err - } - return b[:n], nil +func (x *DestroyEnvironmentRequest) Reset() { + *x = DestroyEnvironmentRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[20] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } } -func (m *CleanupTasksRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_CleanupTasksRequest.Merge(m, src) -} -func (m *CleanupTasksRequest) XXX_Size() int { - return m.Size() -} -func (m *CleanupTasksRequest) XXX_DiscardUnknown() { - xxx_messageInfo_CleanupTasksRequest.DiscardUnknown(m) + +func (x *DestroyEnvironmentRequest) String() string { + return protoimpl.X.MessageStringOf(x) } -var xxx_messageInfo_CleanupTasksRequest proto.InternalMessageInfo +func (*DestroyEnvironmentRequest) ProtoMessage() {} -func (m *CleanupTasksRequest) GetTaskIds() []string { - if m != nil { - return m.TaskIds +func (x *DestroyEnvironmentRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[20] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms } - return nil + return mi.MessageOf(x) } -type CleanupTasksReply struct { - KilledTasks []*ShortTaskInfo `protobuf:"bytes,1,rep,name=killedTasks,proto3" json:"killedTasks,omitempty"` - RunningTasks []*ShortTaskInfo `protobuf:"bytes,2,rep,name=runningTasks,proto3" json:"runningTasks,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` +// Deprecated: Use DestroyEnvironmentRequest.ProtoReflect.Descriptor instead. +func (*DestroyEnvironmentRequest) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{20} } -func (m *CleanupTasksReply) Reset() { *m = CleanupTasksReply{} } -func (m *CleanupTasksReply) String() string { return proto.CompactTextString(m) } -func (*CleanupTasksReply) ProtoMessage() {} -func (*CleanupTasksReply) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{34} -} -func (m *CleanupTasksReply) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *CleanupTasksReply) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_CleanupTasksReply.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err - } - return b[:n], nil +func (x *DestroyEnvironmentRequest) GetId() string { + if x != nil { + return x.Id } + return "" } -func (m *CleanupTasksReply) XXX_Merge(src proto.Message) { - xxx_messageInfo_CleanupTasksReply.Merge(m, src) -} -func (m *CleanupTasksReply) XXX_Size() int { - return m.Size() -} -func (m *CleanupTasksReply) XXX_DiscardUnknown() { - xxx_messageInfo_CleanupTasksReply.DiscardUnknown(m) + +func (x *DestroyEnvironmentRequest) GetKeepTasks() bool { + if x != nil { + return x.KeepTasks + } + return false } -var xxx_messageInfo_CleanupTasksReply proto.InternalMessageInfo +func (x *DestroyEnvironmentRequest) GetAllowInRunningState() bool { + if x != nil { + return x.AllowInRunningState + } + return false +} -func (m *CleanupTasksReply) GetKilledTasks() []*ShortTaskInfo { - if m != nil { - return m.KilledTasks +func (x *DestroyEnvironmentRequest) GetForce() bool { + if x != nil { + return x.Force } - return nil + return false } -func (m *CleanupTasksReply) GetRunningTasks() []*ShortTaskInfo { - if m != nil { - return m.RunningTasks +func (x *DestroyEnvironmentRequest) GetRequestUser() *protos.User { + if x != nil { + return x.RequestUser } return nil } -//////////////////////////////////////// -// Roles -//////////////////////////////////////// -type GetRolesRequest struct { - EnvId string `protobuf:"bytes,1,opt,name=envId,proto3" json:"envId,omitempty"` - PathSpec string `protobuf:"bytes,2,opt,name=pathSpec,proto3" json:"pathSpec,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` +type DestroyEnvironmentReply struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + CleanupTasksReply *CleanupTasksReply `protobuf:"bytes,1,opt,name=cleanupTasksReply,proto3" json:"cleanupTasksReply,omitempty"` + Timestamp int64 `protobuf:"varint,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"` // timestamp of when this object was sent in unix milliseconds } -func (m *GetRolesRequest) Reset() { *m = GetRolesRequest{} } -func (m *GetRolesRequest) String() string { return proto.CompactTextString(m) } -func (*GetRolesRequest) ProtoMessage() {} -func (*GetRolesRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{35} -} -func (m *GetRolesRequest) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *GetRolesRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_GetRolesRequest.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err - } - return b[:n], nil +func (x *DestroyEnvironmentReply) Reset() { + *x = DestroyEnvironmentReply{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[21] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } } -func (m *GetRolesRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_GetRolesRequest.Merge(m, src) -} -func (m *GetRolesRequest) XXX_Size() int { - return m.Size() -} -func (m *GetRolesRequest) XXX_DiscardUnknown() { - xxx_messageInfo_GetRolesRequest.DiscardUnknown(m) + +func (x *DestroyEnvironmentReply) String() string { + return protoimpl.X.MessageStringOf(x) } -var xxx_messageInfo_GetRolesRequest proto.InternalMessageInfo +func (*DestroyEnvironmentReply) ProtoMessage() {} -func (m *GetRolesRequest) GetEnvId() string { - if m != nil { - return m.EnvId +func (x *DestroyEnvironmentReply) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[21] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms } - return "" + return mi.MessageOf(x) +} + +// Deprecated: Use DestroyEnvironmentReply.ProtoReflect.Descriptor instead. +func (*DestroyEnvironmentReply) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{21} } -func (m *GetRolesRequest) GetPathSpec() string { - if m != nil { - return m.PathSpec +func (x *DestroyEnvironmentReply) GetCleanupTasksReply() *CleanupTasksReply { + if x != nil { + return x.CleanupTasksReply } - return "" + return nil } -type RoleInfo struct { - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - Status string `protobuf:"bytes,2,opt,name=status,proto3" json:"status,omitempty"` - State string `protobuf:"bytes,3,opt,name=state,proto3" json:"state,omitempty"` - FullPath string `protobuf:"bytes,4,opt,name=fullPath,proto3" json:"fullPath,omitempty"` - TaskIds []string `protobuf:"bytes,5,rep,name=taskIds,proto3" json:"taskIds,omitempty"` - Roles []*RoleInfo `protobuf:"bytes,6,rep,name=roles,proto3" json:"roles,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *RoleInfo) Reset() { *m = RoleInfo{} } -func (m *RoleInfo) String() string { return proto.CompactTextString(m) } -func (*RoleInfo) ProtoMessage() {} -func (*RoleInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{36} -} -func (m *RoleInfo) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *RoleInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_RoleInfo.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err - } - return b[:n], nil +func (x *DestroyEnvironmentReply) GetTimestamp() int64 { + if x != nil { + return x.Timestamp } + return 0 } -func (m *RoleInfo) XXX_Merge(src proto.Message) { - xxx_messageInfo_RoleInfo.Merge(m, src) + +type GetActiveDetectorsReply struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Detectors []string `protobuf:"bytes,1,rep,name=detectors,proto3" json:"detectors,omitempty"` + Timestamp int64 `protobuf:"varint,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"` // timestamp of when this object was sent in unix milliseconds } -func (m *RoleInfo) XXX_Size() int { - return m.Size() + +func (x *GetActiveDetectorsReply) Reset() { + *x = GetActiveDetectorsReply{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[22] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *RoleInfo) XXX_DiscardUnknown() { - xxx_messageInfo_RoleInfo.DiscardUnknown(m) + +func (x *GetActiveDetectorsReply) String() string { + return protoimpl.X.MessageStringOf(x) } -var xxx_messageInfo_RoleInfo proto.InternalMessageInfo +func (*GetActiveDetectorsReply) ProtoMessage() {} -func (m *RoleInfo) GetName() string { - if m != nil { - return m.Name +func (x *GetActiveDetectorsReply) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[22] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms } - return "" + return mi.MessageOf(x) } -func (m *RoleInfo) GetStatus() string { - if m != nil { - return m.Status - } - return "" +// Deprecated: Use GetActiveDetectorsReply.ProtoReflect.Descriptor instead. +func (*GetActiveDetectorsReply) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{22} } -func (m *RoleInfo) GetState() string { - if m != nil { - return m.State +func (x *GetActiveDetectorsReply) GetDetectors() []string { + if x != nil { + return x.Detectors } - return "" + return nil } -func (m *RoleInfo) GetFullPath() string { - if m != nil { - return m.FullPath +func (x *GetActiveDetectorsReply) GetTimestamp() int64 { + if x != nil { + return x.Timestamp } - return "" + return 0 } -func (m *RoleInfo) GetTaskIds() []string { - if m != nil { - return m.TaskIds - } - return nil +type GetAvailableDetectorsReply struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Detectors []string `protobuf:"bytes,1,rep,name=detectors,proto3" json:"detectors,omitempty"` + Timestamp int64 `protobuf:"varint,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"` // timestamp of when this object was sent in unix milliseconds } -func (m *RoleInfo) GetRoles() []*RoleInfo { - if m != nil { - return m.Roles +func (x *GetAvailableDetectorsReply) Reset() { + *x = GetAvailableDetectorsReply{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[23] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return nil } -type GetRolesReply struct { - Roles []*RoleInfo `protobuf:"bytes,1,rep,name=roles,proto3" json:"roles,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` +func (x *GetAvailableDetectorsReply) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *GetRolesReply) Reset() { *m = GetRolesReply{} } -func (m *GetRolesReply) String() string { return proto.CompactTextString(m) } -func (*GetRolesReply) ProtoMessage() {} -func (*GetRolesReply) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{37} -} -func (m *GetRolesReply) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *GetRolesReply) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_GetRolesReply.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err +func (*GetAvailableDetectorsReply) ProtoMessage() {} + +func (x *GetAvailableDetectorsReply) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[23] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) } - return b[:n], nil + return ms } + return mi.MessageOf(x) } -func (m *GetRolesReply) XXX_Merge(src proto.Message) { - xxx_messageInfo_GetRolesReply.Merge(m, src) + +// Deprecated: Use GetAvailableDetectorsReply.ProtoReflect.Descriptor instead. +func (*GetAvailableDetectorsReply) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{23} } -func (m *GetRolesReply) XXX_Size() int { - return m.Size() + +func (x *GetAvailableDetectorsReply) GetDetectors() []string { + if x != nil { + return x.Detectors + } + return nil } -func (m *GetRolesReply) XXX_DiscardUnknown() { - xxx_messageInfo_GetRolesReply.DiscardUnknown(m) + +func (x *GetAvailableDetectorsReply) GetTimestamp() int64 { + if x != nil { + return x.Timestamp + } + return 0 } -var xxx_messageInfo_GetRolesReply proto.InternalMessageInfo +// ////////////////////////////////////// +// Environment, GET/SET properties +// ////////////////////////////////////// +type SetEnvironmentPropertiesRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + // If properties == nil, the core sets nothing + // and reply ok + Properties map[string]string `protobuf:"bytes,2,rep,name=properties,proto3" json:"properties,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} -func (m *GetRolesReply) GetRoles() []*RoleInfo { - if m != nil { - return m.Roles +func (x *SetEnvironmentPropertiesRequest) Reset() { + *x = SetEnvironmentPropertiesRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[24] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return nil } -type GetWorkflowTemplatesRequest struct { - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` +func (x *SetEnvironmentPropertiesRequest) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *GetWorkflowTemplatesRequest) Reset() { *m = GetWorkflowTemplatesRequest{} } -func (m *GetWorkflowTemplatesRequest) String() string { return proto.CompactTextString(m) } -func (*GetWorkflowTemplatesRequest) ProtoMessage() {} -func (*GetWorkflowTemplatesRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{38} -} -func (m *GetWorkflowTemplatesRequest) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *GetWorkflowTemplatesRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_GetWorkflowTemplatesRequest.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err +func (*SetEnvironmentPropertiesRequest) ProtoMessage() {} + +func (x *SetEnvironmentPropertiesRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[24] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) } - return b[:n], nil + return ms } + return mi.MessageOf(x) } -func (m *GetWorkflowTemplatesRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_GetWorkflowTemplatesRequest.Merge(m, src) -} -func (m *GetWorkflowTemplatesRequest) XXX_Size() int { - return m.Size() + +// Deprecated: Use SetEnvironmentPropertiesRequest.ProtoReflect.Descriptor instead. +func (*SetEnvironmentPropertiesRequest) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{24} } -func (m *GetWorkflowTemplatesRequest) XXX_DiscardUnknown() { - xxx_messageInfo_GetWorkflowTemplatesRequest.DiscardUnknown(m) + +func (x *SetEnvironmentPropertiesRequest) GetId() string { + if x != nil { + return x.Id + } + return "" } -var xxx_messageInfo_GetWorkflowTemplatesRequest proto.InternalMessageInfo +func (x *SetEnvironmentPropertiesRequest) GetProperties() map[string]string { + if x != nil { + return x.Properties + } + return nil +} -type WorkflowTemplateInfo struct { - Repo string `protobuf:"bytes,1,opt,name=repo,proto3" json:"repo,omitempty"` - Template string `protobuf:"bytes,2,opt,name=template,proto3" json:"template,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` +type SetEnvironmentPropertiesReply struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields } -func (m *WorkflowTemplateInfo) Reset() { *m = WorkflowTemplateInfo{} } -func (m *WorkflowTemplateInfo) String() string { return proto.CompactTextString(m) } -func (*WorkflowTemplateInfo) ProtoMessage() {} -func (*WorkflowTemplateInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{39} -} -func (m *WorkflowTemplateInfo) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *WorkflowTemplateInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_WorkflowTemplateInfo.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err - } - return b[:n], nil +func (x *SetEnvironmentPropertiesReply) Reset() { + *x = SetEnvironmentPropertiesReply{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[25] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } } -func (m *WorkflowTemplateInfo) XXX_Merge(src proto.Message) { - xxx_messageInfo_WorkflowTemplateInfo.Merge(m, src) + +func (x *SetEnvironmentPropertiesReply) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *WorkflowTemplateInfo) XXX_Size() int { - return m.Size() + +func (*SetEnvironmentPropertiesReply) ProtoMessage() {} + +func (x *SetEnvironmentPropertiesReply) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[25] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -func (m *WorkflowTemplateInfo) XXX_DiscardUnknown() { - xxx_messageInfo_WorkflowTemplateInfo.DiscardUnknown(m) + +// Deprecated: Use SetEnvironmentPropertiesReply.ProtoReflect.Descriptor instead. +func (*SetEnvironmentPropertiesReply) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{25} } -var xxx_messageInfo_WorkflowTemplateInfo proto.InternalMessageInfo +type GetEnvironmentPropertiesRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields -func (m *WorkflowTemplateInfo) GetRepo() string { - if m != nil { - return m.Repo - } - return "" + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + // If len(queries) == 0, we return an + // empty map. + // To retrieve all KVs, use query '*' + Queries []string `protobuf:"bytes,2,rep,name=queries,proto3" json:"queries,omitempty"` + ExcludeGlobals bool `protobuf:"varint,3,opt,name=excludeGlobals,proto3" json:"excludeGlobals,omitempty"` } -func (m *WorkflowTemplateInfo) GetTemplate() string { - if m != nil { - return m.Template +func (x *GetEnvironmentPropertiesRequest) Reset() { + *x = GetEnvironmentPropertiesRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[26] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return "" } -type GetWorkflowTemplatesReply struct { - WorkflowTemplates []*WorkflowTemplateInfo `protobuf:"bytes,1,rep,name=workflowTemplates,proto3" json:"workflowTemplates,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` +func (x *GetEnvironmentPropertiesRequest) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *GetWorkflowTemplatesReply) Reset() { *m = GetWorkflowTemplatesReply{} } -func (m *GetWorkflowTemplatesReply) String() string { return proto.CompactTextString(m) } -func (*GetWorkflowTemplatesReply) ProtoMessage() {} -func (*GetWorkflowTemplatesReply) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{40} -} -func (m *GetWorkflowTemplatesReply) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *GetWorkflowTemplatesReply) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_GetWorkflowTemplatesReply.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err +func (*GetEnvironmentPropertiesRequest) ProtoMessage() {} + +func (x *GetEnvironmentPropertiesRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[26] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) } - return b[:n], nil + return ms } + return mi.MessageOf(x) +} + +// Deprecated: Use GetEnvironmentPropertiesRequest.ProtoReflect.Descriptor instead. +func (*GetEnvironmentPropertiesRequest) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{26} } -func (m *GetWorkflowTemplatesReply) XXX_Merge(src proto.Message) { - xxx_messageInfo_GetWorkflowTemplatesReply.Merge(m, src) + +func (x *GetEnvironmentPropertiesRequest) GetId() string { + if x != nil { + return x.Id + } + return "" } -func (m *GetWorkflowTemplatesReply) XXX_Size() int { - return m.Size() + +func (x *GetEnvironmentPropertiesRequest) GetQueries() []string { + if x != nil { + return x.Queries + } + return nil } -func (m *GetWorkflowTemplatesReply) XXX_DiscardUnknown() { - xxx_messageInfo_GetWorkflowTemplatesReply.DiscardUnknown(m) + +func (x *GetEnvironmentPropertiesRequest) GetExcludeGlobals() bool { + if x != nil { + return x.ExcludeGlobals + } + return false } -var xxx_messageInfo_GetWorkflowTemplatesReply proto.InternalMessageInfo +type GetEnvironmentPropertiesReply struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Properties map[string]string `protobuf:"bytes,1,rep,name=properties,proto3" json:"properties,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} -func (m *GetWorkflowTemplatesReply) GetWorkflowTemplates() []*WorkflowTemplateInfo { - if m != nil { - return m.WorkflowTemplates +func (x *GetEnvironmentPropertiesReply) Reset() { + *x = GetEnvironmentPropertiesReply{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[27] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return nil } -type ListReposRequest struct { - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` +func (x *GetEnvironmentPropertiesReply) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *ListReposRequest) Reset() { *m = ListReposRequest{} } -func (m *ListReposRequest) String() string { return proto.CompactTextString(m) } -func (*ListReposRequest) ProtoMessage() {} -func (*ListReposRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{41} -} -func (m *ListReposRequest) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *ListReposRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_ListReposRequest.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err +func (*GetEnvironmentPropertiesReply) ProtoMessage() {} + +func (x *GetEnvironmentPropertiesReply) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[27] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) } - return b[:n], nil + return ms } + return mi.MessageOf(x) } -func (m *ListReposRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_ListReposRequest.Merge(m, src) -} -func (m *ListReposRequest) XXX_Size() int { - return m.Size() + +// Deprecated: Use GetEnvironmentPropertiesReply.ProtoReflect.Descriptor instead. +func (*GetEnvironmentPropertiesReply) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{27} } -func (m *ListReposRequest) XXX_DiscardUnknown() { - xxx_messageInfo_ListReposRequest.DiscardUnknown(m) + +func (x *GetEnvironmentPropertiesReply) GetProperties() map[string]string { + if x != nil { + return x.Properties + } + return nil } -var xxx_messageInfo_ListReposRequest proto.InternalMessageInfo +// ////////////////////////////////////// +// Tasks +// ////////////////////////////////////// +type ShortTaskInfo struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields -type RepoInfo struct { - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - Default bool `protobuf:"varint,2,opt,name=default,proto3" json:"default,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Locked bool `protobuf:"varint,2,opt,name=locked,proto3" json:"locked,omitempty"` + TaskId string `protobuf:"bytes,3,opt,name=taskId,proto3" json:"taskId,omitempty"` + Status string `protobuf:"bytes,4,opt,name=status,proto3" json:"status,omitempty"` + State string `protobuf:"bytes,5,opt,name=state,proto3" json:"state,omitempty"` + ClassName string `protobuf:"bytes,6,opt,name=className,proto3" json:"className,omitempty"` + DeploymentInfo *TaskDeploymentInfo `protobuf:"bytes,7,opt,name=deploymentInfo,proto3" json:"deploymentInfo,omitempty"` + Pid string `protobuf:"bytes,8,opt,name=pid,proto3" json:"pid,omitempty"` + SandboxStdout string `protobuf:"bytes,9,opt,name=sandboxStdout,proto3" json:"sandboxStdout,omitempty"` + Claimable bool `protobuf:"varint,10,opt,name=claimable,proto3" json:"claimable,omitempty"` + Critical bool `protobuf:"varint,11,opt,name=critical,proto3" json:"critical,omitempty"` } -func (m *RepoInfo) Reset() { *m = RepoInfo{} } -func (m *RepoInfo) String() string { return proto.CompactTextString(m) } -func (*RepoInfo) ProtoMessage() {} -func (*RepoInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{42} -} -func (m *RepoInfo) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *RepoInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_RepoInfo.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err - } - return b[:n], nil +func (x *ShortTaskInfo) Reset() { + *x = ShortTaskInfo{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[28] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } } -func (m *RepoInfo) XXX_Merge(src proto.Message) { - xxx_messageInfo_RepoInfo.Merge(m, src) -} -func (m *RepoInfo) XXX_Size() int { - return m.Size() + +func (x *ShortTaskInfo) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *RepoInfo) XXX_DiscardUnknown() { - xxx_messageInfo_RepoInfo.DiscardUnknown(m) + +func (*ShortTaskInfo) ProtoMessage() {} + +func (x *ShortTaskInfo) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[28] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_RepoInfo proto.InternalMessageInfo +// Deprecated: Use ShortTaskInfo.ProtoReflect.Descriptor instead. +func (*ShortTaskInfo) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{28} +} -func (m *RepoInfo) GetName() string { - if m != nil { - return m.Name +func (x *ShortTaskInfo) GetName() string { + if x != nil { + return x.Name } return "" } -func (m *RepoInfo) GetDefault() bool { - if m != nil { - return m.Default +func (x *ShortTaskInfo) GetLocked() bool { + if x != nil { + return x.Locked } return false } -type ListReposReply struct { - Repos []*RepoInfo `protobuf:"bytes,1,rep,name=repos,proto3" json:"repos,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` +func (x *ShortTaskInfo) GetTaskId() string { + if x != nil { + return x.TaskId + } + return "" } -func (m *ListReposReply) Reset() { *m = ListReposReply{} } -func (m *ListReposReply) String() string { return proto.CompactTextString(m) } -func (*ListReposReply) ProtoMessage() {} -func (*ListReposReply) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{43} -} -func (m *ListReposReply) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *ListReposReply) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_ListReposReply.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err - } - return b[:n], nil +func (x *ShortTaskInfo) GetStatus() string { + if x != nil { + return x.Status } + return "" } -func (m *ListReposReply) XXX_Merge(src proto.Message) { - xxx_messageInfo_ListReposReply.Merge(m, src) -} -func (m *ListReposReply) XXX_Size() int { - return m.Size() -} -func (m *ListReposReply) XXX_DiscardUnknown() { - xxx_messageInfo_ListReposReply.DiscardUnknown(m) + +func (x *ShortTaskInfo) GetState() string { + if x != nil { + return x.State + } + return "" } -var xxx_messageInfo_ListReposReply proto.InternalMessageInfo +func (x *ShortTaskInfo) GetClassName() string { + if x != nil { + return x.ClassName + } + return "" +} -func (m *ListReposReply) GetRepos() []*RepoInfo { - if m != nil { - return m.Repos +func (x *ShortTaskInfo) GetDeploymentInfo() *TaskDeploymentInfo { + if x != nil { + return x.DeploymentInfo } return nil } -type AddRepoRequest struct { - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` +func (x *ShortTaskInfo) GetPid() string { + if x != nil { + return x.Pid + } + return "" } -func (m *AddRepoRequest) Reset() { *m = AddRepoRequest{} } -func (m *AddRepoRequest) String() string { return proto.CompactTextString(m) } -func (*AddRepoRequest) ProtoMessage() {} -func (*AddRepoRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{44} -} -func (m *AddRepoRequest) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *AddRepoRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_AddRepoRequest.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err - } - return b[:n], nil +func (x *ShortTaskInfo) GetSandboxStdout() string { + if x != nil { + return x.SandboxStdout } + return "" } -func (m *AddRepoRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_AddRepoRequest.Merge(m, src) -} -func (m *AddRepoRequest) XXX_Size() int { - return m.Size() + +func (x *ShortTaskInfo) GetClaimable() bool { + if x != nil { + return x.Claimable + } + return false } -func (m *AddRepoRequest) XXX_DiscardUnknown() { - xxx_messageInfo_AddRepoRequest.DiscardUnknown(m) + +func (x *ShortTaskInfo) GetCritical() bool { + if x != nil { + return x.Critical + } + return false } -var xxx_messageInfo_AddRepoRequest proto.InternalMessageInfo +type TaskDeploymentInfo struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Hostname string `protobuf:"bytes,1,opt,name=hostname,proto3" json:"hostname,omitempty"` + AgentId string `protobuf:"bytes,2,opt,name=agentId,proto3" json:"agentId,omitempty"` + OfferId string `protobuf:"bytes,3,opt,name=offerId,proto3" json:"offerId,omitempty"` + ExecutorId string `protobuf:"bytes,4,opt,name=executorId,proto3" json:"executorId,omitempty"` +} -func (m *AddRepoRequest) GetName() string { - if m != nil { - return m.Name +func (x *TaskDeploymentInfo) Reset() { + *x = TaskDeploymentInfo{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[29] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return "" } -type AddRepoReply struct { - ErrorString string `protobuf:"bytes,1,opt,name=errorString,proto3" json:"errorString,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` +func (x *TaskDeploymentInfo) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *AddRepoReply) Reset() { *m = AddRepoReply{} } -func (m *AddRepoReply) String() string { return proto.CompactTextString(m) } -func (*AddRepoReply) ProtoMessage() {} -func (*AddRepoReply) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{45} -} -func (m *AddRepoReply) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *AddRepoReply) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_AddRepoReply.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err +func (*TaskDeploymentInfo) ProtoMessage() {} + +func (x *TaskDeploymentInfo) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[29] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) } - return b[:n], nil + return ms } -} -func (m *AddRepoReply) XXX_Merge(src proto.Message) { - xxx_messageInfo_AddRepoReply.Merge(m, src) -} -func (m *AddRepoReply) XXX_Size() int { - return m.Size() -} -func (m *AddRepoReply) XXX_DiscardUnknown() { - xxx_messageInfo_AddRepoReply.DiscardUnknown(m) + return mi.MessageOf(x) } -var xxx_messageInfo_AddRepoReply proto.InternalMessageInfo +// Deprecated: Use TaskDeploymentInfo.ProtoReflect.Descriptor instead. +func (*TaskDeploymentInfo) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{29} +} -func (m *AddRepoReply) GetErrorString() string { - if m != nil { - return m.ErrorString +func (x *TaskDeploymentInfo) GetHostname() string { + if x != nil { + return x.Hostname } return "" } -type RemoveRepoRequest struct { - Index int32 `protobuf:"varint,1,opt,name=index,proto3" json:"index,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` +func (x *TaskDeploymentInfo) GetAgentId() string { + if x != nil { + return x.AgentId + } + return "" } -func (m *RemoveRepoRequest) Reset() { *m = RemoveRepoRequest{} } -func (m *RemoveRepoRequest) String() string { return proto.CompactTextString(m) } -func (*RemoveRepoRequest) ProtoMessage() {} -func (*RemoveRepoRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{46} -} -func (m *RemoveRepoRequest) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *RemoveRepoRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_RemoveRepoRequest.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err - } - return b[:n], nil +func (x *TaskDeploymentInfo) GetOfferId() string { + if x != nil { + return x.OfferId } + return "" } -func (m *RemoveRepoRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_RemoveRepoRequest.Merge(m, src) -} -func (m *RemoveRepoRequest) XXX_Size() int { - return m.Size() -} -func (m *RemoveRepoRequest) XXX_DiscardUnknown() { - xxx_messageInfo_RemoveRepoRequest.DiscardUnknown(m) + +func (x *TaskDeploymentInfo) GetExecutorId() string { + if x != nil { + return x.ExecutorId + } + return "" } -var xxx_messageInfo_RemoveRepoRequest proto.InternalMessageInfo +type GetTasksRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} -func (m *RemoveRepoRequest) GetIndex() int32 { - if m != nil { - return m.Index +func (x *GetTasksRequest) Reset() { + *x = GetTasksRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[30] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return 0 } -type RemoveRepoReply struct { - Ok bool `protobuf:"varint,1,opt,name=ok,proto3" json:"ok,omitempty"` - NewDefaultRepo string `protobuf:"bytes,2,opt,name=newDefaultRepo,proto3" json:"newDefaultRepo,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` +func (x *GetTasksRequest) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *RemoveRepoReply) Reset() { *m = RemoveRepoReply{} } -func (m *RemoveRepoReply) String() string { return proto.CompactTextString(m) } -func (*RemoveRepoReply) ProtoMessage() {} -func (*RemoveRepoReply) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{47} -} -func (m *RemoveRepoReply) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *RemoveRepoReply) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_RemoveRepoReply.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err +func (*GetTasksRequest) ProtoMessage() {} + +func (x *GetTasksRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[30] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) } - return b[:n], nil + return ms } + return mi.MessageOf(x) } -func (m *RemoveRepoReply) XXX_Merge(src proto.Message) { - xxx_messageInfo_RemoveRepoReply.Merge(m, src) -} -func (m *RemoveRepoReply) XXX_Size() int { - return m.Size() -} -func (m *RemoveRepoReply) XXX_DiscardUnknown() { - xxx_messageInfo_RemoveRepoReply.DiscardUnknown(m) + +// Deprecated: Use GetTasksRequest.ProtoReflect.Descriptor instead. +func (*GetTasksRequest) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{30} } -var xxx_messageInfo_RemoveRepoReply proto.InternalMessageInfo +type GetTasksReply struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields -func (m *RemoveRepoReply) GetOk() bool { - if m != nil { - return m.Ok - } - return false + Tasks []*ShortTaskInfo `protobuf:"bytes,1,rep,name=tasks,proto3" json:"tasks,omitempty"` + Timestamp int64 `protobuf:"varint,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"` // timestamp of when this object was sent in unix milliseconds } -func (m *RemoveRepoReply) GetNewDefaultRepo() string { - if m != nil { - return m.NewDefaultRepo +func (x *GetTasksReply) Reset() { + *x = GetTasksReply{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[31] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return "" } -type RefreshReposRequest struct { - Index int32 `protobuf:"varint,1,opt,name=index,proto3" json:"index,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` +func (x *GetTasksReply) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *RefreshReposRequest) Reset() { *m = RefreshReposRequest{} } -func (m *RefreshReposRequest) String() string { return proto.CompactTextString(m) } -func (*RefreshReposRequest) ProtoMessage() {} -func (*RefreshReposRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{48} -} -func (m *RefreshReposRequest) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *RefreshReposRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_RefreshReposRequest.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err +func (*GetTasksReply) ProtoMessage() {} + +func (x *GetTasksReply) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[31] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) } - return b[:n], nil + return ms } + return mi.MessageOf(x) } -func (m *RefreshReposRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_RefreshReposRequest.Merge(m, src) -} -func (m *RefreshReposRequest) XXX_Size() int { - return m.Size() -} -func (m *RefreshReposRequest) XXX_DiscardUnknown() { - xxx_messageInfo_RefreshReposRequest.DiscardUnknown(m) + +// Deprecated: Use GetTasksReply.ProtoReflect.Descriptor instead. +func (*GetTasksReply) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{31} } -var xxx_messageInfo_RefreshReposRequest proto.InternalMessageInfo +func (x *GetTasksReply) GetTasks() []*ShortTaskInfo { + if x != nil { + return x.Tasks + } + return nil +} -func (m *RefreshReposRequest) GetIndex() int32 { - if m != nil { - return m.Index +func (x *GetTasksReply) GetTimestamp() int64 { + if x != nil { + return x.Timestamp } return 0 } -type RefreshReposReply struct { - ErrorString string `protobuf:"bytes,1,opt,name=errorString,proto3" json:"errorString,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` +type GetTaskRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + TaskId string `protobuf:"bytes,1,opt,name=taskId,proto3" json:"taskId,omitempty"` } -func (m *RefreshReposReply) Reset() { *m = RefreshReposReply{} } -func (m *RefreshReposReply) String() string { return proto.CompactTextString(m) } -func (*RefreshReposReply) ProtoMessage() {} -func (*RefreshReposReply) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{49} +func (x *GetTaskRequest) Reset() { + *x = GetTaskRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[32] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *RefreshReposReply) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) + +func (x *GetTaskRequest) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *RefreshReposReply) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_RefreshReposReply.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err + +func (*GetTaskRequest) ProtoMessage() {} + +func (x *GetTaskRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[32] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) } - return b[:n], nil + return ms } -} -func (m *RefreshReposReply) XXX_Merge(src proto.Message) { - xxx_messageInfo_RefreshReposReply.Merge(m, src) -} -func (m *RefreshReposReply) XXX_Size() int { - return m.Size() -} -func (m *RefreshReposReply) XXX_DiscardUnknown() { - xxx_messageInfo_RefreshReposReply.DiscardUnknown(m) + return mi.MessageOf(x) } -var xxx_messageInfo_RefreshReposReply proto.InternalMessageInfo +// Deprecated: Use GetTaskRequest.ProtoReflect.Descriptor instead. +func (*GetTaskRequest) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{32} +} -func (m *RefreshReposReply) GetErrorString() string { - if m != nil { - return m.ErrorString +func (x *GetTaskRequest) GetTaskId() string { + if x != nil { + return x.TaskId } return "" } -type SetDefaultRepoRequest struct { - Index int32 `protobuf:"varint,1,opt,name=index,proto3" json:"index,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` +type GetTaskReply struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Task *TaskInfo `protobuf:"bytes,1,opt,name=task,proto3" json:"task,omitempty"` + Timestamp int64 `protobuf:"varint,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"` // timestamp of when this object was sent in unix milliseconds } -func (m *SetDefaultRepoRequest) Reset() { *m = SetDefaultRepoRequest{} } -func (m *SetDefaultRepoRequest) String() string { return proto.CompactTextString(m) } -func (*SetDefaultRepoRequest) ProtoMessage() {} -func (*SetDefaultRepoRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{50} -} -func (m *SetDefaultRepoRequest) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *SetDefaultRepoRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_SetDefaultRepoRequest.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err - } - return b[:n], nil +func (x *GetTaskReply) Reset() { + *x = GetTaskReply{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[33] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } } -func (m *SetDefaultRepoRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_SetDefaultRepoRequest.Merge(m, src) -} -func (m *SetDefaultRepoRequest) XXX_Size() int { - return m.Size() -} -func (m *SetDefaultRepoRequest) XXX_DiscardUnknown() { - xxx_messageInfo_SetDefaultRepoRequest.DiscardUnknown(m) + +func (x *GetTaskReply) String() string { + return protoimpl.X.MessageStringOf(x) } -var xxx_messageInfo_SetDefaultRepoRequest proto.InternalMessageInfo +func (*GetTaskReply) ProtoMessage() {} -func (m *SetDefaultRepoRequest) GetIndex() int32 { - if m != nil { - return m.Index +func (x *GetTaskReply) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[33] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms } - return 0 + return mi.MessageOf(x) } -type SetDefaultRepoReply struct { - ErrorString string `protobuf:"bytes,1,opt,name=errorString,proto3" json:"errorString,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` +// Deprecated: Use GetTaskReply.ProtoReflect.Descriptor instead. +func (*GetTaskReply) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{33} } -func (m *SetDefaultRepoReply) Reset() { *m = SetDefaultRepoReply{} } -func (m *SetDefaultRepoReply) String() string { return proto.CompactTextString(m) } -func (*SetDefaultRepoReply) ProtoMessage() {} -func (*SetDefaultRepoReply) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{51} -} -func (m *SetDefaultRepoReply) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) +func (x *GetTaskReply) GetTask() *TaskInfo { + if x != nil { + return x.Task + } + return nil } -func (m *SetDefaultRepoReply) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_SetDefaultRepoReply.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err - } - return b[:n], nil + +func (x *GetTaskReply) GetTimestamp() int64 { + if x != nil { + return x.Timestamp } + return 0 } -func (m *SetDefaultRepoReply) XXX_Merge(src proto.Message) { - xxx_messageInfo_SetDefaultRepoReply.Merge(m, src) + +type CommandInfo struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Env []string `protobuf:"bytes,1,rep,name=env,proto3" json:"env,omitempty"` + Shell bool `protobuf:"varint,2,opt,name=shell,proto3" json:"shell,omitempty"` + Value string `protobuf:"bytes,3,opt,name=value,proto3" json:"value,omitempty"` + Arguments []string `protobuf:"bytes,4,rep,name=arguments,proto3" json:"arguments,omitempty"` + User string `protobuf:"bytes,5,opt,name=user,proto3" json:"user,omitempty"` } -func (m *SetDefaultRepoReply) XXX_Size() int { - return m.Size() + +func (x *CommandInfo) Reset() { + *x = CommandInfo{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[34] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *SetDefaultRepoReply) XXX_DiscardUnknown() { - xxx_messageInfo_SetDefaultRepoReply.DiscardUnknown(m) + +func (x *CommandInfo) String() string { + return protoimpl.X.MessageStringOf(x) } -var xxx_messageInfo_SetDefaultRepoReply proto.InternalMessageInfo +func (*CommandInfo) ProtoMessage() {} -func (m *SetDefaultRepoReply) GetErrorString() string { - if m != nil { - return m.ErrorString +func (x *CommandInfo) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[34] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms } - return "" + return mi.MessageOf(x) } -func init() { - proto.RegisterEnum("o2control.StatusUpdate_Level", StatusUpdate_Level_name, StatusUpdate_Level_value) - proto.RegisterEnum("o2control.ControlEnvironmentRequest_Optype", ControlEnvironmentRequest_Optype_name, ControlEnvironmentRequest_Optype_value) - proto.RegisterEnum("o2control.EnvironmentOperation_Optype", EnvironmentOperation_Optype_name, EnvironmentOperation_Optype_value) - proto.RegisterType((*Event_MesosHeartbeat)(nil), "o2control.Event_MesosHeartbeat") - proto.RegisterType((*StatusRequest)(nil), "o2control.StatusRequest") - proto.RegisterType((*StatusReply)(nil), "o2control.StatusReply") - proto.RegisterType((*StatusUpdate)(nil), "o2control.StatusUpdate") - proto.RegisterType((*GetFrameworkInfoRequest)(nil), "o2control.GetFrameworkInfoRequest") - proto.RegisterType((*Version)(nil), "o2control.Version") - proto.RegisterType((*GetFrameworkInfoReply)(nil), "o2control.GetFrameworkInfoReply") - proto.RegisterType((*TeardownRequest)(nil), "o2control.TeardownRequest") - proto.RegisterType((*TeardownReply)(nil), "o2control.TeardownReply") - proto.RegisterType((*GetEnvironmentsRequest)(nil), "o2control.GetEnvironmentsRequest") - proto.RegisterType((*GetEnvironmentsReply)(nil), "o2control.GetEnvironmentsReply") - proto.RegisterType((*EnvironmentInfo)(nil), "o2control.EnvironmentInfo") - proto.RegisterType((*NewEnvironmentRequest)(nil), "o2control.NewEnvironmentRequest") - proto.RegisterType((*NewEnvironmentReply)(nil), "o2control.NewEnvironmentReply") - proto.RegisterType((*GetEnvironmentRequest)(nil), "o2control.GetEnvironmentRequest") - proto.RegisterType((*GetEnvironmentReply)(nil), "o2control.GetEnvironmentReply") - proto.RegisterType((*ControlEnvironmentRequest)(nil), "o2control.ControlEnvironmentRequest") - proto.RegisterType((*ControlEnvironmentReply)(nil), "o2control.ControlEnvironmentReply") - proto.RegisterType((*ModifyEnvironmentRequest)(nil), "o2control.ModifyEnvironmentRequest") - proto.RegisterType((*EnvironmentOperation)(nil), "o2control.EnvironmentOperation") - proto.RegisterType((*ModifyEnvironmentReply)(nil), "o2control.ModifyEnvironmentReply") - proto.RegisterType((*DestroyEnvironmentRequest)(nil), "o2control.DestroyEnvironmentRequest") - proto.RegisterType((*DestroyEnvironmentReply)(nil), "o2control.DestroyEnvironmentReply") - proto.RegisterType((*ShortTaskInfo)(nil), "o2control.ShortTaskInfo") - proto.RegisterType((*TaskDeploymentInfo)(nil), "o2control.TaskDeploymentInfo") - proto.RegisterType((*GetTasksRequest)(nil), "o2control.GetTasksRequest") - proto.RegisterType((*GetTasksReply)(nil), "o2control.GetTasksReply") - proto.RegisterType((*GetTaskRequest)(nil), "o2control.GetTaskRequest") - proto.RegisterType((*GetTaskReply)(nil), "o2control.GetTaskReply") - proto.RegisterType((*TaskClassInfo)(nil), "o2control.TaskClassInfo") - proto.RegisterType((*CommandInfo)(nil), "o2control.CommandInfo") - proto.RegisterType((*ChannelInfo)(nil), "o2control.ChannelInfo") - proto.RegisterType((*TaskInfo)(nil), "o2control.TaskInfo") - proto.RegisterType((*CleanupTasksRequest)(nil), "o2control.CleanupTasksRequest") - proto.RegisterType((*CleanupTasksReply)(nil), "o2control.CleanupTasksReply") - proto.RegisterType((*GetRolesRequest)(nil), "o2control.GetRolesRequest") - proto.RegisterType((*RoleInfo)(nil), "o2control.RoleInfo") - proto.RegisterType((*GetRolesReply)(nil), "o2control.GetRolesReply") - proto.RegisterType((*GetWorkflowTemplatesRequest)(nil), "o2control.GetWorkflowTemplatesRequest") - proto.RegisterType((*WorkflowTemplateInfo)(nil), "o2control.WorkflowTemplateInfo") - proto.RegisterType((*GetWorkflowTemplatesReply)(nil), "o2control.GetWorkflowTemplatesReply") - proto.RegisterType((*ListReposRequest)(nil), "o2control.ListReposRequest") - proto.RegisterType((*RepoInfo)(nil), "o2control.RepoInfo") - proto.RegisterType((*ListReposReply)(nil), "o2control.ListReposReply") - proto.RegisterType((*AddRepoRequest)(nil), "o2control.AddRepoRequest") - proto.RegisterType((*AddRepoReply)(nil), "o2control.AddRepoReply") - proto.RegisterType((*RemoveRepoRequest)(nil), "o2control.RemoveRepoRequest") - proto.RegisterType((*RemoveRepoReply)(nil), "o2control.RemoveRepoReply") - proto.RegisterType((*RefreshReposRequest)(nil), "o2control.RefreshReposRequest") - proto.RegisterType((*RefreshReposReply)(nil), "o2control.RefreshReposReply") - proto.RegisterType((*SetDefaultRepoRequest)(nil), "o2control.SetDefaultRepoRequest") - proto.RegisterType((*SetDefaultRepoReply)(nil), "o2control.SetDefaultRepoReply") -} - -func init() { proto.RegisterFile("protos/o2control.proto", fileDescriptor_2aa6aa9a1f02efa9) } - -var fileDescriptor_2aa6aa9a1f02efa9 = []byte{ - // 2159 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x59, 0x4f, 0x73, 0x1b, 0x49, - 0x15, 0xd7, 0xc8, 0x92, 0x25, 0x3d, 0xc9, 0xb2, 0xdc, 0x76, 0xec, 0xb1, 0xd6, 0xeb, 0x78, 0x9b, - 0x54, 0x36, 0xd9, 0x5d, 0x9c, 0xc5, 0x0b, 0x6c, 0x2a, 0x2c, 0x04, 0x5b, 0x56, 0x1c, 0x41, 0x6c, - 0xa5, 0x46, 0x4a, 0x52, 0x6c, 0x15, 0x15, 0xc6, 0x9a, 0x96, 0xa5, 0xf5, 0x68, 0x5a, 0xcc, 0x1f, - 0x3b, 0x3e, 0x70, 0xe3, 0x46, 0x51, 0x7b, 0xe0, 0xc2, 0x95, 0xe2, 0xcc, 0xf7, 0xe0, 0x42, 0x01, - 0xdf, 0x80, 0x0a, 0x55, 0x9c, 0xf8, 0x10, 0x54, 0xff, 0x99, 0x99, 0x9e, 0xd1, 0xc8, 0x76, 0x15, - 0x37, 0xbd, 0xd7, 0xbf, 0xf7, 0xba, 0xdf, 0x9f, 0x7e, 0xef, 0xf5, 0x08, 0xd6, 0xa7, 0x2e, 0xf5, - 0xa9, 0xf7, 0x88, 0xee, 0x0d, 0xa8, 0xe3, 0xbb, 0xd4, 0xde, 0xe5, 0x0c, 0x54, 0x89, 0x18, 0x78, - 0x1d, 0xd6, 0xda, 0x17, 0xc4, 0xf1, 0xdf, 0x1e, 0x13, 0x8f, 0x7a, 0xcf, 0x89, 0xe9, 0xfa, 0xa7, - 0xc4, 0xf4, 0xf1, 0x32, 0x2c, 0xf5, 0x7c, 0xd3, 0x0f, 0x3c, 0x83, 0xfc, 0x3a, 0x20, 0x9e, 0x8f, - 0x4f, 0xa1, 0x1a, 0x32, 0xa6, 0xf6, 0x15, 0x5a, 0x83, 0xa2, 0xe7, 0x9b, 0x3e, 0xd1, 0xb5, 0x1d, - 0xed, 0x41, 0xc5, 0x10, 0x04, 0xfa, 0x31, 0x2c, 0x79, 0x1c, 0xf4, 0x6a, 0x6a, 0x99, 0x3e, 0xf1, - 0xf4, 0xfc, 0xce, 0xc2, 0x83, 0xea, 0xde, 0xc6, 0x6e, 0x7c, 0x82, 0x9e, 0xb2, 0x6e, 0x24, 0xd1, - 0xf8, 0x6f, 0x1a, 0xd4, 0xd4, 0x75, 0xf4, 0x05, 0x14, 0x6d, 0x72, 0x41, 0x6c, 0xbe, 0x4b, 0x7d, - 0xef, 0xc3, 0x39, 0x7a, 0x76, 0x5f, 0x30, 0x90, 0x21, 0xb0, 0xa8, 0x03, 0xf5, 0x49, 0xc2, 0x18, - 0x3d, 0xbf, 0xa3, 0x3d, 0xa8, 0xee, 0xdd, 0x55, 0xa4, 0xb3, 0x6c, 0x7e, 0x9e, 0x33, 0x52, 0x82, - 0xf8, 0xfb, 0x50, 0xe4, 0xaa, 0x51, 0x05, 0x8a, 0x87, 0xed, 0x83, 0x57, 0x47, 0x8d, 0x1c, 0x2a, - 0x43, 0xa1, 0x73, 0xf2, 0xac, 0xdb, 0xd0, 0x50, 0x15, 0x4a, 0x6f, 0xf6, 0x8d, 0x93, 0xce, 0xc9, - 0x51, 0x23, 0xcf, 0x10, 0x6d, 0xc3, 0xe8, 0x1a, 0x8d, 0x85, 0x83, 0x12, 0x14, 0xb9, 0x7e, 0xbc, - 0x09, 0x1b, 0x47, 0xc4, 0x7f, 0xe6, 0x9a, 0x13, 0x72, 0x49, 0xdd, 0xf3, 0x8e, 0x33, 0xa4, 0xa1, - 0x3b, 0xff, 0xac, 0x41, 0xe9, 0x35, 0x71, 0xbd, 0x31, 0x75, 0x98, 0x2f, 0x27, 0xe6, 0x37, 0xd4, - 0xe5, 0x56, 0x16, 0x0d, 0x41, 0x70, 0xee, 0xd8, 0xa1, 0x2e, 0x3f, 0x3d, 0xe3, 0x32, 0x82, 0x71, - 0xa7, 0xa6, 0x3f, 0x18, 0xe9, 0x0b, 0x82, 0xcb, 0x09, 0xc6, 0x3d, 0x0d, 0xc6, 0xb6, 0xa5, 0x17, - 0x44, 0x34, 0x38, 0x81, 0x76, 0xa0, 0x3a, 0x75, 0xa9, 0x15, 0x0c, 0xfc, 0x13, 0x73, 0x42, 0xf4, - 0x22, 0x5f, 0x53, 0x59, 0x68, 0x1b, 0xe0, 0x42, 0x1c, 0xa2, 0xe7, 0xbb, 0xfa, 0x22, 0x07, 0x28, - 0x1c, 0xfc, 0x6d, 0x1e, 0xee, 0xcc, 0x5a, 0xc0, 0xe2, 0xbf, 0x03, 0xd5, 0x61, 0xc4, 0xb5, 0x64, - 0x16, 0xa8, 0x2c, 0xf4, 0x19, 0xac, 0x10, 0xe7, 0x62, 0xec, 0x52, 0x67, 0x42, 0x1c, 0xdf, 0x6b, - 0xd1, 0xc0, 0xf1, 0xa5, 0x2d, 0xb3, 0x0b, 0xec, 0x24, 0xbe, 0xe9, 0x9d, 0x4b, 0x98, 0x30, 0x4e, - 0xe1, 0xc4, 0xf9, 0x56, 0x50, 0xf3, 0x6d, 0x1b, 0x60, 0x44, 0xbd, 0x50, 0x79, 0x51, 0x48, 0xc5, - 0x1c, 0x84, 0xa1, 0x36, 0x76, 0x3c, 0xdf, 0x74, 0x06, 0x84, 0xbb, 0x40, 0x58, 0x98, 0xe0, 0xa1, - 0xcf, 0xa0, 0x24, 0x2d, 0xd6, 0x4b, 0x3c, 0x4f, 0x90, 0x92, 0x27, 0x32, 0x44, 0x46, 0x08, 0xc1, - 0x0f, 0x61, 0xb9, 0x4f, 0x4c, 0xd7, 0xa2, 0x97, 0x8e, 0x0c, 0x25, 0x5a, 0x87, 0x45, 0x97, 0x98, - 0x1e, 0x75, 0xa4, 0x17, 0x24, 0xc5, 0xae, 0x50, 0x0c, 0x9d, 0xda, 0x57, 0x58, 0x87, 0xf5, 0x23, - 0xe2, 0xb7, 0x15, 0xdb, 0xc3, 0x6c, 0x78, 0x07, 0x6b, 0x33, 0x2b, 0xb7, 0xf3, 0xf2, 0x4f, 0xa0, - 0xa6, 0x3a, 0x53, 0x5e, 0xb8, 0xa6, 0x9a, 0xea, 0xf1, 0x32, 0x0f, 0x5f, 0x02, 0x8f, 0xff, 0xa9, - 0xc1, 0x72, 0x0a, 0x81, 0xea, 0x90, 0x1f, 0x87, 0x9b, 0xe5, 0xc7, 0x3c, 0x8f, 0x06, 0x2e, 0x31, - 0x7d, 0x62, 0xbd, 0x19, 0x11, 0x87, 0xc7, 0xb0, 0x62, 0xa8, 0xac, 0x38, 0x3a, 0x0b, 0x6a, 0x74, - 0x76, 0xa1, 0xc8, 0x23, 0xa8, 0x17, 0xf8, 0xa1, 0x74, 0xf5, 0xf6, 0x8e, 0xa8, 0xeb, 0xf7, 0x4d, - 0x4f, 0x64, 0x94, 0x80, 0xa1, 0x26, 0x94, 0x5d, 0x4a, 0x7d, 0x83, 0xda, 0x61, 0xb2, 0x46, 0x34, - 0xfa, 0x04, 0x1a, 0x83, 0xc0, 0x75, 0x89, 0xe3, 0x1b, 0x81, 0x73, 0x12, 0x4c, 0x4e, 0x89, 0xc8, - 0xd7, 0x25, 0x63, 0x86, 0x8f, 0x5b, 0x70, 0xe7, 0x84, 0x5c, 0x2a, 0x56, 0x85, 0x91, 0xfa, 0x04, - 0x1a, 0xcc, 0x6d, 0x43, 0x9b, 0x5e, 0xf6, 0xc9, 0x64, 0x6a, 0xc7, 0xf5, 0x6b, 0x86, 0x8f, 0x7b, - 0xb0, 0x9a, 0x56, 0xc2, 0x22, 0xf2, 0x15, 0x54, 0x15, 0xff, 0x71, 0xe9, 0xeb, 0xdd, 0xad, 0xc2, - 0xf1, 0xc7, 0xfc, 0x3a, 0x65, 0x9c, 0x2c, 0xe5, 0x72, 0xfc, 0x5b, 0x0d, 0x56, 0xd3, 0xc8, 0xff, - 0x7b, 0x7b, 0xf4, 0x08, 0xca, 0xa1, 0x9d, 0xb2, 0x26, 0xae, 0x2a, 0xa2, 0xcc, 0xcf, 0x5c, 0x26, - 0x02, 0xe1, 0xbf, 0x6b, 0xb0, 0xd9, 0x12, 0xcb, 0x37, 0x1f, 0x1a, 0x3d, 0x85, 0x82, 0x7f, 0x35, - 0x25, 0x5c, 0x75, 0x7d, 0xef, 0x53, 0x45, 0xf5, 0x5c, 0x1d, 0xbb, 0xdd, 0x29, 0x13, 0x31, 0xb8, - 0x20, 0x36, 0x61, 0x51, 0xd0, 0xac, 0xc8, 0x9e, 0x74, 0xbb, 0x2f, 0x1b, 0x39, 0x84, 0xa0, 0xde, - 0xeb, 0xef, 0x1b, 0xfd, 0xb7, 0xfb, 0xad, 0x7e, 0xe7, 0x75, 0xa7, 0xff, 0x8b, 0x86, 0x86, 0x56, - 0x60, 0xa9, 0xd7, 0xef, 0xbe, 0x8c, 0x59, 0x79, 0xb4, 0x04, 0x95, 0x56, 0xf7, 0xe4, 0x59, 0xe7, - 0xe8, 0x95, 0xd1, 0x6e, 0x2c, 0xb0, 0x6a, 0x6c, 0xb4, 0x7b, 0xed, 0x7e, 0xa3, 0x80, 0x6a, 0x50, - 0x3e, 0xea, 0xbe, 0x15, 0xb5, 0xb9, 0x88, 0xcf, 0x61, 0x23, 0xeb, 0x30, 0xcc, 0xb7, 0x69, 0x73, - 0xa2, 0xa4, 0xce, 0xab, 0x49, 0x9d, 0x95, 0x88, 0x0b, 0x73, 0x12, 0xf1, 0x0f, 0x1a, 0xe8, 0xc7, - 0xd4, 0x1a, 0x0f, 0xaf, 0x6e, 0xe5, 0x3d, 0xa0, 0x53, 0xe2, 0x9a, 0xfe, 0x98, 0x3a, 0xe1, 0x3d, - 0xbe, 0x9b, 0x1d, 0xd9, 0x6e, 0x88, 0x33, 0x14, 0x11, 0x74, 0x1f, 0xea, 0x2e, 0x19, 0x50, 0x67, - 0x38, 0x3e, 0x0b, 0x5c, 0xb2, 0x6f, 0xdb, 0xfc, 0x5c, 0x65, 0x23, 0xc5, 0xc5, 0x7f, 0xd2, 0x60, - 0x2d, 0x4b, 0x19, 0x7a, 0x22, 0xe3, 0x27, 0x9a, 0xed, 0xfd, 0x1b, 0xf6, 0x4e, 0x84, 0x4e, 0xdc, - 0x5d, 0x5b, 0x54, 0xd9, 0x7c, 0x78, 0x77, 0x05, 0x8d, 0xbf, 0x97, 0x11, 0xd6, 0x65, 0xa8, 0x1a, - 0xed, 0xe3, 0xee, 0xeb, 0xf6, 0x5b, 0xa3, 0xfb, 0x82, 0x45, 0xac, 0x06, 0xe5, 0xfd, 0xc3, 0x43, - 0x41, 0x15, 0xf0, 0xef, 0x34, 0x58, 0xcf, 0xf0, 0x1c, 0x0b, 0xd3, 0xcf, 0xa1, 0x31, 0x34, 0xc7, - 0x36, 0xb1, 0xba, 0xb1, 0xb7, 0xb4, 0xdb, 0x79, 0x6b, 0x46, 0x50, 0x06, 0x21, 0x3f, 0x1b, 0x73, - 0xb5, 0x90, 0xe1, 0x0e, 0x6c, 0x1e, 0x12, 0xcf, 0x77, 0xe9, 0x6d, 0xe2, 0xb8, 0x05, 0x95, 0x73, - 0x42, 0xa6, 0x7d, 0x5e, 0xf9, 0xf2, 0x3c, 0x02, 0x31, 0x03, 0x13, 0xd8, 0xc8, 0x52, 0xc5, 0x0c, - 0xfb, 0x19, 0xac, 0x0c, 0x6c, 0x62, 0x3a, 0x81, 0x80, 0x72, 0xa6, 0xbc, 0xe1, 0x5b, 0xea, 0x5d, - 0x4a, 0x63, 0x8c, 0x59, 0x31, 0xfc, 0x1f, 0x0d, 0x96, 0x12, 0x35, 0x16, 0x21, 0x28, 0x38, 0x2c, - 0x38, 0xe2, 0xa0, 0xfc, 0x37, 0xeb, 0x5c, 0x36, 0x1d, 0x9c, 0x13, 0x4b, 0x9e, 0x53, 0x52, 0x8c, - 0xcf, 0x2a, 0x72, 0xc7, 0x92, 0x6e, 0x90, 0x14, 0xe3, 0x8b, 0x81, 0x4d, 0x76, 0x61, 0x49, 0xc5, - 0x5e, 0x2b, 0xaa, 0x37, 0x65, 0x0b, 0x2a, 0x03, 0xdb, 0xf4, 0x3c, 0xa5, 0xf3, 0xc6, 0x0c, 0xd4, - 0x86, 0xba, 0x45, 0xa6, 0x36, 0xbd, 0x0a, 0x4b, 0x95, 0xec, 0xbe, 0xea, 0x8c, 0xc7, 0x0e, 0x7f, - 0x98, 0x00, 0x19, 0x29, 0x21, 0x56, 0x28, 0xd1, 0x2c, 0x8c, 0xa5, 0x23, 0x1b, 0x03, 0x14, 0x8b, - 0x23, 0x1a, 0xe9, 0x50, 0x32, 0xcf, 0x18, 0x30, 0x0c, 0x7c, 0x48, 0xb2, 0x15, 0x3a, 0x1c, 0x12, - 0x37, 0x32, 0x3c, 0x24, 0xd9, 0xa0, 0x41, 0xde, 0x91, 0x41, 0xe0, 0x53, 0xb6, 0x28, 0xac, 0x57, - 0x38, 0x78, 0x05, 0x96, 0x8f, 0x88, 0x2f, 0x03, 0x20, 0x7a, 0xfa, 0x53, 0x58, 0x8a, 0x59, 0x2c, - 0xbe, 0x51, 0x3b, 0xd4, 0x6e, 0xd5, 0x0e, 0xf1, 0x03, 0xa8, 0x4b, 0x05, 0xca, 0xa4, 0x21, 0xe3, - 0xa2, 0xa9, 0x71, 0xc1, 0x5f, 0x42, 0x2d, 0x42, 0xb2, 0x9d, 0x3e, 0x86, 0x02, 0x5b, 0x91, 0xc9, - 0xb3, 0x9a, 0xf2, 0x28, 0xdf, 0x83, 0x03, 0x70, 0x1b, 0x96, 0x18, 0xa7, 0xc5, 0xa2, 0x32, 0x37, - 0x4b, 0x58, 0xfb, 0x17, 0xe2, 0xc7, 0xd4, 0x22, 0x51, 0xfb, 0x8f, 0x59, 0xf8, 0x37, 0x50, 0x6d, - 0xd1, 0xc9, 0xc4, 0x74, 0x2c, 0xae, 0xa4, 0x01, 0x0b, 0xc4, 0xb9, 0xe0, 0x66, 0x56, 0x0c, 0xf6, - 0x93, 0x27, 0xc8, 0x88, 0xd8, 0xb6, 0xcc, 0x33, 0x41, 0x30, 0xee, 0x85, 0x69, 0x07, 0xd1, 0x65, - 0xe3, 0x04, 0x4b, 0x1b, 0xd3, 0x3d, 0x0b, 0xc4, 0x38, 0x53, 0xe0, 0x3a, 0x62, 0x06, 0x3b, 0x60, - 0xe0, 0x11, 0x57, 0x66, 0x1a, 0xff, 0x8d, 0x8f, 0xa1, 0xda, 0x1a, 0x99, 0x8e, 0x43, 0xec, 0xb9, - 0x36, 0x20, 0xa5, 0x35, 0x55, 0x64, 0xc9, 0xe2, 0xde, 0x74, 0xcf, 0x88, 0x1f, 0x67, 0x39, 0xa3, - 0xf0, 0x7f, 0xf3, 0x50, 0x8e, 0xae, 0xcd, 0x0f, 0xa1, 0xe2, 0xb1, 0xe0, 0xf0, 0x0c, 0x15, 0xfe, - 0x9c, 0x1f, 0xb8, 0x18, 0xca, 0xe4, 0x06, 0xa1, 0x57, 0x65, 0xaf, 0xd5, 0x53, 0x71, 0x88, 0xbc, - 0x6e, 0xc4, 0x50, 0xf4, 0x53, 0x58, 0x1e, 0x3b, 0xa7, 0x34, 0x70, 0x2c, 0x69, 0x92, 0xa7, 0x2f, - 0xf0, 0x74, 0x59, 0x57, 0x4b, 0x40, 0x6c, 0xad, 0x91, 0x86, 0xa3, 0x03, 0x68, 0xd0, 0xc0, 0x4f, - 0xaa, 0x28, 0x5c, 0xab, 0x62, 0x06, 0x8f, 0x1e, 0xb3, 0x90, 0x47, 0x01, 0xe5, 0xce, 0x4e, 0x89, - 0xc7, 0xab, 0x86, 0x0a, 0x65, 0x17, 0x8f, 0x65, 0xd6, 0x4b, 0xd3, 0x1f, 0xc9, 0x3b, 0x1f, 0xd1, - 0x2c, 0xde, 0xc4, 0xb9, 0xe8, 0x58, 0xfc, 0xa6, 0x57, 0x0c, 0x41, 0xe0, 0x47, 0xb0, 0x9a, 0x2c, - 0x69, 0x22, 0xd7, 0x75, 0x28, 0x89, 0xec, 0xf6, 0x64, 0x22, 0x85, 0x24, 0xfe, 0xbd, 0x06, 0x2b, - 0x33, 0x45, 0x10, 0x3d, 0x81, 0xea, 0xf9, 0xd8, 0xb6, 0x89, 0xd5, 0xbf, 0xd5, 0x1d, 0x53, 0xc1, - 0xe8, 0x2b, 0xa8, 0xb9, 0x81, 0xe3, 0x8c, 0x9d, 0xb3, 0xb0, 0x6a, 0x5f, 0x2f, 0x9c, 0x40, 0xe3, - 0x16, 0xbf, 0xfb, 0x6c, 0x7a, 0x8a, 0x0e, 0x1f, 0x59, 0xaa, 0x29, 0x96, 0x32, 0xdf, 0x4c, 0x4d, - 0x7f, 0xd4, 0x9b, 0x92, 0x41, 0xd8, 0x23, 0x43, 0x1a, 0xff, 0x45, 0x83, 0x72, 0x38, 0x80, 0xcd, - 0xab, 0xd5, 0xb2, 0xf6, 0xe6, 0xb3, 0x6b, 0x6f, 0x62, 0xf4, 0x6e, 0x42, 0x79, 0x18, 0xd8, 0x36, - 0x0f, 0x83, 0xa8, 0x56, 0x11, 0xad, 0x7a, 0xb6, 0x98, 0xf0, 0x2c, 0x7a, 0x08, 0x45, 0xd6, 0xb4, - 0x3d, 0x7d, 0x91, 0x3b, 0x20, 0x73, 0x38, 0x14, 0x08, 0xfc, 0x84, 0x57, 0x37, 0x69, 0x34, 0xf3, - 0x7f, 0x24, 0xab, 0xdd, 0x28, 0xfb, 0x21, 0x7c, 0x70, 0x44, 0xfc, 0x37, 0xa9, 0x89, 0x3b, 0x2a, - 0x9c, 0xcf, 0x60, 0x2d, 0xbd, 0x16, 0x7a, 0xc5, 0x25, 0x53, 0x1a, 0x7a, 0x85, 0xfd, 0xe6, 0xe9, - 0x16, 0x4e, 0xf2, 0xd2, 0xa5, 0x21, 0x8d, 0xbf, 0x81, 0xcd, 0xec, 0x6d, 0xd8, 0x71, 0x8f, 0x61, - 0x25, 0x3d, 0xf2, 0x67, 0x8d, 0x11, 0x59, 0x07, 0x31, 0x66, 0x25, 0x31, 0x82, 0xc6, 0x8b, 0xb1, - 0xc7, 0x1a, 0x39, 0x8d, 0xec, 0x78, 0x0c, 0x65, 0x46, 0xcf, 0x8d, 0xa8, 0x0e, 0x25, 0x8b, 0x0c, - 0xcd, 0xc0, 0xf6, 0x65, 0x59, 0x0c, 0x49, 0xfc, 0x23, 0xa8, 0x2b, 0xda, 0x42, 0xef, 0x32, 0x2a, - 0xcb, 0xbb, 0x72, 0x0f, 0x43, 0x20, 0xf0, 0x3d, 0xa8, 0xef, 0x5b, 0x16, 0xe3, 0x86, 0xd9, 0x98, - 0xb1, 0x39, 0xfe, 0x1c, 0x6a, 0x11, 0x4a, 0xbe, 0x34, 0x89, 0xeb, 0x52, 0xb7, 0xe7, 0xbb, 0x63, - 0xe7, 0x2c, 0x7c, 0x69, 0x2a, 0x2c, 0xfc, 0x10, 0x56, 0x0c, 0x32, 0xa1, 0x17, 0x44, 0x55, 0xbd, - 0x06, 0xc5, 0xb1, 0x63, 0x91, 0x77, 0xe1, 0xa7, 0x0b, 0x4e, 0xe0, 0x0e, 0x2c, 0xab, 0x50, 0x39, - 0x5c, 0x53, 0xd1, 0x90, 0xca, 0x46, 0x9e, 0x9e, 0xb3, 0x61, 0xd5, 0x21, 0x97, 0x87, 0xc2, 0x60, - 0x06, 0x93, 0xe1, 0x4b, 0x71, 0xf1, 0xa7, 0xb0, 0x6a, 0x90, 0xa1, 0x4b, 0xbc, 0x91, 0xea, 0xdb, - 0x39, 0xfb, 0xfe, 0x80, 0x1d, 0x51, 0x05, 0xdf, 0xce, 0xb2, 0xef, 0xc2, 0x9d, 0x1e, 0xf1, 0x95, - 0x5d, 0xaf, 0xdf, 0xe5, 0x4b, 0x58, 0x4d, 0xc3, 0x6f, 0xb5, 0xcf, 0xde, 0xb7, 0x35, 0x28, 0xc9, - 0xc7, 0x07, 0x6a, 0x41, 0xb5, 0xef, 0x9a, 0x83, 0x73, 0xf1, 0x19, 0x0b, 0xe9, 0x33, 0x5f, 0xb6, - 0xe4, 0x19, 0x9a, 0xeb, 0x19, 0x2b, 0x6c, 0xc2, 0xcb, 0x7d, 0xae, 0xa1, 0xaf, 0xa1, 0x91, 0xfe, - 0x3a, 0x83, 0xb0, 0x82, 0x9f, 0xf3, 0xf1, 0xa9, 0xb9, 0x73, 0x2d, 0x86, 0x6b, 0x47, 0x07, 0x50, - 0x0e, 0xbf, 0x5e, 0x20, 0xf5, 0x81, 0x99, 0xfa, 0xfa, 0xd1, 0xd4, 0x33, 0xd7, 0x84, 0x8e, 0x37, - 0xbc, 0x32, 0xaa, 0x9f, 0x35, 0xd0, 0x47, 0xc9, 0xad, 0x33, 0x3e, 0x86, 0x34, 0xef, 0x5e, 0x07, - 0x11, 0x8a, 0xfb, 0x50, 0x4f, 0x3e, 0xce, 0x91, 0x6a, 0x52, 0xe6, 0xe3, 0xbf, 0xb9, 0x7d, 0x0d, - 0x22, 0xd2, 0x9a, 0xdc, 0x0f, 0xed, 0xcc, 0x3d, 0x4a, 0x96, 0xd6, 0x8c, 0x07, 0x3b, 0xce, 0xa1, - 0x5f, 0x01, 0x9a, 0x7d, 0x71, 0xa2, 0x7b, 0xb7, 0x79, 0x1d, 0x37, 0xf1, 0x0d, 0x28, 0xb1, 0xc3, - 0x2f, 0x61, 0x65, 0xe6, 0xad, 0x84, 0xbe, 0xa3, 0x88, 0xce, 0x7b, 0x83, 0x36, 0x3f, 0xba, 0x1e, - 0x14, 0x19, 0x30, 0xfb, 0x64, 0x49, 0x18, 0x30, 0xf7, 0x71, 0x94, 0x30, 0x60, 0xce, 0xbb, 0x47, - 0xe4, 0x5a, 0x38, 0x2a, 0x27, 0x72, 0x2d, 0x35, 0x52, 0x27, 0x72, 0x2d, 0x31, 0x5b, 0xe3, 0x1c, - 0x7a, 0x0a, 0x25, 0xc9, 0x42, 0x9b, 0xb3, 0xb0, 0x50, 0xc3, 0x46, 0xd6, 0x92, 0x50, 0x70, 0x02, - 0x35, 0x75, 0xaa, 0x40, 0xdb, 0x73, 0xdf, 0x5c, 0x42, 0xd5, 0xb5, 0x6f, 0xb2, 0xc8, 0x28, 0xde, - 0x21, 0xd3, 0x46, 0xa9, 0xb3, 0x42, 0xda, 0xa8, 0xb8, 0xa5, 0xe2, 0x1c, 0x1a, 0xf2, 0xef, 0x82, - 0x33, 0x2d, 0x0c, 0xdd, 0x4f, 0xca, 0xcc, 0x6b, 0xa5, 0xcd, 0x7b, 0x37, 0xe2, 0xc4, 0x3e, 0x6d, - 0xa8, 0x44, 0x0d, 0x07, 0x7d, 0xa0, 0x08, 0xa5, 0x9b, 0x5a, 0x73, 0x33, 0x7b, 0x31, 0x8a, 0x81, - 0x6c, 0x2a, 0x89, 0x18, 0x24, 0xdb, 0x51, 0x22, 0x06, 0x6a, 0x0f, 0xc2, 0x39, 0xf4, 0x1c, 0x20, - 0x6e, 0x1c, 0x68, 0x2b, 0xd1, 0xe5, 0x52, 0xad, 0xa7, 0xd9, 0x9c, 0xb3, 0x1a, 0x45, 0x53, 0x6d, - 0x05, 0x89, 0x68, 0x66, 0x34, 0x94, 0xe6, 0xd6, 0xdc, 0xf5, 0xa8, 0x36, 0x24, 0x8b, 0x7e, 0xa2, - 0x36, 0x64, 0xb6, 0x8f, 0x44, 0x6d, 0xc8, 0xe8, 0x18, 0x38, 0x77, 0xf0, 0xf8, 0xaf, 0xef, 0xb7, - 0xb5, 0x7f, 0xbc, 0xdf, 0xd6, 0xfe, 0xf5, 0x7e, 0x5b, 0xfb, 0xe3, 0xbf, 0xb7, 0x73, 0x80, 0x07, - 0xa3, 0xdd, 0x01, 0x71, 0x9d, 0x5d, 0xd3, 0x1e, 0x0f, 0xc8, 0x2e, 0xdd, 0xdb, 0x0d, 0x35, 0xb8, - 0xd3, 0x81, 0x47, 0xdc, 0x0b, 0xe2, 0x7e, 0x9d, 0x9f, 0x9e, 0x9e, 0x2e, 0xf2, 0x7f, 0x72, 0xbe, - 0xf8, 0x5f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xb4, 0x51, 0xba, 0xac, 0xe3, 0x19, 0x00, 0x00, -} - -// Reference imports to suppress errors if they are not otherwise used. -var _ context.Context -var _ grpc.ClientConn - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the grpc package it is being compiled against. -const _ = grpc.SupportPackageIsVersion4 - -// ControlClient is the client API for Control service. -// -// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. -type ControlClient interface { - TrackStatus(ctx context.Context, in *StatusRequest, opts ...grpc.CallOption) (Control_TrackStatusClient, error) - GetFrameworkInfo(ctx context.Context, in *GetFrameworkInfoRequest, opts ...grpc.CallOption) (*GetFrameworkInfoReply, error) - Teardown(ctx context.Context, in *TeardownRequest, opts ...grpc.CallOption) (*TeardownReply, error) - GetEnvironments(ctx context.Context, in *GetEnvironmentsRequest, opts ...grpc.CallOption) (*GetEnvironmentsReply, error) - NewEnvironment(ctx context.Context, in *NewEnvironmentRequest, opts ...grpc.CallOption) (*NewEnvironmentReply, error) - GetEnvironment(ctx context.Context, in *GetEnvironmentRequest, opts ...grpc.CallOption) (*GetEnvironmentReply, error) - ControlEnvironment(ctx context.Context, in *ControlEnvironmentRequest, opts ...grpc.CallOption) (*ControlEnvironmentReply, error) - ModifyEnvironment(ctx context.Context, in *ModifyEnvironmentRequest, opts ...grpc.CallOption) (*ModifyEnvironmentReply, error) - DestroyEnvironment(ctx context.Context, in *DestroyEnvironmentRequest, opts ...grpc.CallOption) (*DestroyEnvironmentReply, error) - GetTasks(ctx context.Context, in *GetTasksRequest, opts ...grpc.CallOption) (*GetTasksReply, error) - GetTask(ctx context.Context, in *GetTaskRequest, opts ...grpc.CallOption) (*GetTaskReply, error) - CleanupTasks(ctx context.Context, in *CleanupTasksRequest, opts ...grpc.CallOption) (*CleanupTasksReply, error) - GetRoles(ctx context.Context, in *GetRolesRequest, opts ...grpc.CallOption) (*GetRolesReply, error) - GetWorkflowTemplates(ctx context.Context, in *GetWorkflowTemplatesRequest, opts ...grpc.CallOption) (*GetWorkflowTemplatesReply, error) - ListRepos(ctx context.Context, in *ListReposRequest, opts ...grpc.CallOption) (*ListReposReply, error) - AddRepo(ctx context.Context, in *AddRepoRequest, opts ...grpc.CallOption) (*AddRepoReply, error) - RemoveRepo(ctx context.Context, in *RemoveRepoRequest, opts ...grpc.CallOption) (*RemoveRepoReply, error) - RefreshRepos(ctx context.Context, in *RefreshReposRequest, opts ...grpc.CallOption) (*RefreshReposReply, error) - SetDefaultRepo(ctx context.Context, in *SetDefaultRepoRequest, opts ...grpc.CallOption) (*SetDefaultRepoReply, error) +// Deprecated: Use CommandInfo.ProtoReflect.Descriptor instead. +func (*CommandInfo) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{34} } -type controlClient struct { - cc *grpc.ClientConn +func (x *CommandInfo) GetEnv() []string { + if x != nil { + return x.Env + } + return nil } -func NewControlClient(cc *grpc.ClientConn) ControlClient { - return &controlClient{cc} +func (x *CommandInfo) GetShell() bool { + if x != nil { + return x.Shell + } + return false } -func (c *controlClient) TrackStatus(ctx context.Context, in *StatusRequest, opts ...grpc.CallOption) (Control_TrackStatusClient, error) { - stream, err := c.cc.NewStream(ctx, &_Control_serviceDesc.Streams[0], "/o2control.Control/TrackStatus", opts...) - if err != nil { - return nil, err - } - x := &controlTrackStatusClient{stream} - if err := x.ClientStream.SendMsg(in); err != nil { - return nil, err +func (x *CommandInfo) GetValue() string { + if x != nil { + return x.Value } - if err := x.ClientStream.CloseSend(); err != nil { - return nil, err + return "" +} + +func (x *CommandInfo) GetArguments() []string { + if x != nil { + return x.Arguments } - return x, nil + return nil } -type Control_TrackStatusClient interface { - Recv() (*StatusReply, error) - grpc.ClientStream +func (x *CommandInfo) GetUser() string { + if x != nil { + return x.User + } + return "" } -type controlTrackStatusClient struct { - grpc.ClientStream +type ChannelInfo struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Type string `protobuf:"bytes,2,opt,name=type,proto3" json:"type,omitempty"` + Target string `protobuf:"bytes,3,opt,name=target,proto3" json:"target,omitempty"` } -func (x *controlTrackStatusClient) Recv() (*StatusReply, error) { - m := new(StatusReply) - if err := x.ClientStream.RecvMsg(m); err != nil { - return nil, err +func (x *ChannelInfo) Reset() { + *x = ChannelInfo{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[35] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return m, nil } -func (c *controlClient) GetFrameworkInfo(ctx context.Context, in *GetFrameworkInfoRequest, opts ...grpc.CallOption) (*GetFrameworkInfoReply, error) { - out := new(GetFrameworkInfoReply) - err := c.cc.Invoke(ctx, "/o2control.Control/GetFrameworkInfo", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil +func (x *ChannelInfo) String() string { + return protoimpl.X.MessageStringOf(x) } -func (c *controlClient) Teardown(ctx context.Context, in *TeardownRequest, opts ...grpc.CallOption) (*TeardownReply, error) { - out := new(TeardownReply) - err := c.cc.Invoke(ctx, "/o2control.Control/Teardown", in, out, opts...) - if err != nil { - return nil, err +func (*ChannelInfo) ProtoMessage() {} + +func (x *ChannelInfo) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[35] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms } - return out, nil + return mi.MessageOf(x) } -func (c *controlClient) GetEnvironments(ctx context.Context, in *GetEnvironmentsRequest, opts ...grpc.CallOption) (*GetEnvironmentsReply, error) { - out := new(GetEnvironmentsReply) - err := c.cc.Invoke(ctx, "/o2control.Control/GetEnvironments", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil +// Deprecated: Use ChannelInfo.ProtoReflect.Descriptor instead. +func (*ChannelInfo) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{35} } -func (c *controlClient) NewEnvironment(ctx context.Context, in *NewEnvironmentRequest, opts ...grpc.CallOption) (*NewEnvironmentReply, error) { - out := new(NewEnvironmentReply) - err := c.cc.Invoke(ctx, "/o2control.Control/NewEnvironment", in, out, opts...) - if err != nil { - return nil, err +func (x *ChannelInfo) GetName() string { + if x != nil { + return x.Name } - return out, nil + return "" } -func (c *controlClient) GetEnvironment(ctx context.Context, in *GetEnvironmentRequest, opts ...grpc.CallOption) (*GetEnvironmentReply, error) { - out := new(GetEnvironmentReply) - err := c.cc.Invoke(ctx, "/o2control.Control/GetEnvironment", in, out, opts...) - if err != nil { - return nil, err +func (x *ChannelInfo) GetType() string { + if x != nil { + return x.Type } - return out, nil + return "" } -func (c *controlClient) ControlEnvironment(ctx context.Context, in *ControlEnvironmentRequest, opts ...grpc.CallOption) (*ControlEnvironmentReply, error) { - out := new(ControlEnvironmentReply) - err := c.cc.Invoke(ctx, "/o2control.Control/ControlEnvironment", in, out, opts...) - if err != nil { - return nil, err +func (x *ChannelInfo) GetTarget() string { + if x != nil { + return x.Target } - return out, nil + return "" } -func (c *controlClient) ModifyEnvironment(ctx context.Context, in *ModifyEnvironmentRequest, opts ...grpc.CallOption) (*ModifyEnvironmentReply, error) { - out := new(ModifyEnvironmentReply) - err := c.cc.Invoke(ctx, "/o2control.Control/ModifyEnvironment", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil +type TaskInfo struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ShortInfo *ShortTaskInfo `protobuf:"bytes,1,opt,name=shortInfo,proto3" json:"shortInfo,omitempty"` + InboundChannels []*ChannelInfo `protobuf:"bytes,3,rep,name=inboundChannels,proto3" json:"inboundChannels,omitempty"` + OutboundChannels []*ChannelInfo `protobuf:"bytes,4,rep,name=outboundChannels,proto3" json:"outboundChannels,omitempty"` + CommandInfo *CommandInfo `protobuf:"bytes,5,opt,name=commandInfo,proto3" json:"commandInfo,omitempty"` + TaskPath string `protobuf:"bytes,6,opt,name=taskPath,proto3" json:"taskPath,omitempty"` + EnvId string `protobuf:"bytes,7,opt,name=envId,proto3" json:"envId,omitempty"` + Properties map[string]string `protobuf:"bytes,9,rep,name=properties,proto3" json:"properties,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` } -func (c *controlClient) DestroyEnvironment(ctx context.Context, in *DestroyEnvironmentRequest, opts ...grpc.CallOption) (*DestroyEnvironmentReply, error) { - out := new(DestroyEnvironmentReply) - err := c.cc.Invoke(ctx, "/o2control.Control/DestroyEnvironment", in, out, opts...) - if err != nil { - return nil, err +func (x *TaskInfo) Reset() { + *x = TaskInfo{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[36] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return out, nil } -func (c *controlClient) GetTasks(ctx context.Context, in *GetTasksRequest, opts ...grpc.CallOption) (*GetTasksReply, error) { - out := new(GetTasksReply) - err := c.cc.Invoke(ctx, "/o2control.Control/GetTasks", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil +func (x *TaskInfo) String() string { + return protoimpl.X.MessageStringOf(x) } -func (c *controlClient) GetTask(ctx context.Context, in *GetTaskRequest, opts ...grpc.CallOption) (*GetTaskReply, error) { - out := new(GetTaskReply) - err := c.cc.Invoke(ctx, "/o2control.Control/GetTask", in, out, opts...) - if err != nil { - return nil, err +func (*TaskInfo) ProtoMessage() {} + +func (x *TaskInfo) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[36] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms } - return out, nil + return mi.MessageOf(x) } -func (c *controlClient) CleanupTasks(ctx context.Context, in *CleanupTasksRequest, opts ...grpc.CallOption) (*CleanupTasksReply, error) { - out := new(CleanupTasksReply) - err := c.cc.Invoke(ctx, "/o2control.Control/CleanupTasks", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil +// Deprecated: Use TaskInfo.ProtoReflect.Descriptor instead. +func (*TaskInfo) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{36} } -func (c *controlClient) GetRoles(ctx context.Context, in *GetRolesRequest, opts ...grpc.CallOption) (*GetRolesReply, error) { - out := new(GetRolesReply) - err := c.cc.Invoke(ctx, "/o2control.Control/GetRoles", in, out, opts...) - if err != nil { - return nil, err +func (x *TaskInfo) GetShortInfo() *ShortTaskInfo { + if x != nil { + return x.ShortInfo } - return out, nil + return nil } -func (c *controlClient) GetWorkflowTemplates(ctx context.Context, in *GetWorkflowTemplatesRequest, opts ...grpc.CallOption) (*GetWorkflowTemplatesReply, error) { - out := new(GetWorkflowTemplatesReply) - err := c.cc.Invoke(ctx, "/o2control.Control/GetWorkflowTemplates", in, out, opts...) - if err != nil { - return nil, err +func (x *TaskInfo) GetInboundChannels() []*ChannelInfo { + if x != nil { + return x.InboundChannels } - return out, nil + return nil } -func (c *controlClient) ListRepos(ctx context.Context, in *ListReposRequest, opts ...grpc.CallOption) (*ListReposReply, error) { - out := new(ListReposReply) - err := c.cc.Invoke(ctx, "/o2control.Control/ListRepos", in, out, opts...) - if err != nil { - return nil, err +func (x *TaskInfo) GetOutboundChannels() []*ChannelInfo { + if x != nil { + return x.OutboundChannels } - return out, nil + return nil } -func (c *controlClient) AddRepo(ctx context.Context, in *AddRepoRequest, opts ...grpc.CallOption) (*AddRepoReply, error) { - out := new(AddRepoReply) - err := c.cc.Invoke(ctx, "/o2control.Control/AddRepo", in, out, opts...) - if err != nil { - return nil, err +func (x *TaskInfo) GetCommandInfo() *CommandInfo { + if x != nil { + return x.CommandInfo } - return out, nil + return nil } -func (c *controlClient) RemoveRepo(ctx context.Context, in *RemoveRepoRequest, opts ...grpc.CallOption) (*RemoveRepoReply, error) { - out := new(RemoveRepoReply) - err := c.cc.Invoke(ctx, "/o2control.Control/RemoveRepo", in, out, opts...) - if err != nil { - return nil, err +func (x *TaskInfo) GetTaskPath() string { + if x != nil { + return x.TaskPath } - return out, nil + return "" } -func (c *controlClient) RefreshRepos(ctx context.Context, in *RefreshReposRequest, opts ...grpc.CallOption) (*RefreshReposReply, error) { - out := new(RefreshReposReply) - err := c.cc.Invoke(ctx, "/o2control.Control/RefreshRepos", in, out, opts...) - if err != nil { - return nil, err +func (x *TaskInfo) GetEnvId() string { + if x != nil { + return x.EnvId } - return out, nil + return "" } -func (c *controlClient) SetDefaultRepo(ctx context.Context, in *SetDefaultRepoRequest, opts ...grpc.CallOption) (*SetDefaultRepoReply, error) { - out := new(SetDefaultRepoReply) - err := c.cc.Invoke(ctx, "/o2control.Control/SetDefaultRepo", in, out, opts...) - if err != nil { - return nil, err +func (x *TaskInfo) GetProperties() map[string]string { + if x != nil { + return x.Properties } - return out, nil + return nil } -// ControlServer is the server API for Control service. -type ControlServer interface { - TrackStatus(*StatusRequest, Control_TrackStatusServer) error - GetFrameworkInfo(context.Context, *GetFrameworkInfoRequest) (*GetFrameworkInfoReply, error) - Teardown(context.Context, *TeardownRequest) (*TeardownReply, error) - GetEnvironments(context.Context, *GetEnvironmentsRequest) (*GetEnvironmentsReply, error) - NewEnvironment(context.Context, *NewEnvironmentRequest) (*NewEnvironmentReply, error) - GetEnvironment(context.Context, *GetEnvironmentRequest) (*GetEnvironmentReply, error) - ControlEnvironment(context.Context, *ControlEnvironmentRequest) (*ControlEnvironmentReply, error) - ModifyEnvironment(context.Context, *ModifyEnvironmentRequest) (*ModifyEnvironmentReply, error) - DestroyEnvironment(context.Context, *DestroyEnvironmentRequest) (*DestroyEnvironmentReply, error) - GetTasks(context.Context, *GetTasksRequest) (*GetTasksReply, error) - GetTask(context.Context, *GetTaskRequest) (*GetTaskReply, error) - CleanupTasks(context.Context, *CleanupTasksRequest) (*CleanupTasksReply, error) - GetRoles(context.Context, *GetRolesRequest) (*GetRolesReply, error) - GetWorkflowTemplates(context.Context, *GetWorkflowTemplatesRequest) (*GetWorkflowTemplatesReply, error) - ListRepos(context.Context, *ListReposRequest) (*ListReposReply, error) - AddRepo(context.Context, *AddRepoRequest) (*AddRepoReply, error) - RemoveRepo(context.Context, *RemoveRepoRequest) (*RemoveRepoReply, error) - RefreshRepos(context.Context, *RefreshReposRequest) (*RefreshReposReply, error) - SetDefaultRepo(context.Context, *SetDefaultRepoRequest) (*SetDefaultRepoReply, error) -} +type CleanupTasksRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields -func RegisterControlServer(s *grpc.Server, srv ControlServer) { - s.RegisterService(&_Control_serviceDesc, srv) + TaskIds []string `protobuf:"bytes,1,rep,name=taskIds,proto3" json:"taskIds,omitempty"` } -func _Control_TrackStatus_Handler(srv interface{}, stream grpc.ServerStream) error { - m := new(StatusRequest) - if err := stream.RecvMsg(m); err != nil { - return err +func (x *CleanupTasksRequest) Reset() { + *x = CleanupTasksRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[37] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return srv.(ControlServer).TrackStatus(m, &controlTrackStatusServer{stream}) } -type Control_TrackStatusServer interface { - Send(*StatusReply) error - grpc.ServerStream +func (x *CleanupTasksRequest) String() string { + return protoimpl.X.MessageStringOf(x) } -type controlTrackStatusServer struct { - grpc.ServerStream +func (*CleanupTasksRequest) ProtoMessage() {} + +func (x *CleanupTasksRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[37] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -func (x *controlTrackStatusServer) Send(m *StatusReply) error { - return x.ServerStream.SendMsg(m) +// Deprecated: Use CleanupTasksRequest.ProtoReflect.Descriptor instead. +func (*CleanupTasksRequest) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{37} } -func _Control_GetFrameworkInfo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetFrameworkInfoRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ControlServer).GetFrameworkInfo(ctx, in) +func (x *CleanupTasksRequest) GetTaskIds() []string { + if x != nil { + return x.TaskIds } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/o2control.Control/GetFrameworkInfo", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ControlServer).GetFrameworkInfo(ctx, req.(*GetFrameworkInfoRequest)) - } - return interceptor(ctx, in, info, handler) + return nil } -func _Control_Teardown_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(TeardownRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ControlServer).Teardown(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/o2control.Control/Teardown", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ControlServer).Teardown(ctx, req.(*TeardownRequest)) - } - return interceptor(ctx, in, info, handler) +type CleanupTasksReply struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + KilledTasks []*ShortTaskInfo `protobuf:"bytes,1,rep,name=killedTasks,proto3" json:"killedTasks,omitempty"` + RunningTasks []*ShortTaskInfo `protobuf:"bytes,2,rep,name=runningTasks,proto3" json:"runningTasks,omitempty"` + Timestamp int64 `protobuf:"varint,3,opt,name=timestamp,proto3" json:"timestamp,omitempty"` // timestamp of when this object was sent in unix milliseconds } -func _Control_GetEnvironments_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetEnvironmentsRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ControlServer).GetEnvironments(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/o2control.Control/GetEnvironments", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ControlServer).GetEnvironments(ctx, req.(*GetEnvironmentsRequest)) +func (x *CleanupTasksReply) Reset() { + *x = CleanupTasksReply{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[38] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return interceptor(ctx, in, info, handler) } -func _Control_NewEnvironment_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(NewEnvironmentRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ControlServer).NewEnvironment(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/o2control.Control/NewEnvironment", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ControlServer).NewEnvironment(ctx, req.(*NewEnvironmentRequest)) - } - return interceptor(ctx, in, info, handler) +func (x *CleanupTasksReply) String() string { + return protoimpl.X.MessageStringOf(x) } -func _Control_GetEnvironment_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetEnvironmentRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ControlServer).GetEnvironment(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/o2control.Control/GetEnvironment", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ControlServer).GetEnvironment(ctx, req.(*GetEnvironmentRequest)) +func (*CleanupTasksReply) ProtoMessage() {} + +func (x *CleanupTasksReply) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[38] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms } - return interceptor(ctx, in, info, handler) + return mi.MessageOf(x) } -func _Control_ControlEnvironment_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ControlEnvironmentRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ControlServer).ControlEnvironment(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/o2control.Control/ControlEnvironment", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ControlServer).ControlEnvironment(ctx, req.(*ControlEnvironmentRequest)) - } - return interceptor(ctx, in, info, handler) +// Deprecated: Use CleanupTasksReply.ProtoReflect.Descriptor instead. +func (*CleanupTasksReply) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{38} } -func _Control_ModifyEnvironment_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ModifyEnvironmentRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ControlServer).ModifyEnvironment(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/o2control.Control/ModifyEnvironment", +func (x *CleanupTasksReply) GetKilledTasks() []*ShortTaskInfo { + if x != nil { + return x.KilledTasks } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ControlServer).ModifyEnvironment(ctx, req.(*ModifyEnvironmentRequest)) - } - return interceptor(ctx, in, info, handler) + return nil } -func _Control_DestroyEnvironment_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(DestroyEnvironmentRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ControlServer).DestroyEnvironment(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/o2control.Control/DestroyEnvironment", +func (x *CleanupTasksReply) GetRunningTasks() []*ShortTaskInfo { + if x != nil { + return x.RunningTasks } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ControlServer).DestroyEnvironment(ctx, req.(*DestroyEnvironmentRequest)) - } - return interceptor(ctx, in, info, handler) + return nil } -func _Control_GetTasks_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetTasksRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ControlServer).GetTasks(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/o2control.Control/GetTasks", +func (x *CleanupTasksReply) GetTimestamp() int64 { + if x != nil { + return x.Timestamp } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ControlServer).GetTasks(ctx, req.(*GetTasksRequest)) - } - return interceptor(ctx, in, info, handler) + return 0 } -func _Control_GetTask_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetTaskRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ControlServer).GetTask(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/o2control.Control/GetTask", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ControlServer).GetTask(ctx, req.(*GetTaskRequest)) - } - return interceptor(ctx, in, info, handler) +// ////////////////////////////////////// +// Roles +// ////////////////////////////////////// +type GetRolesRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + EnvId string `protobuf:"bytes,1,opt,name=envId,proto3" json:"envId,omitempty"` + PathSpec string `protobuf:"bytes,2,opt,name=pathSpec,proto3" json:"pathSpec,omitempty"` } -func _Control_CleanupTasks_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(CleanupTasksRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ControlServer).CleanupTasks(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/o2control.Control/CleanupTasks", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ControlServer).CleanupTasks(ctx, req.(*CleanupTasksRequest)) +func (x *GetRolesRequest) Reset() { + *x = GetRolesRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[39] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return interceptor(ctx, in, info, handler) } -func _Control_GetRoles_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetRolesRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ControlServer).GetRoles(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/o2control.Control/GetRoles", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ControlServer).GetRoles(ctx, req.(*GetRolesRequest)) - } - return interceptor(ctx, in, info, handler) +func (x *GetRolesRequest) String() string { + return protoimpl.X.MessageStringOf(x) } -func _Control_GetWorkflowTemplates_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetWorkflowTemplatesRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ControlServer).GetWorkflowTemplates(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/o2control.Control/GetWorkflowTemplates", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ControlServer).GetWorkflowTemplates(ctx, req.(*GetWorkflowTemplatesRequest)) +func (*GetRolesRequest) ProtoMessage() {} + +func (x *GetRolesRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[39] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms } - return interceptor(ctx, in, info, handler) + return mi.MessageOf(x) } -func _Control_ListRepos_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ListReposRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ControlServer).ListRepos(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/o2control.Control/ListRepos", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ControlServer).ListRepos(ctx, req.(*ListReposRequest)) - } - return interceptor(ctx, in, info, handler) +// Deprecated: Use GetRolesRequest.ProtoReflect.Descriptor instead. +func (*GetRolesRequest) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{39} } -func _Control_AddRepo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(AddRepoRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ControlServer).AddRepo(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/o2control.Control/AddRepo", +func (x *GetRolesRequest) GetEnvId() string { + if x != nil { + return x.EnvId } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ControlServer).AddRepo(ctx, req.(*AddRepoRequest)) - } - return interceptor(ctx, in, info, handler) + return "" } -func _Control_RemoveRepo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(RemoveRepoRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ControlServer).RemoveRepo(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/o2control.Control/RemoveRepo", +func (x *GetRolesRequest) GetPathSpec() string { + if x != nil { + return x.PathSpec } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ControlServer).RemoveRepo(ctx, req.(*RemoveRepoRequest)) - } - return interceptor(ctx, in, info, handler) + return "" } -func _Control_RefreshRepos_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(RefreshReposRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ControlServer).RefreshRepos(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/o2control.Control/RefreshRepos", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ControlServer).RefreshRepos(ctx, req.(*RefreshReposRequest)) - } - return interceptor(ctx, in, info, handler) +type RoleInfo struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Status string `protobuf:"bytes,2,opt,name=status,proto3" json:"status,omitempty"` + State string `protobuf:"bytes,3,opt,name=state,proto3" json:"state,omitempty"` + FullPath string `protobuf:"bytes,4,opt,name=fullPath,proto3" json:"fullPath,omitempty"` + TaskIds []string `protobuf:"bytes,5,rep,name=taskIds,proto3" json:"taskIds,omitempty"` + Roles []*RoleInfo `protobuf:"bytes,6,rep,name=roles,proto3" json:"roles,omitempty"` + Defaults map[string]string `protobuf:"bytes,7,rep,name=defaults,proto3" json:"defaults,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Vars map[string]string `protobuf:"bytes,8,rep,name=vars,proto3" json:"vars,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + UserVars map[string]string `protobuf:"bytes,9,rep,name=userVars,proto3" json:"userVars,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + ConsolidatedStack map[string]string `protobuf:"bytes,10,rep,name=consolidatedStack,proto3" json:"consolidatedStack,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Description string `protobuf:"bytes,11,opt,name=description,proto3" json:"description,omitempty"` } -func _Control_SetDefaultRepo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(SetDefaultRepoRequest) - if err := dec(in); err != nil { - return nil, err +func (x *RoleInfo) Reset() { + *x = RoleInfo{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[40] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - if interceptor == nil { - return srv.(ControlServer).SetDefaultRepo(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/o2control.Control/SetDefaultRepo", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ControlServer).SetDefaultRepo(ctx, req.(*SetDefaultRepoRequest)) - } - return interceptor(ctx, in, info, handler) } -var _Control_serviceDesc = grpc.ServiceDesc{ - ServiceName: "o2control.Control", - HandlerType: (*ControlServer)(nil), - Methods: []grpc.MethodDesc{ - { - MethodName: "GetFrameworkInfo", - Handler: _Control_GetFrameworkInfo_Handler, - }, - { - MethodName: "Teardown", - Handler: _Control_Teardown_Handler, - }, - { - MethodName: "GetEnvironments", - Handler: _Control_GetEnvironments_Handler, - }, - { - MethodName: "NewEnvironment", - Handler: _Control_NewEnvironment_Handler, - }, - { - MethodName: "GetEnvironment", - Handler: _Control_GetEnvironment_Handler, - }, - { - MethodName: "ControlEnvironment", - Handler: _Control_ControlEnvironment_Handler, - }, - { - MethodName: "ModifyEnvironment", - Handler: _Control_ModifyEnvironment_Handler, - }, - { - MethodName: "DestroyEnvironment", - Handler: _Control_DestroyEnvironment_Handler, - }, - { - MethodName: "GetTasks", - Handler: _Control_GetTasks_Handler, - }, - { - MethodName: "GetTask", - Handler: _Control_GetTask_Handler, - }, - { - MethodName: "CleanupTasks", - Handler: _Control_CleanupTasks_Handler, - }, - { - MethodName: "GetRoles", - Handler: _Control_GetRoles_Handler, - }, - { - MethodName: "GetWorkflowTemplates", - Handler: _Control_GetWorkflowTemplates_Handler, - }, - { - MethodName: "ListRepos", - Handler: _Control_ListRepos_Handler, - }, - { - MethodName: "AddRepo", - Handler: _Control_AddRepo_Handler, - }, - { - MethodName: "RemoveRepo", - Handler: _Control_RemoveRepo_Handler, - }, - { - MethodName: "RefreshRepos", - Handler: _Control_RefreshRepos_Handler, - }, - { - MethodName: "SetDefaultRepo", - Handler: _Control_SetDefaultRepo_Handler, - }, - }, - Streams: []grpc.StreamDesc{ - { - StreamName: "TrackStatus", - Handler: _Control_TrackStatus_Handler, - ServerStreams: true, - }, - }, - Metadata: "protos/o2control.proto", +func (x *RoleInfo) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *Event_MesosHeartbeat) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err +func (*RoleInfo) ProtoMessage() {} + +func (x *RoleInfo) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[40] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms } - return dAtA[:n], nil + return mi.MessageOf(x) } -func (m *Event_MesosHeartbeat) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) - } - return i, nil +// Deprecated: Use RoleInfo.ProtoReflect.Descriptor instead. +func (*RoleInfo) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{40} } -func (m *StatusRequest) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err +func (x *RoleInfo) GetName() string { + if x != nil { + return x.Name } - return dAtA[:n], nil + return "" } -func (m *StatusRequest) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) +func (x *RoleInfo) GetStatus() string { + if x != nil { + return x.Status } - return i, nil + return "" } -func (m *StatusReply) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err +func (x *RoleInfo) GetState() string { + if x != nil { + return x.State } - return dAtA[:n], nil + return "" } -func (m *StatusReply) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.State) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.State))) - i += copy(dAtA[i:], m.State) - } - if len(m.StatusUpdates) > 0 { - for _, msg := range m.StatusUpdates { - dAtA[i] = 0x12 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n - } +func (x *RoleInfo) GetFullPath() string { + if x != nil { + return x.FullPath } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) - } - return i, nil + return "" } -func (m *StatusUpdate) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err +func (x *RoleInfo) GetTaskIds() []string { + if x != nil { + return x.TaskIds } - return dAtA[:n], nil + return nil } -func (m *StatusUpdate) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if m.Level != 0 { - dAtA[i] = 0x8 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(m.Level)) - } - if m.Event != nil { - nn1, err1 := m.Event.MarshalTo(dAtA[i:]) - if err1 != nil { - return 0, err1 - } - i += nn1 - } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) +func (x *RoleInfo) GetRoles() []*RoleInfo { + if x != nil { + return x.Roles } - return i, nil + return nil } -func (m *StatusUpdate_MesosHeartbeat) MarshalTo(dAtA []byte) (int, error) { - i := 0 - if m.MesosHeartbeat != nil { - dAtA[i] = 0x12 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(m.MesosHeartbeat.Size())) - n2, err2 := m.MesosHeartbeat.MarshalTo(dAtA[i:]) - if err2 != nil { - return 0, err2 - } - i += n2 - } - return i, nil -} -func (m *GetFrameworkInfoRequest) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *GetFrameworkInfoRequest) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) - } - return i, nil -} - -func (m *Version) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *Version) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if m.Major != 0 { - dAtA[i] = 0x8 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(m.Major)) - } - if m.Minor != 0 { - dAtA[i] = 0x10 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(m.Minor)) - } - if m.Patch != 0 { - dAtA[i] = 0x18 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(m.Patch)) - } - if len(m.Build) > 0 { - dAtA[i] = 0x22 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.Build))) - i += copy(dAtA[i:], m.Build) - } - if len(m.ProductName) > 0 { - dAtA[i] = 0x2a - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.ProductName))) - i += copy(dAtA[i:], m.ProductName) - } - if len(m.VersionStr) > 0 { - dAtA[i] = 0x32 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.VersionStr))) - i += copy(dAtA[i:], m.VersionStr) - } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) - } - return i, nil -} - -func (m *GetFrameworkInfoReply) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *GetFrameworkInfoReply) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.FrameworkId) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.FrameworkId))) - i += copy(dAtA[i:], m.FrameworkId) - } - if m.EnvironmentsCount != 0 { - dAtA[i] = 0x10 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(m.EnvironmentsCount)) - } - if m.TasksCount != 0 { - dAtA[i] = 0x18 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(m.TasksCount)) - } - if len(m.State) > 0 { - dAtA[i] = 0x22 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.State))) - i += copy(dAtA[i:], m.State) - } - if m.HostsCount != 0 { - dAtA[i] = 0x28 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(m.HostsCount)) - } - if len(m.InstanceName) > 0 { - dAtA[i] = 0x32 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.InstanceName))) - i += copy(dAtA[i:], m.InstanceName) - } - if m.Version != nil { - dAtA[i] = 0x3a - i++ - i = encodeVarintO2Control(dAtA, i, uint64(m.Version.Size())) - n3, err3 := m.Version.MarshalTo(dAtA[i:]) - if err3 != nil { - return 0, err3 - } - i += n3 - } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) +func (x *RoleInfo) GetDefaults() map[string]string { + if x != nil { + return x.Defaults } - return i, nil + return nil } -func (m *TeardownRequest) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err +func (x *RoleInfo) GetVars() map[string]string { + if x != nil { + return x.Vars } - return dAtA[:n], nil + return nil } -func (m *TeardownRequest) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.Reason) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.Reason))) - i += copy(dAtA[i:], m.Reason) +func (x *RoleInfo) GetUserVars() map[string]string { + if x != nil { + return x.UserVars } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) - } - return i, nil + return nil } -func (m *TeardownReply) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err +func (x *RoleInfo) GetConsolidatedStack() map[string]string { + if x != nil { + return x.ConsolidatedStack } - return dAtA[:n], nil + return nil } -func (m *TeardownReply) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) +func (x *RoleInfo) GetDescription() string { + if x != nil { + return x.Description } - return i, nil + return "" } -func (m *GetEnvironmentsRequest) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil +type GetRolesReply struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Roles []*RoleInfo `protobuf:"bytes,1,rep,name=roles,proto3" json:"roles,omitempty"` + Timestamp int64 `protobuf:"varint,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"` // timestamp of when this object was sent in unix milliseconds } -func (m *GetEnvironmentsRequest) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) +func (x *GetRolesReply) Reset() { + *x = GetRolesReply{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[41] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return i, nil } -func (m *GetEnvironmentsReply) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil +func (x *GetRolesReply) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *GetEnvironmentsReply) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.FrameworkId) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.FrameworkId))) - i += copy(dAtA[i:], m.FrameworkId) - } - if len(m.Environments) > 0 { - for _, msg := range m.Environments { - dAtA[i] = 0x12 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n - } - } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) - } - return i, nil -} - -func (m *EnvironmentInfo) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *EnvironmentInfo) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.Id) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.Id))) - i += copy(dAtA[i:], m.Id) - } - if len(m.CreatedWhen) > 0 { - dAtA[i] = 0x12 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.CreatedWhen))) - i += copy(dAtA[i:], m.CreatedWhen) - } - if len(m.State) > 0 { - dAtA[i] = 0x1a - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.State))) - i += copy(dAtA[i:], m.State) - } - if len(m.Tasks) > 0 { - for _, msg := range m.Tasks { - dAtA[i] = 0x22 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n +func (*GetRolesReply) ProtoMessage() {} + +func (x *GetRolesReply) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[41] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) } + return ms } - if len(m.RootRole) > 0 { - dAtA[i] = 0x2a - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.RootRole))) - i += copy(dAtA[i:], m.RootRole) - } - if m.CurrentRunNumber != 0 { - dAtA[i] = 0x30 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(m.CurrentRunNumber)) - } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) - } - return i, nil + return mi.MessageOf(x) } -func (m *NewEnvironmentRequest) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil +// Deprecated: Use GetRolesReply.ProtoReflect.Descriptor instead. +func (*GetRolesReply) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{41} } -func (m *NewEnvironmentRequest) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.WorkflowTemplate) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.WorkflowTemplate))) - i += copy(dAtA[i:], m.WorkflowTemplate) - } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) +func (x *GetRolesReply) GetRoles() []*RoleInfo { + if x != nil { + return x.Roles } - return i, nil + return nil } -func (m *NewEnvironmentReply) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err +func (x *GetRolesReply) GetTimestamp() int64 { + if x != nil { + return x.Timestamp } - return dAtA[:n], nil + return 0 } -func (m *NewEnvironmentReply) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if m.Environment != nil { - dAtA[i] = 0xa - i++ - i = encodeVarintO2Control(dAtA, i, uint64(m.Environment.Size())) - n4, err4 := m.Environment.MarshalTo(dAtA[i:]) - if err4 != nil { - return 0, err4 - } - i += n4 - } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) - } - return i, nil -} +type GetWorkflowTemplatesRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields -func (m *GetEnvironmentRequest) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil + RepoPattern string `protobuf:"bytes,1,opt,name=repoPattern,proto3" json:"repoPattern,omitempty"` + RevisionPattern string `protobuf:"bytes,2,opt,name=revisionPattern,proto3" json:"revisionPattern,omitempty"` + AllBranches bool `protobuf:"varint,3,opt,name=allBranches,proto3" json:"allBranches,omitempty"` + AllTags bool `protobuf:"varint,4,opt,name=allTags,proto3" json:"allTags,omitempty"` + AllWorkflows bool `protobuf:"varint,5,opt,name=allWorkflows,proto3" json:"allWorkflows,omitempty"` } -func (m *GetEnvironmentRequest) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.Id) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.Id))) - i += copy(dAtA[i:], m.Id) +func (x *GetWorkflowTemplatesRequest) Reset() { + *x = GetWorkflowTemplatesRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[42] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) - } - return i, nil } -func (m *GetEnvironmentReply) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil +func (x *GetWorkflowTemplatesRequest) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *GetEnvironmentReply) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if m.Environment != nil { - dAtA[i] = 0xa - i++ - i = encodeVarintO2Control(dAtA, i, uint64(m.Environment.Size())) - n5, err5 := m.Environment.MarshalTo(dAtA[i:]) - if err5 != nil { - return 0, err5 - } - i += n5 - } - if m.Workflow != nil { - dAtA[i] = 0x12 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(m.Workflow.Size())) - n6, err6 := m.Workflow.MarshalTo(dAtA[i:]) - if err6 != nil { - return 0, err6 - } - i += n6 - } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) - } - return i, nil -} - -func (m *ControlEnvironmentRequest) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *ControlEnvironmentRequest) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.Id) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.Id))) - i += copy(dAtA[i:], m.Id) - } - if m.Type != 0 { - dAtA[i] = 0x10 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(m.Type)) - } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) - } - return i, nil -} - -func (m *ControlEnvironmentReply) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *ControlEnvironmentReply) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.Id) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.Id))) - i += copy(dAtA[i:], m.Id) - } - if len(m.State) > 0 { - dAtA[i] = 0x12 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.State))) - i += copy(dAtA[i:], m.State) - } - if m.CurrentRunNumber != 0 { - dAtA[i] = 0x18 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(m.CurrentRunNumber)) - } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) - } - return i, nil -} - -func (m *ModifyEnvironmentRequest) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *ModifyEnvironmentRequest) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.Id) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.Id))) - i += copy(dAtA[i:], m.Id) - } - if len(m.Operations) > 0 { - for _, msg := range m.Operations { - dAtA[i] = 0x12 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n - } - } - if m.ReconfigureAll { - dAtA[i] = 0x18 - i++ - if m.ReconfigureAll { - dAtA[i] = 1 - } else { - dAtA[i] = 0 +func (*GetWorkflowTemplatesRequest) ProtoMessage() {} + +func (x *GetWorkflowTemplatesRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[42] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) } - i++ - } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) + return ms } - return i, nil + return mi.MessageOf(x) } -func (m *EnvironmentOperation) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil +// Deprecated: Use GetWorkflowTemplatesRequest.ProtoReflect.Descriptor instead. +func (*GetWorkflowTemplatesRequest) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{42} } -func (m *EnvironmentOperation) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if m.Type != 0 { - dAtA[i] = 0x8 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(m.Type)) +func (x *GetWorkflowTemplatesRequest) GetRepoPattern() string { + if x != nil { + return x.RepoPattern } - if len(m.RoleName) > 0 { - dAtA[i] = 0x12 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.RoleName))) - i += copy(dAtA[i:], m.RoleName) - } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) - } - return i, nil + return "" } -func (m *ModifyEnvironmentReply) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err +func (x *GetWorkflowTemplatesRequest) GetRevisionPattern() string { + if x != nil { + return x.RevisionPattern } - return dAtA[:n], nil + return "" } -func (m *ModifyEnvironmentReply) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.FailedOperations) > 0 { - for _, msg := range m.FailedOperations { - dAtA[i] = 0xa - i++ - i = encodeVarintO2Control(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n - } - } - if len(m.Id) > 0 { - dAtA[i] = 0x12 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.Id))) - i += copy(dAtA[i:], m.Id) - } - if len(m.State) > 0 { - dAtA[i] = 0x1a - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.State))) - i += copy(dAtA[i:], m.State) - } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) - } - return i, nil -} - -func (m *DestroyEnvironmentRequest) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *DestroyEnvironmentRequest) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.Id) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.Id))) - i += copy(dAtA[i:], m.Id) - } - if m.KeepTasks { - dAtA[i] = 0x10 - i++ - if m.KeepTasks { - dAtA[i] = 1 - } else { - dAtA[i] = 0 - } - i++ +func (x *GetWorkflowTemplatesRequest) GetAllBranches() bool { + if x != nil { + return x.AllBranches } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) - } - return i, nil + return false } -func (m *DestroyEnvironmentReply) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err +func (x *GetWorkflowTemplatesRequest) GetAllTags() bool { + if x != nil { + return x.AllTags } - return dAtA[:n], nil + return false } -func (m *DestroyEnvironmentReply) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if m.CleanupTasksReply != nil { - dAtA[i] = 0xa - i++ - i = encodeVarintO2Control(dAtA, i, uint64(m.CleanupTasksReply.Size())) - n7, err7 := m.CleanupTasksReply.MarshalTo(dAtA[i:]) - if err7 != nil { - return 0, err7 - } - i += n7 - } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) - } - return i, nil -} - -func (m *ShortTaskInfo) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *ShortTaskInfo) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.Name) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.Name))) - i += copy(dAtA[i:], m.Name) - } - if m.Locked { - dAtA[i] = 0x10 - i++ - if m.Locked { - dAtA[i] = 1 - } else { - dAtA[i] = 0 - } - i++ - } - if len(m.TaskId) > 0 { - dAtA[i] = 0x1a - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.TaskId))) - i += copy(dAtA[i:], m.TaskId) - } - if len(m.Status) > 0 { - dAtA[i] = 0x22 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.Status))) - i += copy(dAtA[i:], m.Status) - } - if len(m.State) > 0 { - dAtA[i] = 0x2a - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.State))) - i += copy(dAtA[i:], m.State) - } - if len(m.ClassName) > 0 { - dAtA[i] = 0x32 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.ClassName))) - i += copy(dAtA[i:], m.ClassName) - } - if m.DeploymentInfo != nil { - dAtA[i] = 0x3a - i++ - i = encodeVarintO2Control(dAtA, i, uint64(m.DeploymentInfo.Size())) - n8, err8 := m.DeploymentInfo.MarshalTo(dAtA[i:]) - if err8 != nil { - return 0, err8 - } - i += n8 - } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) - } - return i, nil -} - -func (m *TaskDeploymentInfo) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *TaskDeploymentInfo) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.Hostname) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.Hostname))) - i += copy(dAtA[i:], m.Hostname) - } - if len(m.AgentId) > 0 { - dAtA[i] = 0x12 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.AgentId))) - i += copy(dAtA[i:], m.AgentId) - } - if len(m.OfferId) > 0 { - dAtA[i] = 0x1a - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.OfferId))) - i += copy(dAtA[i:], m.OfferId) - } - if len(m.ExecutorId) > 0 { - dAtA[i] = 0x22 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.ExecutorId))) - i += copy(dAtA[i:], m.ExecutorId) - } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) - } - return i, nil -} - -func (m *GetTasksRequest) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *GetTasksRequest) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) - } - return i, nil -} - -func (m *GetTasksReply) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *GetTasksReply) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.Tasks) > 0 { - for _, msg := range m.Tasks { - dAtA[i] = 0xa - i++ - i = encodeVarintO2Control(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n - } +func (x *GetWorkflowTemplatesRequest) GetAllWorkflows() bool { + if x != nil { + return x.AllWorkflows } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) - } - return i, nil + return false } -func (m *GetTaskRequest) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil +type VarSpecMessage struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + DefaultValue string `protobuf:"bytes,1,opt,name=defaultValue,proto3" json:"defaultValue,omitempty"` + Type VarSpecMessage_Type `protobuf:"varint,2,opt,name=type,proto3,enum=o2control.VarSpecMessage_Type" json:"type,omitempty"` + Label string `protobuf:"bytes,3,opt,name=label,proto3" json:"label,omitempty"` + Description string `protobuf:"bytes,4,opt,name=description,proto3" json:"description,omitempty"` + Widget VarSpecMessage_UiWidget `protobuf:"varint,5,opt,name=widget,proto3,enum=o2control.VarSpecMessage_UiWidget" json:"widget,omitempty"` + Panel string `protobuf:"bytes,6,opt,name=panel,proto3" json:"panel,omitempty"` // hint for the UI on where to put or group the given variable input + AllowedValues []string `protobuf:"bytes,7,rep,name=allowedValues,proto3" json:"allowedValues,omitempty"` // list of offered values from which to choose (only for some UiWidgets) + Index int32 `protobuf:"varint,8,opt,name=index,proto3" json:"index,omitempty"` + VisibleIf string `protobuf:"bytes,9,opt,name=visibleIf,proto3" json:"visibleIf,omitempty"` // JS expression that evaluates to bool + EnabledIf string `protobuf:"bytes,10,opt,name=enabledIf,proto3" json:"enabledIf,omitempty"` // JS expression that evaluates to bool + Rows uint32 `protobuf:"varint,11,opt,name=rows,proto3" json:"rows,omitempty"` // this field is used only if widget == editBox } -func (m *GetTaskRequest) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.TaskId) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.TaskId))) - i += copy(dAtA[i:], m.TaskId) +func (x *VarSpecMessage) Reset() { + *x = VarSpecMessage{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[43] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) - } - return i, nil } -func (m *GetTaskReply) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil +func (x *VarSpecMessage) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *GetTaskReply) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if m.Task != nil { - dAtA[i] = 0xa - i++ - i = encodeVarintO2Control(dAtA, i, uint64(m.Task.Size())) - n9, err9 := m.Task.MarshalTo(dAtA[i:]) - if err9 != nil { - return 0, err9 - } - i += n9 - } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) - } - return i, nil -} - -func (m *TaskClassInfo) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *TaskClassInfo) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.Name) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.Name))) - i += copy(dAtA[i:], m.Name) - } - if len(m.ControlMode) > 0 { - dAtA[i] = 0x12 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.ControlMode))) - i += copy(dAtA[i:], m.ControlMode) - } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) - } - return i, nil -} - -func (m *CommandInfo) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *CommandInfo) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.Env) > 0 { - for _, s := range m.Env { - dAtA[i] = 0xa - i++ - l = len(s) - for l >= 1<<7 { - dAtA[i] = uint8(uint64(l)&0x7f | 0x80) - l >>= 7 - i++ - } - dAtA[i] = uint8(l) - i++ - i += copy(dAtA[i:], s) - } - } - if m.Shell { - dAtA[i] = 0x10 - i++ - if m.Shell { - dAtA[i] = 1 - } else { - dAtA[i] = 0 - } - i++ - } - if len(m.Value) > 0 { - dAtA[i] = 0x1a - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.Value))) - i += copy(dAtA[i:], m.Value) - } - if len(m.Arguments) > 0 { - for _, s := range m.Arguments { - dAtA[i] = 0x22 - i++ - l = len(s) - for l >= 1<<7 { - dAtA[i] = uint8(uint64(l)&0x7f | 0x80) - l >>= 7 - i++ - } - dAtA[i] = uint8(l) - i++ - i += copy(dAtA[i:], s) - } - } - if len(m.User) > 0 { - dAtA[i] = 0x2a - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.User))) - i += copy(dAtA[i:], m.User) - } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) - } - return i, nil -} - -func (m *ChannelInfo) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *ChannelInfo) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.Name) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.Name))) - i += copy(dAtA[i:], m.Name) - } - if len(m.Type) > 0 { - dAtA[i] = 0x12 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.Type))) - i += copy(dAtA[i:], m.Type) - } - if len(m.Target) > 0 { - dAtA[i] = 0x1a - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.Target))) - i += copy(dAtA[i:], m.Target) - } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) - } - return i, nil -} - -func (m *TaskInfo) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *TaskInfo) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if m.ShortInfo != nil { - dAtA[i] = 0xa - i++ - i = encodeVarintO2Control(dAtA, i, uint64(m.ShortInfo.Size())) - n10, err10 := m.ShortInfo.MarshalTo(dAtA[i:]) - if err10 != nil { - return 0, err10 - } - i += n10 - } - if m.ClassInfo != nil { - dAtA[i] = 0x12 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(m.ClassInfo.Size())) - n11, err11 := m.ClassInfo.MarshalTo(dAtA[i:]) - if err11 != nil { - return 0, err11 - } - i += n11 - } - if len(m.InboundChannels) > 0 { - for _, msg := range m.InboundChannels { - dAtA[i] = 0x1a - i++ - i = encodeVarintO2Control(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n - } - } - if len(m.OutboundChannels) > 0 { - for _, msg := range m.OutboundChannels { - dAtA[i] = 0x22 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n - } - } - if m.CommandInfo != nil { - dAtA[i] = 0x2a - i++ - i = encodeVarintO2Control(dAtA, i, uint64(m.CommandInfo.Size())) - n12, err12 := m.CommandInfo.MarshalTo(dAtA[i:]) - if err12 != nil { - return 0, err12 - } - i += n12 - } - if len(m.TaskPath) > 0 { - dAtA[i] = 0x32 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.TaskPath))) - i += copy(dAtA[i:], m.TaskPath) - } - if len(m.EnvId) > 0 { - dAtA[i] = 0x3a - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.EnvId))) - i += copy(dAtA[i:], m.EnvId) - } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) - } - return i, nil -} - -func (m *CleanupTasksRequest) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *CleanupTasksRequest) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.TaskIds) > 0 { - for _, s := range m.TaskIds { - dAtA[i] = 0xa - i++ - l = len(s) - for l >= 1<<7 { - dAtA[i] = uint8(uint64(l)&0x7f | 0x80) - l >>= 7 - i++ - } - dAtA[i] = uint8(l) - i++ - i += copy(dAtA[i:], s) +func (*VarSpecMessage) ProtoMessage() {} + +func (x *VarSpecMessage) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[43] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) } + return ms } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) - } - return i, nil + return mi.MessageOf(x) } -func (m *CleanupTasksReply) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil +// Deprecated: Use VarSpecMessage.ProtoReflect.Descriptor instead. +func (*VarSpecMessage) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{43} } -func (m *CleanupTasksReply) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.KilledTasks) > 0 { - for _, msg := range m.KilledTasks { - dAtA[i] = 0xa - i++ - i = encodeVarintO2Control(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n - } - } - if len(m.RunningTasks) > 0 { - for _, msg := range m.RunningTasks { - dAtA[i] = 0x12 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n - } +func (x *VarSpecMessage) GetDefaultValue() string { + if x != nil { + return x.DefaultValue } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) - } - return i, nil -} - -func (m *GetRolesRequest) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *GetRolesRequest) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.EnvId) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.EnvId))) - i += copy(dAtA[i:], m.EnvId) - } - if len(m.PathSpec) > 0 { - dAtA[i] = 0x12 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.PathSpec))) - i += copy(dAtA[i:], m.PathSpec) - } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) - } - return i, nil -} - -func (m *RoleInfo) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *RoleInfo) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.Name) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.Name))) - i += copy(dAtA[i:], m.Name) - } - if len(m.Status) > 0 { - dAtA[i] = 0x12 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.Status))) - i += copy(dAtA[i:], m.Status) - } - if len(m.State) > 0 { - dAtA[i] = 0x1a - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.State))) - i += copy(dAtA[i:], m.State) - } - if len(m.FullPath) > 0 { - dAtA[i] = 0x22 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.FullPath))) - i += copy(dAtA[i:], m.FullPath) - } - if len(m.TaskIds) > 0 { - for _, s := range m.TaskIds { - dAtA[i] = 0x2a - i++ - l = len(s) - for l >= 1<<7 { - dAtA[i] = uint8(uint64(l)&0x7f | 0x80) - l >>= 7 - i++ - } - dAtA[i] = uint8(l) - i++ - i += copy(dAtA[i:], s) - } - } - if len(m.Roles) > 0 { - for _, msg := range m.Roles { - dAtA[i] = 0x32 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n - } - } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) - } - return i, nil + return "" } -func (m *GetRolesReply) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err +func (x *VarSpecMessage) GetType() VarSpecMessage_Type { + if x != nil { + return x.Type } - return dAtA[:n], nil + return VarSpecMessage_string } -func (m *GetRolesReply) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.Roles) > 0 { - for _, msg := range m.Roles { - dAtA[i] = 0xa - i++ - i = encodeVarintO2Control(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n - } +func (x *VarSpecMessage) GetLabel() string { + if x != nil { + return x.Label } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) - } - return i, nil + return "" } -func (m *GetWorkflowTemplatesRequest) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err +func (x *VarSpecMessage) GetDescription() string { + if x != nil { + return x.Description } - return dAtA[:n], nil + return "" } -func (m *GetWorkflowTemplatesRequest) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) +func (x *VarSpecMessage) GetWidget() VarSpecMessage_UiWidget { + if x != nil { + return x.Widget } - return i, nil + return VarSpecMessage_editBox } -func (m *WorkflowTemplateInfo) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err +func (x *VarSpecMessage) GetPanel() string { + if x != nil { + return x.Panel } - return dAtA[:n], nil + return "" } -func (m *WorkflowTemplateInfo) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.Repo) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.Repo))) - i += copy(dAtA[i:], m.Repo) - } - if len(m.Template) > 0 { - dAtA[i] = 0x12 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.Template))) - i += copy(dAtA[i:], m.Template) - } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) +func (x *VarSpecMessage) GetAllowedValues() []string { + if x != nil { + return x.AllowedValues } - return i, nil + return nil } -func (m *GetWorkflowTemplatesReply) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err +func (x *VarSpecMessage) GetIndex() int32 { + if x != nil { + return x.Index } - return dAtA[:n], nil + return 0 } -func (m *GetWorkflowTemplatesReply) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.WorkflowTemplates) > 0 { - for _, msg := range m.WorkflowTemplates { - dAtA[i] = 0xa - i++ - i = encodeVarintO2Control(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n - } +func (x *VarSpecMessage) GetVisibleIf() string { + if x != nil { + return x.VisibleIf } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) - } - return i, nil + return "" } -func (m *ListReposRequest) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err +func (x *VarSpecMessage) GetEnabledIf() string { + if x != nil { + return x.EnabledIf } - return dAtA[:n], nil + return "" } -func (m *ListReposRequest) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) +func (x *VarSpecMessage) GetRows() uint32 { + if x != nil { + return x.Rows } - return i, nil + return 0 } -func (m *RepoInfo) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil +type WorkflowTemplateInfo struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Repo string `protobuf:"bytes,1,opt,name=repo,proto3" json:"repo,omitempty"` + Template string `protobuf:"bytes,2,opt,name=template,proto3" json:"template,omitempty"` + Revision string `protobuf:"bytes,3,opt,name=revision,proto3" json:"revision,omitempty"` + VarSpecMap map[string]*VarSpecMessage `protobuf:"bytes,4,rep,name=varSpecMap,proto3" json:"varSpecMap,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Description string `protobuf:"bytes,5,opt,name=description,proto3" json:"description,omitempty"` } -func (m *RepoInfo) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.Name) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.Name))) - i += copy(dAtA[i:], m.Name) - } - if m.Default { - dAtA[i] = 0x10 - i++ - if m.Default { - dAtA[i] = 1 - } else { - dAtA[i] = 0 - } - i++ - } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) +func (x *WorkflowTemplateInfo) Reset() { + *x = WorkflowTemplateInfo{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[44] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return i, nil } -func (m *ListReposReply) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil +func (x *WorkflowTemplateInfo) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *ListReposReply) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.Repos) > 0 { - for _, msg := range m.Repos { - dAtA[i] = 0xa - i++ - i = encodeVarintO2Control(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n +func (*WorkflowTemplateInfo) ProtoMessage() {} + +func (x *WorkflowTemplateInfo) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[44] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) } + return ms } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) - } - return i, nil + return mi.MessageOf(x) } -func (m *AddRepoRequest) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil +// Deprecated: Use WorkflowTemplateInfo.ProtoReflect.Descriptor instead. +func (*WorkflowTemplateInfo) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{44} } -func (m *AddRepoRequest) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.Name) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.Name))) - i += copy(dAtA[i:], m.Name) +func (x *WorkflowTemplateInfo) GetRepo() string { + if x != nil { + return x.Repo } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) - } - return i, nil + return "" } -func (m *AddRepoReply) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err +func (x *WorkflowTemplateInfo) GetTemplate() string { + if x != nil { + return x.Template } - return dAtA[:n], nil + return "" } -func (m *AddRepoReply) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.ErrorString) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.ErrorString))) - i += copy(dAtA[i:], m.ErrorString) - } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) +func (x *WorkflowTemplateInfo) GetRevision() string { + if x != nil { + return x.Revision } - return i, nil + return "" } -func (m *RemoveRepoRequest) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err +func (x *WorkflowTemplateInfo) GetVarSpecMap() map[string]*VarSpecMessage { + if x != nil { + return x.VarSpecMap } - return dAtA[:n], nil + return nil } -func (m *RemoveRepoRequest) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if m.Index != 0 { - dAtA[i] = 0x8 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(m.Index)) +func (x *WorkflowTemplateInfo) GetDescription() string { + if x != nil { + return x.Description } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) - } - return i, nil + return "" } -func (m *RemoveRepoReply) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil +type GetWorkflowTemplatesReply struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + WorkflowTemplates []*WorkflowTemplateInfo `protobuf:"bytes,1,rep,name=workflowTemplates,proto3" json:"workflowTemplates,omitempty"` + Timestamp int64 `protobuf:"varint,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"` // timestamp of when this object was sent in unix milliseconds } -func (m *RemoveRepoReply) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if m.Ok { - dAtA[i] = 0x8 - i++ - if m.Ok { - dAtA[i] = 1 - } else { - dAtA[i] = 0 - } - i++ +func (x *GetWorkflowTemplatesReply) Reset() { + *x = GetWorkflowTemplatesReply{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[45] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - if len(m.NewDefaultRepo) > 0 { - dAtA[i] = 0x12 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.NewDefaultRepo))) - i += copy(dAtA[i:], m.NewDefaultRepo) - } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) - } - return i, nil } -func (m *RefreshReposRequest) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil +func (x *GetWorkflowTemplatesReply) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *RefreshReposRequest) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if m.Index != 0 { - dAtA[i] = 0x8 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(m.Index)) - } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) +func (*GetWorkflowTemplatesReply) ProtoMessage() {} + +func (x *GetWorkflowTemplatesReply) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[45] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms } - return i, nil + return mi.MessageOf(x) } -func (m *RefreshReposReply) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil +// Deprecated: Use GetWorkflowTemplatesReply.ProtoReflect.Descriptor instead. +func (*GetWorkflowTemplatesReply) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{45} } -func (m *RefreshReposReply) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.ErrorString) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.ErrorString))) - i += copy(dAtA[i:], m.ErrorString) +func (x *GetWorkflowTemplatesReply) GetWorkflowTemplates() []*WorkflowTemplateInfo { + if x != nil { + return x.WorkflowTemplates } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) - } - return i, nil + return nil } -func (m *SetDefaultRepoRequest) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err +func (x *GetWorkflowTemplatesReply) GetTimestamp() int64 { + if x != nil { + return x.Timestamp } - return dAtA[:n], nil + return 0 } -func (m *SetDefaultRepoRequest) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if m.Index != 0 { - dAtA[i] = 0x8 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(m.Index)) - } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) - } - return i, nil +type ListReposRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + GetRevisions bool `protobuf:"varint,1,opt,name=getRevisions,proto3" json:"getRevisions,omitempty"` } -func (m *SetDefaultRepoReply) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err +func (x *ListReposRequest) Reset() { + *x = ListReposRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[46] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return dAtA[:n], nil } -func (m *SetDefaultRepoReply) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.ErrorString) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.ErrorString))) - i += copy(dAtA[i:], m.ErrorString) - } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) - } - return i, nil +func (x *ListReposRequest) String() string { + return protoimpl.X.MessageStringOf(x) } -func encodeVarintO2Control(dAtA []byte, offset int, v uint64) int { - for v >= 1<<7 { - dAtA[offset] = uint8(v&0x7f | 0x80) - v >>= 7 - offset++ +func (*ListReposRequest) ProtoMessage() {} + +func (x *ListReposRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[46] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms } - dAtA[offset] = uint8(v) - return offset + 1 + return mi.MessageOf(x) } -func (m *Event_MesosHeartbeat) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n + +// Deprecated: Use ListReposRequest.ProtoReflect.Descriptor instead. +func (*ListReposRequest) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{46} } -func (m *StatusRequest) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) +func (x *ListReposRequest) GetGetRevisions() bool { + if x != nil { + return x.GetRevisions } - return n + return false } -func (m *StatusReply) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.State) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) +type RepoInfo struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Default bool `protobuf:"varint,2,opt,name=default,proto3" json:"default,omitempty"` + DefaultRevision string `protobuf:"bytes,3,opt,name=defaultRevision,proto3" json:"defaultRevision,omitempty"` + Revisions []string `protobuf:"bytes,4,rep,name=revisions,proto3" json:"revisions,omitempty"` +} + +func (x *RepoInfo) Reset() { + *x = RepoInfo{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[47] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - if len(m.StatusUpdates) > 0 { - for _, e := range m.StatusUpdates { - l = e.Size() - n += 1 + l + sovO2Control(uint64(l)) +} + +func (x *RepoInfo) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RepoInfo) ProtoMessage() {} + +func (x *RepoInfo) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[47] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) } + return ms } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n + return mi.MessageOf(x) } -func (m *StatusUpdate) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.Level != 0 { - n += 1 + sovO2Control(uint64(m.Level)) - } - if m.Event != nil { - n += m.Event.Size() - } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n +// Deprecated: Use RepoInfo.ProtoReflect.Descriptor instead. +func (*RepoInfo) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{47} } -func (m *StatusUpdate_MesosHeartbeat) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.MesosHeartbeat != nil { - l = m.MesosHeartbeat.Size() - n += 1 + l + sovO2Control(uint64(l)) +func (x *RepoInfo) GetName() string { + if x != nil { + return x.Name } - return n + return "" } -func (m *GetFrameworkInfoRequest) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) + +func (x *RepoInfo) GetDefault() bool { + if x != nil { + return x.Default } - return n + return false } -func (m *Version) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.Major != 0 { - n += 1 + sovO2Control(uint64(m.Major)) - } - if m.Minor != 0 { - n += 1 + sovO2Control(uint64(m.Minor)) +func (x *RepoInfo) GetDefaultRevision() string { + if x != nil { + return x.DefaultRevision } - if m.Patch != 0 { - n += 1 + sovO2Control(uint64(m.Patch)) - } - l = len(m.Build) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - l = len(m.ProductName) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - l = len(m.VersionStr) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n + return "" } -func (m *GetFrameworkInfoReply) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.FrameworkId) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - if m.EnvironmentsCount != 0 { - n += 1 + sovO2Control(uint64(m.EnvironmentsCount)) +func (x *RepoInfo) GetRevisions() []string { + if x != nil { + return x.Revisions } - if m.TasksCount != 0 { - n += 1 + sovO2Control(uint64(m.TasksCount)) - } - l = len(m.State) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - if m.HostsCount != 0 { - n += 1 + sovO2Control(uint64(m.HostsCount)) - } - l = len(m.InstanceName) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - if m.Version != nil { - l = m.Version.Size() - n += 1 + l + sovO2Control(uint64(l)) - } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n + return nil } -func (m *TeardownRequest) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.Reason) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n +type ListReposReply struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Repos []*RepoInfo `protobuf:"bytes,1,rep,name=repos,proto3" json:"repos,omitempty"` + GlobalDefaultRevision string `protobuf:"bytes,2,opt,name=globalDefaultRevision,proto3" json:"globalDefaultRevision,omitempty"` + Timestamp int64 `protobuf:"varint,3,opt,name=timestamp,proto3" json:"timestamp,omitempty"` // timestamp of when this object was sent in unix milliseconds } -func (m *TeardownReply) Size() (n int) { - if m == nil { - return 0 +func (x *ListReposReply) Reset() { + *x = ListReposReply{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[48] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - var l int - _ = l - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n } -func (m *GetEnvironmentsRequest) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n +func (x *ListReposReply) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *GetEnvironmentsReply) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.FrameworkId) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - if len(m.Environments) > 0 { - for _, e := range m.Environments { - l = e.Size() - n += 1 + l + sovO2Control(uint64(l)) +func (*ListReposReply) ProtoMessage() {} + +func (x *ListReposReply) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[48] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) } + return ms } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n + return mi.MessageOf(x) } -func (m *EnvironmentInfo) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.Id) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - l = len(m.CreatedWhen) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - l = len(m.State) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - if len(m.Tasks) > 0 { - for _, e := range m.Tasks { - l = e.Size() - n += 1 + l + sovO2Control(uint64(l)) - } - } - l = len(m.RootRole) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - if m.CurrentRunNumber != 0 { - n += 1 + sovO2Control(uint64(m.CurrentRunNumber)) - } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n +// Deprecated: Use ListReposReply.ProtoReflect.Descriptor instead. +func (*ListReposReply) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{48} } -func (m *NewEnvironmentRequest) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.WorkflowTemplate) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) +func (x *ListReposReply) GetRepos() []*RepoInfo { + if x != nil { + return x.Repos } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n + return nil } -func (m *NewEnvironmentReply) Size() (n int) { - if m == nil { - return 0 +func (x *ListReposReply) GetGlobalDefaultRevision() string { + if x != nil { + return x.GlobalDefaultRevision } - var l int - _ = l - if m.Environment != nil { - l = m.Environment.Size() - n += 1 + l + sovO2Control(uint64(l)) - } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n + return "" } -func (m *GetEnvironmentRequest) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.Id) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) +func (x *ListReposReply) GetTimestamp() int64 { + if x != nil { + return x.Timestamp } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n + return 0 } -func (m *GetEnvironmentReply) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.Environment != nil { - l = m.Environment.Size() - n += 1 + l + sovO2Control(uint64(l)) - } - if m.Workflow != nil { - l = m.Workflow.Size() - n += 1 + l + sovO2Control(uint64(l)) - } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n +type AddRepoRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + DefaultRevision string `protobuf:"bytes,2,opt,name=defaultRevision,proto3" json:"defaultRevision,omitempty"` } -func (m *ControlEnvironmentRequest) Size() (n int) { - if m == nil { - return 0 +func (x *AddRepoRequest) Reset() { + *x = AddRepoRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[49] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - var l int - _ = l - l = len(m.Id) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - if m.Type != 0 { - n += 1 + sovO2Control(uint64(m.Type)) - } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n } -func (m *ControlEnvironmentReply) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.Id) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - l = len(m.State) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - if m.CurrentRunNumber != 0 { - n += 1 + sovO2Control(uint64(m.CurrentRunNumber)) - } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n +func (x *AddRepoRequest) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *ModifyEnvironmentRequest) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.Id) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - if len(m.Operations) > 0 { - for _, e := range m.Operations { - l = e.Size() - n += 1 + l + sovO2Control(uint64(l)) +func (*AddRepoRequest) ProtoMessage() {} + +func (x *AddRepoRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[49] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) } + return ms } - if m.ReconfigureAll { - n += 2 - } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n + return mi.MessageOf(x) } -func (m *EnvironmentOperation) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.Type != 0 { - n += 1 + sovO2Control(uint64(m.Type)) - } - l = len(m.RoleName) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n +// Deprecated: Use AddRepoRequest.ProtoReflect.Descriptor instead. +func (*AddRepoRequest) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{49} } -func (m *ModifyEnvironmentReply) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if len(m.FailedOperations) > 0 { - for _, e := range m.FailedOperations { - l = e.Size() - n += 1 + l + sovO2Control(uint64(l)) - } +func (x *AddRepoRequest) GetName() string { + if x != nil { + return x.Name } - l = len(m.Id) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - l = len(m.State) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n + return "" } -func (m *DestroyEnvironmentRequest) Size() (n int) { - if m == nil { - return 0 +func (x *AddRepoRequest) GetDefaultRevision() string { + if x != nil { + return x.DefaultRevision } - var l int - _ = l - l = len(m.Id) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - if m.KeepTasks { - n += 2 - } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n + return "" } -func (m *DestroyEnvironmentReply) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.CleanupTasksReply != nil { - l = m.CleanupTasksReply.Size() - n += 1 + l + sovO2Control(uint64(l)) - } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n -} +type AddRepoReply struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields -func (m *ShortTaskInfo) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.Name) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - if m.Locked { - n += 2 - } - l = len(m.TaskId) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - l = len(m.Status) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - l = len(m.State) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - l = len(m.ClassName) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - if m.DeploymentInfo != nil { - l = m.DeploymentInfo.Size() - n += 1 + l + sovO2Control(uint64(l)) - } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n + NewDefaultRevision string `protobuf:"bytes,1,opt,name=newDefaultRevision,proto3" json:"newDefaultRevision,omitempty"` + Info string `protobuf:"bytes,2,opt,name=info,proto3" json:"info,omitempty"` + Timestamp int64 `protobuf:"varint,3,opt,name=timestamp,proto3" json:"timestamp,omitempty"` // timestamp of when this object was sent in unix milliseconds } -func (m *TaskDeploymentInfo) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.Hostname) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - l = len(m.AgentId) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) +func (x *AddRepoReply) Reset() { + *x = AddRepoReply{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[50] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - l = len(m.OfferId) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - l = len(m.ExecutorId) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n } -func (m *GetTasksRequest) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n +func (x *AddRepoReply) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *GetTasksReply) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if len(m.Tasks) > 0 { - for _, e := range m.Tasks { - l = e.Size() - n += 1 + l + sovO2Control(uint64(l)) +func (*AddRepoReply) ProtoMessage() {} + +func (x *AddRepoReply) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[50] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) } + return ms } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n + return mi.MessageOf(x) } -func (m *GetTaskRequest) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.TaskId) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n +// Deprecated: Use AddRepoReply.ProtoReflect.Descriptor instead. +func (*AddRepoReply) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{50} } -func (m *GetTaskReply) Size() (n int) { - if m == nil { - return 0 +func (x *AddRepoReply) GetNewDefaultRevision() string { + if x != nil { + return x.NewDefaultRevision } - var l int - _ = l - if m.Task != nil { - l = m.Task.Size() - n += 1 + l + sovO2Control(uint64(l)) - } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n + return "" } -func (m *TaskClassInfo) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.Name) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) +func (x *AddRepoReply) GetInfo() string { + if x != nil { + return x.Info } - l = len(m.ControlMode) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n + return "" } -func (m *CommandInfo) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if len(m.Env) > 0 { - for _, s := range m.Env { - l = len(s) - n += 1 + l + sovO2Control(uint64(l)) - } - } - if m.Shell { - n += 2 - } - l = len(m.Value) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - if len(m.Arguments) > 0 { - for _, s := range m.Arguments { - l = len(s) - n += 1 + l + sovO2Control(uint64(l)) - } - } - l = len(m.User) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) +func (x *AddRepoReply) GetTimestamp() int64 { + if x != nil { + return x.Timestamp } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n + return 0 } -func (m *ChannelInfo) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.Name) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - l = len(m.Type) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - l = len(m.Target) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n +type RemoveRepoRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Index int32 `protobuf:"varint,1,opt,name=index,proto3" json:"index,omitempty"` } -func (m *TaskInfo) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.ShortInfo != nil { - l = m.ShortInfo.Size() - n += 1 + l + sovO2Control(uint64(l)) - } - if m.ClassInfo != nil { - l = m.ClassInfo.Size() - n += 1 + l + sovO2Control(uint64(l)) - } - if len(m.InboundChannels) > 0 { - for _, e := range m.InboundChannels { - l = e.Size() - n += 1 + l + sovO2Control(uint64(l)) - } - } - if len(m.OutboundChannels) > 0 { - for _, e := range m.OutboundChannels { - l = e.Size() - n += 1 + l + sovO2Control(uint64(l)) - } - } - if m.CommandInfo != nil { - l = m.CommandInfo.Size() - n += 1 + l + sovO2Control(uint64(l)) - } - l = len(m.TaskPath) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - l = len(m.EnvId) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) +func (x *RemoveRepoRequest) Reset() { + *x = RemoveRepoRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[51] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n } -func (m *CleanupTasksRequest) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if len(m.TaskIds) > 0 { - for _, s := range m.TaskIds { - l = len(s) - n += 1 + l + sovO2Control(uint64(l)) - } - } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n +func (x *RemoveRepoRequest) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *CleanupTasksReply) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if len(m.KilledTasks) > 0 { - for _, e := range m.KilledTasks { - l = e.Size() - n += 1 + l + sovO2Control(uint64(l)) - } - } - if len(m.RunningTasks) > 0 { - for _, e := range m.RunningTasks { - l = e.Size() - n += 1 + l + sovO2Control(uint64(l)) +func (*RemoveRepoRequest) ProtoMessage() {} + +func (x *RemoveRepoRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[51] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) } + return ms } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n + return mi.MessageOf(x) } -func (m *GetRolesRequest) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.EnvId) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - l = len(m.PathSpec) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n +// Deprecated: Use RemoveRepoRequest.ProtoReflect.Descriptor instead. +func (*RemoveRepoRequest) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{51} } -func (m *RoleInfo) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.Name) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - l = len(m.Status) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - l = len(m.State) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - l = len(m.FullPath) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - if len(m.TaskIds) > 0 { - for _, s := range m.TaskIds { - l = len(s) - n += 1 + l + sovO2Control(uint64(l)) - } - } - if len(m.Roles) > 0 { - for _, e := range m.Roles { - l = e.Size() - n += 1 + l + sovO2Control(uint64(l)) - } - } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) +func (x *RemoveRepoRequest) GetIndex() int32 { + if x != nil { + return x.Index } - return n + return 0 } -func (m *GetRolesReply) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if len(m.Roles) > 0 { - for _, e := range m.Roles { - l = e.Size() - n += 1 + l + sovO2Control(uint64(l)) - } - } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n +type RemoveRepoReply struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + NewDefaultRepo string `protobuf:"bytes,1,opt,name=newDefaultRepo,proto3" json:"newDefaultRepo,omitempty"` + Timestamp int64 `protobuf:"varint,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"` // timestamp of when this object was sent in unix milliseconds } -func (m *GetWorkflowTemplatesRequest) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) +func (x *RemoveRepoReply) Reset() { + *x = RemoveRepoReply{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[52] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return n } -func (m *WorkflowTemplateInfo) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.Repo) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - l = len(m.Template) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n +func (x *RemoveRepoReply) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *GetWorkflowTemplatesReply) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if len(m.WorkflowTemplates) > 0 { - for _, e := range m.WorkflowTemplates { - l = e.Size() - n += 1 + l + sovO2Control(uint64(l)) +func (*RemoveRepoReply) ProtoMessage() {} + +func (x *RemoveRepoReply) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[52] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) } + return ms } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n + return mi.MessageOf(x) } -func (m *ListReposRequest) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n +// Deprecated: Use RemoveRepoReply.ProtoReflect.Descriptor instead. +func (*RemoveRepoReply) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{52} } -func (m *RepoInfo) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.Name) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - if m.Default { - n += 2 +func (x *RemoveRepoReply) GetNewDefaultRepo() string { + if x != nil { + return x.NewDefaultRepo } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n + return "" } -func (m *ListReposReply) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if len(m.Repos) > 0 { - for _, e := range m.Repos { - l = e.Size() - n += 1 + l + sovO2Control(uint64(l)) - } +func (x *RemoveRepoReply) GetTimestamp() int64 { + if x != nil { + return x.Timestamp } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n + return 0 } -func (m *AddRepoRequest) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.Name) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n -} +type RefreshReposRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields -func (m *AddRepoReply) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.ErrorString) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n + Index int32 `protobuf:"varint,1,opt,name=index,proto3" json:"index,omitempty"` } -func (m *RemoveRepoRequest) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.Index != 0 { - n += 1 + sovO2Control(uint64(m.Index)) - } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) +func (x *RefreshReposRequest) Reset() { + *x = RefreshReposRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[53] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return n } -func (m *RemoveRepoReply) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.Ok { - n += 2 - } - l = len(m.NewDefaultRepo) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n +func (x *RefreshReposRequest) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *RefreshReposRequest) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.Index != 0 { - n += 1 + sovO2Control(uint64(m.Index)) - } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) +func (*RefreshReposRequest) ProtoMessage() {} + +func (x *RefreshReposRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[53] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms } - return n + return mi.MessageOf(x) } -func (m *RefreshReposReply) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.ErrorString) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n -} - -func (m *SetDefaultRepoRequest) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.Index != 0 { - n += 1 + sovO2Control(uint64(m.Index)) - } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n +// Deprecated: Use RefreshReposRequest.ProtoReflect.Descriptor instead. +func (*RefreshReposRequest) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{53} } -func (m *SetDefaultRepoReply) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.ErrorString) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) +func (x *RefreshReposRequest) GetIndex() int32 { + if x != nil { + return x.Index } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n + return 0 } -func sovO2Control(x uint64) (n int) { - for { - n++ - x >>= 7 - if x == 0 { - break - } - } - return n -} -func sozO2Control(x uint64) (n int) { - return sovO2Control(uint64((x << 1) ^ uint64((int64(x) >> 63)))) -} -func (m *Event_MesosHeartbeat) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: Event_MesosHeartbeat: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: Event_MesosHeartbeat: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } +type SetDefaultRepoRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil + Index int32 `protobuf:"varint,1,opt,name=index,proto3" json:"index,omitempty"` } -func (m *StatusRequest) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: StatusRequest: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: StatusRequest: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - if iNdEx > l { - return io.ErrUnexpectedEOF +func (x *SetDefaultRepoRequest) Reset() { + *x = SetDefaultRepoRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[54] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return nil } -func (m *StatusReply) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: StatusReply: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: StatusReply: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field State", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.State = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field StatusUpdates", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.StatusUpdates = append(m.StatusUpdates, &StatusUpdate{}) - if err := m.StatusUpdates[len(m.StatusUpdates)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil +func (x *SetDefaultRepoRequest) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *StatusUpdate) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: StatusUpdate: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: StatusUpdate: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Level", wireType) - } - m.Level = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Level |= StatusUpdate_Level(b&0x7F) << shift - if b < 0x80 { - break - } - } - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field MesosHeartbeat", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - v := &Event_MesosHeartbeat{} - if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - m.Event = &StatusUpdate_MesosHeartbeat{v} - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *GetFrameworkInfoRequest) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: GetFrameworkInfoRequest: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: GetFrameworkInfoRequest: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } +func (*SetDefaultRepoRequest) ProtoMessage() {} - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *Version) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: Version: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: Version: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Major", wireType) - } - m.Major = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Major |= int32(b&0x7F) << shift - if b < 0x80 { - break - } - } - case 2: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Minor", wireType) - } - m.Minor = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Minor |= int32(b&0x7F) << shift - if b < 0x80 { - break - } - } - case 3: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Patch", wireType) - } - m.Patch = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Patch |= int32(b&0x7F) << shift - if b < 0x80 { - break - } - } - case 4: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Build", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Build = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 5: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ProductName", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.ProductName = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 6: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field VersionStr", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.VersionStr = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy +func (x *SetDefaultRepoRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[54] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) } + return ms } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil + return mi.MessageOf(x) } -func (m *GetFrameworkInfoReply) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: GetFrameworkInfoReply: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: GetFrameworkInfoReply: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field FrameworkId", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.FrameworkId = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 2: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field EnvironmentsCount", wireType) - } - m.EnvironmentsCount = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.EnvironmentsCount |= int32(b&0x7F) << shift - if b < 0x80 { - break - } - } - case 3: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field TasksCount", wireType) - } - m.TasksCount = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.TasksCount |= int32(b&0x7F) << shift - if b < 0x80 { - break - } - } - case 4: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field State", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.State = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 5: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field HostsCount", wireType) - } - m.HostsCount = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.HostsCount |= int32(b&0x7F) << shift - if b < 0x80 { - break - } - } - case 6: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field InstanceName", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.InstanceName = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 7: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Version", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.Version == nil { - m.Version = &Version{} - } - if err := m.Version.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil +// Deprecated: Use SetDefaultRepoRequest.ProtoReflect.Descriptor instead. +func (*SetDefaultRepoRequest) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{54} } -func (m *TeardownRequest) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: TeardownRequest: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: TeardownRequest: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Reason", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Reason = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - if iNdEx > l { - return io.ErrUnexpectedEOF +func (x *SetDefaultRepoRequest) GetIndex() int32 { + if x != nil { + return x.Index } - return nil + return 0 } -func (m *TeardownReply) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: TeardownReply: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: TeardownReply: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *GetEnvironmentsRequest) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: GetEnvironmentsRequest: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: GetEnvironmentsRequest: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } +type SetGlobalDefaultRevisionRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil + Revision string `protobuf:"bytes,1,opt,name=revision,proto3" json:"revision,omitempty"` } -func (m *GetEnvironmentsReply) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: GetEnvironmentsReply: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: GetEnvironmentsReply: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field FrameworkId", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.FrameworkId = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Environments", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Environments = append(m.Environments, &EnvironmentInfo{}) - if err := m.Environments[len(m.Environments)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - if iNdEx > l { - return io.ErrUnexpectedEOF +func (x *SetGlobalDefaultRevisionRequest) Reset() { + *x = SetGlobalDefaultRevisionRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[55] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return nil } -func (m *EnvironmentInfo) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: EnvironmentInfo: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: EnvironmentInfo: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Id = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field CreatedWhen", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.CreatedWhen = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 3: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field State", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.State = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 4: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Tasks", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Tasks = append(m.Tasks, &ShortTaskInfo{}) - if err := m.Tasks[len(m.Tasks)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 5: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field RootRole", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.RootRole = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 6: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field CurrentRunNumber", wireType) - } - m.CurrentRunNumber = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.CurrentRunNumber |= uint32(b&0x7F) << shift - if b < 0x80 { - break - } - } - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *NewEnvironmentRequest) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: NewEnvironmentRequest: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: NewEnvironmentRequest: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field WorkflowTemplate", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.WorkflowTemplate = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *NewEnvironmentReply) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: NewEnvironmentReply: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: NewEnvironmentReply: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Environment", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.Environment == nil { - m.Environment = &EnvironmentInfo{} - } - if err := m.Environment.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *GetEnvironmentRequest) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: GetEnvironmentRequest: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: GetEnvironmentRequest: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Id = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *GetEnvironmentReply) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: GetEnvironmentReply: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: GetEnvironmentReply: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Environment", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.Environment == nil { - m.Environment = &EnvironmentInfo{} - } - if err := m.Environment.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Workflow", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.Workflow == nil { - m.Workflow = &RoleInfo{} - } - if err := m.Workflow.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *ControlEnvironmentRequest) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: ControlEnvironmentRequest: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: ControlEnvironmentRequest: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Id = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 2: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) - } - m.Type = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Type |= ControlEnvironmentRequest_Optype(b&0x7F) << shift - if b < 0x80 { - break - } - } - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *ControlEnvironmentReply) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: ControlEnvironmentReply: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: ControlEnvironmentReply: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Id = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field State", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.State = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 3: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field CurrentRunNumber", wireType) - } - m.CurrentRunNumber = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.CurrentRunNumber |= uint32(b&0x7F) << shift - if b < 0x80 { - break - } - } - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *ModifyEnvironmentRequest) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: ModifyEnvironmentRequest: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: ModifyEnvironmentRequest: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Id = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Operations", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Operations = append(m.Operations, &EnvironmentOperation{}) - if err := m.Operations[len(m.Operations)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 3: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field ReconfigureAll", wireType) - } - var v int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - v |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - m.ReconfigureAll = bool(v != 0) - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *EnvironmentOperation) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: EnvironmentOperation: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: EnvironmentOperation: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) - } - m.Type = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Type |= EnvironmentOperation_Optype(b&0x7F) << shift - if b < 0x80 { - break - } - } - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field RoleName", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.RoleName = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *ModifyEnvironmentReply) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: ModifyEnvironmentReply: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: ModifyEnvironmentReply: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field FailedOperations", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.FailedOperations = append(m.FailedOperations, &EnvironmentOperation{}) - if err := m.FailedOperations[len(m.FailedOperations)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Id = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 3: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field State", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.State = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *DestroyEnvironmentRequest) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: DestroyEnvironmentRequest: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: DestroyEnvironmentRequest: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Id = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 2: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field KeepTasks", wireType) - } - var v int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - v |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - m.KeepTasks = bool(v != 0) - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *DestroyEnvironmentReply) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: DestroyEnvironmentReply: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: DestroyEnvironmentReply: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field CleanupTasksReply", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.CleanupTasksReply == nil { - m.CleanupTasksReply = &CleanupTasksReply{} - } - if err := m.CleanupTasksReply.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *ShortTaskInfo) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: ShortTaskInfo: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: ShortTaskInfo: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Name = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 2: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Locked", wireType) - } - var v int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - v |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - m.Locked = bool(v != 0) - case 3: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field TaskId", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.TaskId = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 4: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Status", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Status = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 5: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field State", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.State = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 6: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ClassName", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.ClassName = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 7: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field DeploymentInfo", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.DeploymentInfo == nil { - m.DeploymentInfo = &TaskDeploymentInfo{} - } - if err := m.DeploymentInfo.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *TaskDeploymentInfo) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: TaskDeploymentInfo: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: TaskDeploymentInfo: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Hostname", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Hostname = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field AgentId", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.AgentId = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 3: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field OfferId", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.OfferId = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 4: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ExecutorId", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.ExecutorId = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *GetTasksRequest) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: GetTasksRequest: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: GetTasksRequest: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *GetTasksReply) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: GetTasksReply: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: GetTasksReply: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Tasks", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Tasks = append(m.Tasks, &ShortTaskInfo{}) - if err := m.Tasks[len(m.Tasks)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *GetTaskRequest) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: GetTaskRequest: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: GetTaskRequest: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field TaskId", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.TaskId = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *GetTaskReply) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: GetTaskReply: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: GetTaskReply: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Task", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.Task == nil { - m.Task = &TaskInfo{} - } - if err := m.Task.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *TaskClassInfo) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: TaskClassInfo: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: TaskClassInfo: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Name = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ControlMode", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.ControlMode = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *CommandInfo) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: CommandInfo: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: CommandInfo: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Env", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Env = append(m.Env, string(dAtA[iNdEx:postIndex])) - iNdEx = postIndex - case 2: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Shell", wireType) - } - var v int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - v |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - m.Shell = bool(v != 0) - case 3: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Value", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Value = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 4: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Arguments", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Arguments = append(m.Arguments, string(dAtA[iNdEx:postIndex])) - iNdEx = postIndex - case 5: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field User", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.User = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *ChannelInfo) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: ChannelInfo: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: ChannelInfo: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Name = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Type = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 3: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Target", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Target = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *TaskInfo) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: TaskInfo: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: TaskInfo: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ShortInfo", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.ShortInfo == nil { - m.ShortInfo = &ShortTaskInfo{} - } - if err := m.ShortInfo.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ClassInfo", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.ClassInfo == nil { - m.ClassInfo = &TaskClassInfo{} - } - if err := m.ClassInfo.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 3: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field InboundChannels", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.InboundChannels = append(m.InboundChannels, &ChannelInfo{}) - if err := m.InboundChannels[len(m.InboundChannels)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 4: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field OutboundChannels", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.OutboundChannels = append(m.OutboundChannels, &ChannelInfo{}) - if err := m.OutboundChannels[len(m.OutboundChannels)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 5: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field CommandInfo", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.CommandInfo == nil { - m.CommandInfo = &CommandInfo{} - } - if err := m.CommandInfo.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 6: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field TaskPath", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.TaskPath = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 7: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field EnvId", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.EnvId = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *CleanupTasksRequest) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: CleanupTasksRequest: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: CleanupTasksRequest: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field TaskIds", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.TaskIds = append(m.TaskIds, string(dAtA[iNdEx:postIndex])) - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *CleanupTasksReply) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: CleanupTasksReply: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: CleanupTasksReply: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field KilledTasks", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.KilledTasks = append(m.KilledTasks, &ShortTaskInfo{}) - if err := m.KilledTasks[len(m.KilledTasks)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field RunningTasks", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.RunningTasks = append(m.RunningTasks, &ShortTaskInfo{}) - if err := m.RunningTasks[len(m.RunningTasks)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *GetRolesRequest) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: GetRolesRequest: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: GetRolesRequest: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field EnvId", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.EnvId = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field PathSpec", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.PathSpec = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *RoleInfo) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: RoleInfo: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: RoleInfo: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Name = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Status", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Status = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 3: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field State", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.State = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 4: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field FullPath", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.FullPath = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 5: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field TaskIds", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.TaskIds = append(m.TaskIds, string(dAtA[iNdEx:postIndex])) - iNdEx = postIndex - case 6: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Roles", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Roles = append(m.Roles, &RoleInfo{}) - if err := m.Roles[len(m.Roles)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *GetRolesReply) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: GetRolesReply: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: GetRolesReply: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Roles", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Roles = append(m.Roles, &RoleInfo{}) - if err := m.Roles[len(m.Roles)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *GetWorkflowTemplatesRequest) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: GetWorkflowTemplatesRequest: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: GetWorkflowTemplatesRequest: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *WorkflowTemplateInfo) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: WorkflowTemplateInfo: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: WorkflowTemplateInfo: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Repo", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Repo = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Template", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Template = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *GetWorkflowTemplatesReply) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: GetWorkflowTemplatesReply: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: GetWorkflowTemplatesReply: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field WorkflowTemplates", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.WorkflowTemplates = append(m.WorkflowTemplates, &WorkflowTemplateInfo{}) - if err := m.WorkflowTemplates[len(m.WorkflowTemplates)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *ListReposRequest) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: ListReposRequest: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: ListReposRequest: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy + +func (x *SetGlobalDefaultRevisionRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SetGlobalDefaultRevisionRequest) ProtoMessage() {} + +func (x *SetGlobalDefaultRevisionRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[55] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) } + return ms } + return mi.MessageOf(x) +} - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil +// Deprecated: Use SetGlobalDefaultRevisionRequest.ProtoReflect.Descriptor instead. +func (*SetGlobalDefaultRevisionRequest) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{55} } -func (m *RepoInfo) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: RepoInfo: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: RepoInfo: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Name = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 2: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Default", wireType) - } - var v int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - v |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - m.Default = bool(v != 0) - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } + +func (x *SetGlobalDefaultRevisionRequest) GetRevision() string { + if x != nil { + return x.Revision } + return "" +} - if iNdEx > l { - return io.ErrUnexpectedEOF +type SetRepoDefaultRevisionRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Index int32 `protobuf:"varint,1,opt,name=index,proto3" json:"index,omitempty"` + Revision string `protobuf:"bytes,2,opt,name=revision,proto3" json:"revision,omitempty"` +} + +func (x *SetRepoDefaultRevisionRequest) Reset() { + *x = SetRepoDefaultRevisionRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[56] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return nil } -func (m *ListReposReply) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: ListReposReply: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: ListReposReply: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Repos", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Repos = append(m.Repos, &RepoInfo{}) - if err := m.Repos[len(m.Repos)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy + +func (x *SetRepoDefaultRevisionRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SetRepoDefaultRevisionRequest) ProtoMessage() {} + +func (x *SetRepoDefaultRevisionRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[56] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) } + return ms } + return mi.MessageOf(x) +} + +// Deprecated: Use SetRepoDefaultRevisionRequest.ProtoReflect.Descriptor instead. +func (*SetRepoDefaultRevisionRequest) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{56} +} - if iNdEx > l { - return io.ErrUnexpectedEOF +func (x *SetRepoDefaultRevisionRequest) GetIndex() int32 { + if x != nil { + return x.Index } - return nil + return 0 } -func (m *AddRepoRequest) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: AddRepoRequest: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: AddRepoRequest: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Name = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } + +func (x *SetRepoDefaultRevisionRequest) GetRevision() string { + if x != nil { + return x.Revision } + return "" +} - if iNdEx > l { - return io.ErrUnexpectedEOF +type SetRepoDefaultRevisionReply struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Info string `protobuf:"bytes,1,opt,name=info,proto3" json:"info,omitempty"` + Timestamp int64 `protobuf:"varint,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"` // timestamp of when this object was sent in unix milliseconds +} + +func (x *SetRepoDefaultRevisionReply) Reset() { + *x = SetRepoDefaultRevisionReply{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[57] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return nil } -func (m *AddRepoReply) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: AddRepoReply: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: AddRepoReply: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ErrorString", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.ErrorString = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy + +func (x *SetRepoDefaultRevisionReply) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SetRepoDefaultRevisionReply) ProtoMessage() {} + +func (x *SetRepoDefaultRevisionReply) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[57] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) } + return ms } + return mi.MessageOf(x) +} - if iNdEx > l { - return io.ErrUnexpectedEOF +// Deprecated: Use SetRepoDefaultRevisionReply.ProtoReflect.Descriptor instead. +func (*SetRepoDefaultRevisionReply) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{57} +} + +func (x *SetRepoDefaultRevisionReply) GetInfo() string { + if x != nil { + return x.Info } - return nil + return "" } -func (m *RemoveRepoRequest) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: RemoveRepoRequest: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: RemoveRepoRequest: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Index", wireType) - } - m.Index = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Index |= int32(b&0x7F) << shift - if b < 0x80 { - break - } - } - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } + +func (x *SetRepoDefaultRevisionReply) GetTimestamp() int64 { + if x != nil { + return x.Timestamp } + return 0 +} - if iNdEx > l { - return io.ErrUnexpectedEOF +type Empty struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *Empty) Reset() { + *x = Empty{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[58] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return nil } -func (m *RemoveRepoReply) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: RemoveRepoReply: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: RemoveRepoReply: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Ok", wireType) - } - var v int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - v |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - m.Ok = bool(v != 0) - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field NewDefaultRepo", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.NewDefaultRepo = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy + +func (x *Empty) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Empty) ProtoMessage() {} + +func (x *Empty) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[58] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) } + return ms } + return mi.MessageOf(x) +} + +// Deprecated: Use Empty.ProtoReflect.Descriptor instead. +func (*Empty) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{58} +} + +type ListIntegratedServicesReply struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields - if iNdEx > l { - return io.ErrUnexpectedEOF + Services map[string]*IntegratedServiceInfo `protobuf:"bytes,1,rep,name=services,proto3" json:"services,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` // keys are IDs (e.g. "ddsched"), the service name should be displayed to users instead + Timestamp int64 `protobuf:"varint,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"` // timestamp of when this object was sent in unix milliseconds +} + +func (x *ListIntegratedServicesReply) Reset() { + *x = ListIntegratedServicesReply{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[59] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return nil } -func (m *RefreshReposRequest) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: RefreshReposRequest: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: RefreshReposRequest: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Index", wireType) - } - m.Index = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Index |= int32(b&0x7F) << shift - if b < 0x80 { - break - } - } - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy + +func (x *ListIntegratedServicesReply) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListIntegratedServicesReply) ProtoMessage() {} + +func (x *ListIntegratedServicesReply) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[59] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) } + return ms } + return mi.MessageOf(x) +} - if iNdEx > l { - return io.ErrUnexpectedEOF +// Deprecated: Use ListIntegratedServicesReply.ProtoReflect.Descriptor instead. +func (*ListIntegratedServicesReply) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{59} +} + +func (x *ListIntegratedServicesReply) GetServices() map[string]*IntegratedServiceInfo { + if x != nil { + return x.Services } return nil } -func (m *RefreshReposReply) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: RefreshReposReply: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: RefreshReposReply: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ErrorString", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.ErrorString = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } + +func (x *ListIntegratedServicesReply) GetTimestamp() int64 { + if x != nil { + return x.Timestamp } + return 0 +} + +type IntegratedServiceInfo struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` // user-visible service name, e.g. "DD scheduler" + Enabled bool `protobuf:"varint,2,opt,name=enabled,proto3" json:"enabled,omitempty"` + Endpoint string `protobuf:"bytes,3,opt,name=endpoint,proto3" json:"endpoint,omitempty"` + ConnectionState string `protobuf:"bytes,4,opt,name=connectionState,proto3" json:"connectionState,omitempty"` // allowed values: READY, CONNECTING, TRANSIENT_FAILURE, IDLE, SHUTDOWN + Data string `protobuf:"bytes,5,opt,name=data,proto3" json:"data,omitempty"` // always a JSON payload with a map inside. +} - if iNdEx > l { - return io.ErrUnexpectedEOF +func (x *IntegratedServiceInfo) Reset() { + *x = IntegratedServiceInfo{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[60] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return nil } -func (m *SetDefaultRepoRequest) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: SetDefaultRepoRequest: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: SetDefaultRepoRequest: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Index", wireType) - } - m.Index = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Index |= int32(b&0x7F) << shift - if b < 0x80 { - break - } - } - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy + +func (x *IntegratedServiceInfo) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*IntegratedServiceInfo) ProtoMessage() {} + +func (x *IntegratedServiceInfo) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[60] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) } + return ms } + return mi.MessageOf(x) +} + +// Deprecated: Use IntegratedServiceInfo.ProtoReflect.Descriptor instead. +func (*IntegratedServiceInfo) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{60} +} - if iNdEx > l { - return io.ErrUnexpectedEOF +func (x *IntegratedServiceInfo) GetName() string { + if x != nil { + return x.Name } - return nil + return "" } -func (m *SetDefaultRepoReply) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: SetDefaultRepoReply: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: SetDefaultRepoReply: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ErrorString", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.ErrorString = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } + +func (x *IntegratedServiceInfo) GetEnabled() bool { + if x != nil { + return x.Enabled } + return false +} - if iNdEx > l { - return io.ErrUnexpectedEOF +func (x *IntegratedServiceInfo) GetEndpoint() string { + if x != nil { + return x.Endpoint } - return nil + return "" } -func skipO2Control(dAtA []byte) (n int, err error) { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowO2Control - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - wireType := int(wire & 0x7) - switch wireType { - case 0: - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowO2Control - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - iNdEx++ - if dAtA[iNdEx-1] < 0x80 { - break - } - } - return iNdEx, nil - case 1: - iNdEx += 8 - return iNdEx, nil - case 2: - var length int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowO2Control - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - length |= (int(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - if length < 0 { - return 0, ErrInvalidLengthO2Control - } - iNdEx += length - if iNdEx < 0 { - return 0, ErrInvalidLengthO2Control - } - return iNdEx, nil - case 3: - for { - var innerWire uint64 - var start int = iNdEx - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowO2Control - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - innerWire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - innerWireType := int(innerWire & 0x7) - if innerWireType == 4 { - break - } - next, err := skipO2Control(dAtA[start:]) - if err != nil { - return 0, err - } - iNdEx = start + next - if iNdEx < 0 { - return 0, ErrInvalidLengthO2Control - } - } - return iNdEx, nil - case 4: - return iNdEx, nil - case 5: - iNdEx += 4 - return iNdEx, nil - default: - return 0, fmt.Errorf("proto: illegal wireType %d", wireType) - } + +func (x *IntegratedServiceInfo) GetConnectionState() string { + if x != nil { + return x.ConnectionState + } + return "" +} + +func (x *IntegratedServiceInfo) GetData() string { + if x != nil { + return x.Data } - panic("unreachable") + return "" +} + +var File_protos_o2control_proto protoreflect.FileDescriptor + +var file_protos_o2control_proto_rawDesc = []byte{ + 0x0a, 0x16, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, + 0x6f, 0x6c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x09, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, + 0x72, 0x6f, 0x6c, 0x1a, 0x13, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x65, 0x76, 0x65, 0x6e, + 0x74, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x22, 0x0a, 0x10, 0x53, 0x75, 0x62, 0x73, + 0x63, 0x72, 0x69, 0x62, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x19, 0x0a, 0x17, + 0x47, 0x65, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x6e, 0x66, 0x6f, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xa3, 0x01, 0x0a, 0x07, 0x56, 0x65, 0x72, 0x73, + 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x05, 0x52, 0x05, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x6d, 0x69, 0x6e, + 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x6d, 0x69, 0x6e, 0x6f, 0x72, 0x12, + 0x14, 0x0a, 0x05, 0x70, 0x61, 0x74, 0x63, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, + 0x70, 0x61, 0x74, 0x63, 0x68, 0x12, 0x14, 0x0a, 0x05, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x12, 0x20, 0x0a, 0x0b, 0x70, + 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, + 0x0a, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0a, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x72, 0x22, 0xef, 0x03, + 0x0a, 0x15, 0x47, 0x65, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x6e, + 0x66, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x20, 0x0a, 0x0b, 0x66, 0x72, 0x61, 0x6d, 0x65, + 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x66, 0x72, + 0x61, 0x6d, 0x65, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x64, 0x12, 0x2c, 0x0a, 0x11, 0x65, 0x6e, 0x76, + 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x05, 0x52, 0x11, 0x65, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, + 0x74, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x74, 0x61, 0x73, 0x6b, 0x73, + 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x74, 0x61, 0x73, + 0x6b, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1e, 0x0a, + 0x0a, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x0a, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x22, 0x0a, + 0x0c, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x06, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0c, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x4e, 0x61, 0x6d, + 0x65, 0x12, 0x2c, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x56, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, + 0x34, 0x0a, 0x15, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x15, + 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x6e, 0x64, + 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x30, 0x0a, 0x13, 0x64, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, + 0x72, 0x73, 0x49, 0x6e, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x09, 0x20, 0x03, + 0x28, 0x09, 0x52, 0x13, 0x64, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x49, 0x6e, 0x49, + 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x28, 0x0a, 0x0f, 0x61, 0x63, 0x74, 0x69, 0x76, + 0x65, 0x44, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x09, + 0x52, 0x0f, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x44, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, + 0x73, 0x12, 0x2e, 0x0a, 0x12, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x44, 0x65, + 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x09, 0x52, 0x12, 0x61, + 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x44, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, + 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x0c, + 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, + 0x29, 0x0a, 0x0f, 0x54, 0x65, 0x61, 0x72, 0x64, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x22, 0x0f, 0x0a, 0x0d, 0x54, 0x65, + 0x61, 0x72, 0x64, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0xa0, 0x01, 0x0a, 0x16, + 0x47, 0x65, 0x74, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x68, 0x6f, 0x77, 0x41, 0x6c, + 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, 0x68, 0x6f, 0x77, 0x41, 0x6c, 0x6c, + 0x12, 0x24, 0x0a, 0x0d, 0x73, 0x68, 0x6f, 0x77, 0x54, 0x61, 0x73, 0x6b, 0x49, 0x6e, 0x66, 0x6f, + 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x73, 0x68, 0x6f, 0x77, 0x54, 0x61, 0x73, + 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x73, 0x12, 0x46, 0x0a, 0x1e, 0x73, 0x68, 0x6f, 0x77, 0x44, 0x65, + 0x74, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x65, 0x64, + 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1e, + 0x73, 0x68, 0x6f, 0x77, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x49, 0x6e, 0x74, 0x65, + 0x67, 0x72, 0x61, 0x74, 0x65, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x22, 0x96, + 0x01, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, + 0x74, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x20, 0x0a, 0x0b, 0x66, 0x72, 0x61, 0x6d, 0x65, + 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x66, 0x72, + 0x61, 0x6d, 0x65, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x64, 0x12, 0x3e, 0x0a, 0x0c, 0x65, 0x6e, 0x76, + 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x1a, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x45, 0x6e, 0x76, 0x69, + 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0c, 0x65, 0x6e, 0x76, + 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0xdb, 0x08, 0x0a, 0x0f, 0x45, 0x6e, 0x76, 0x69, + 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x0e, 0x0a, 0x02, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x20, 0x0a, 0x0b, 0x63, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x57, 0x68, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x57, 0x68, 0x65, 0x6e, 0x12, 0x14, 0x0a, + 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, + 0x61, 0x74, 0x65, 0x12, 0x2e, 0x0a, 0x05, 0x74, 0x61, 0x73, 0x6b, 0x73, 0x18, 0x04, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x53, + 0x68, 0x6f, 0x72, 0x74, 0x54, 0x61, 0x73, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x05, 0x74, 0x61, + 0x73, 0x6b, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x6f, 0x6f, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x6f, 0x6f, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x12, + 0x2a, 0x0a, 0x10, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x52, 0x75, 0x6e, 0x4e, 0x75, 0x6d, + 0x62, 0x65, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x10, 0x63, 0x75, 0x72, 0x72, 0x65, + 0x6e, 0x74, 0x52, 0x75, 0x6e, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x44, 0x0a, 0x08, 0x64, + 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28, 0x2e, + 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, + 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, + 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, + 0x73, 0x12, 0x38, 0x0a, 0x04, 0x76, 0x61, 0x72, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x24, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x45, 0x6e, 0x76, 0x69, + 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x56, 0x61, 0x72, 0x73, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x04, 0x76, 0x61, 0x72, 0x73, 0x12, 0x44, 0x0a, 0x08, 0x75, + 0x73, 0x65, 0x72, 0x56, 0x61, 0x72, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28, 0x2e, + 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, + 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x56, 0x61, + 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x56, 0x61, 0x72, + 0x73, 0x12, 0x22, 0x0a, 0x0c, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x4f, 0x66, 0x46, 0x6c, 0x70, + 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x4f, + 0x66, 0x46, 0x6c, 0x70, 0x73, 0x12, 0x2c, 0x0a, 0x11, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, + 0x64, 0x44, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x09, + 0x52, 0x11, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x64, 0x44, 0x65, 0x74, 0x65, 0x63, 0x74, + 0x6f, 0x72, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x24, 0x0a, 0x0d, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x4f, + 0x66, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, 0x6e, 0x75, + 0x6d, 0x62, 0x65, 0x72, 0x4f, 0x66, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x12, 0x6e, 0x0a, 0x16, 0x69, + 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x65, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x73, 0x44, 0x61, 0x74, 0x61, 0x18, 0x0e, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x36, 0x2e, 0x6f, 0x32, + 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, + 0x65, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, + 0x65, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x44, 0x61, 0x74, 0x61, 0x45, 0x6e, + 0x74, 0x72, 0x79, 0x52, 0x16, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x65, 0x64, 0x53, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x44, 0x61, 0x74, 0x61, 0x12, 0x24, 0x0a, 0x0d, 0x6e, + 0x75, 0x6d, 0x62, 0x65, 0x72, 0x4f, 0x66, 0x54, 0x61, 0x73, 0x6b, 0x73, 0x18, 0x0f, 0x20, 0x01, + 0x28, 0x05, 0x52, 0x0d, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x4f, 0x66, 0x54, 0x61, 0x73, 0x6b, + 0x73, 0x12, 0x2c, 0x0a, 0x11, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x54, 0x72, 0x61, 0x6e, + 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x10, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x63, 0x75, + 0x72, 0x72, 0x65, 0x6e, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x30, 0x0a, 0x13, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x4f, 0x66, 0x41, 0x63, 0x74, 0x69, 0x76, + 0x65, 0x54, 0x61, 0x73, 0x6b, 0x73, 0x18, 0x11, 0x20, 0x01, 0x28, 0x05, 0x52, 0x13, 0x6e, 0x75, + 0x6d, 0x62, 0x65, 0x72, 0x4f, 0x66, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x54, 0x61, 0x73, 0x6b, + 0x73, 0x12, 0x34, 0x0a, 0x15, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x4f, 0x66, 0x49, 0x6e, 0x61, + 0x63, 0x74, 0x69, 0x76, 0x65, 0x54, 0x61, 0x73, 0x6b, 0x73, 0x18, 0x12, 0x20, 0x01, 0x28, 0x05, + 0x52, 0x15, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x4f, 0x66, 0x49, 0x6e, 0x61, 0x63, 0x74, 0x69, + 0x76, 0x65, 0x54, 0x61, 0x73, 0x6b, 0x73, 0x1a, 0x3b, 0x0a, 0x0d, 0x44, 0x65, 0x66, 0x61, 0x75, + 0x6c, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x37, 0x0a, 0x09, 0x56, 0x61, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x3b, 0x0a, + 0x0d, 0x55, 0x73, 0x65, 0x72, 0x56, 0x61, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, + 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, + 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x49, 0x0a, 0x1b, 0x49, 0x6e, + 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x65, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, + 0x44, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xac, 0x02, 0x0a, 0x15, 0x4e, 0x65, 0x77, 0x45, 0x6e, 0x76, + 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x2a, 0x0a, 0x10, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x54, 0x65, 0x6d, 0x70, 0x6c, + 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x77, 0x6f, 0x72, 0x6b, 0x66, + 0x6c, 0x6f, 0x77, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x12, 0x3e, 0x0a, 0x04, 0x76, + 0x61, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x6f, 0x32, 0x63, 0x6f, + 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x4e, 0x65, 0x77, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, + 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x56, 0x61, 0x72, 0x73, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x04, 0x76, 0x61, 0x72, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x70, + 0x75, 0x62, 0x6c, 0x69, 0x63, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x70, 0x75, 0x62, + 0x6c, 0x69, 0x63, 0x12, 0x26, 0x0a, 0x0e, 0x61, 0x75, 0x74, 0x6f, 0x54, 0x72, 0x61, 0x6e, 0x73, + 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x61, 0x75, 0x74, + 0x6f, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2e, 0x0a, 0x0b, 0x72, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x55, 0x73, 0x65, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x0c, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x0b, + 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x55, 0x73, 0x65, 0x72, 0x1a, 0x37, 0x0a, 0x09, 0x56, + 0x61, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x3a, 0x02, 0x38, 0x01, 0x22, 0x89, 0x01, 0x0a, 0x13, 0x4e, 0x65, 0x77, 0x45, 0x6e, 0x76, 0x69, + 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x3c, 0x0a, 0x0b, + 0x65, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1a, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x45, 0x6e, + 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0b, 0x65, + 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x75, + 0x62, 0x6c, 0x69, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x70, 0x75, 0x62, 0x6c, + 0x69, 0x63, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, + 0x22, 0x84, 0x02, 0x0a, 0x19, 0x4e, 0x65, 0x77, 0x41, 0x75, 0x74, 0x6f, 0x45, 0x6e, 0x76, 0x69, + 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2a, + 0x0a, 0x10, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, + 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, + 0x6f, 0x77, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x12, 0x42, 0x0a, 0x04, 0x76, 0x61, + 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, + 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x4e, 0x65, 0x77, 0x41, 0x75, 0x74, 0x6f, 0x45, 0x6e, 0x76, 0x69, + 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x56, + 0x61, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x04, 0x76, 0x61, 0x72, 0x73, 0x12, 0x0e, + 0x0a, 0x02, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x2e, + 0x0a, 0x0b, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x55, 0x73, 0x65, 0x72, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x55, 0x73, 0x65, + 0x72, 0x52, 0x0b, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x55, 0x73, 0x65, 0x72, 0x1a, 0x37, + 0x0a, 0x09, 0x56, 0x61, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, + 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x37, 0x0a, 0x17, 0x4e, 0x65, 0x77, 0x41, 0x75, + 0x74, 0x6f, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x70, + 0x6c, 0x79, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, + 0x22, 0x53, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, + 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x2a, 0x0a, 0x10, 0x73, 0x68, 0x6f, + 0x77, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x54, 0x72, 0x65, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x10, 0x73, 0x68, 0x6f, 0x77, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, + 0x77, 0x54, 0x72, 0x65, 0x65, 0x22, 0xba, 0x01, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x45, 0x6e, 0x76, + 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x3c, 0x0a, + 0x0b, 0x65, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x45, + 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0b, + 0x65, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x2f, 0x0a, 0x08, 0x77, + 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, + 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x52, 0x6f, 0x6c, 0x65, 0x49, 0x6e, + 0x66, 0x6f, 0x52, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x12, 0x16, 0x0a, 0x06, + 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x70, 0x75, + 0x62, 0x6c, 0x69, 0x63, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, + 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, + 0x6d, 0x70, 0x22, 0x8b, 0x02, 0x0a, 0x19, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x45, 0x6e, + 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, + 0x12, 0x3f, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2b, + 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x72, + 0x6f, 0x6c, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x2e, 0x4f, 0x70, 0x74, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, + 0x65, 0x12, 0x2e, 0x0a, 0x0b, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x55, 0x73, 0x65, 0x72, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, + 0x55, 0x73, 0x65, 0x72, 0x52, 0x0b, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x55, 0x73, 0x65, + 0x72, 0x22, 0x6d, 0x0a, 0x06, 0x4f, 0x70, 0x74, 0x79, 0x70, 0x65, 0x12, 0x08, 0x0a, 0x04, 0x4e, + 0x4f, 0x4f, 0x50, 0x10, 0x00, 0x12, 0x12, 0x0a, 0x0e, 0x53, 0x54, 0x41, 0x52, 0x54, 0x5f, 0x41, + 0x43, 0x54, 0x49, 0x56, 0x49, 0x54, 0x59, 0x10, 0x01, 0x12, 0x11, 0x0a, 0x0d, 0x53, 0x54, 0x4f, + 0x50, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x49, 0x54, 0x59, 0x10, 0x02, 0x12, 0x0d, 0x0a, 0x09, + 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x55, 0x52, 0x45, 0x10, 0x03, 0x12, 0x09, 0x0a, 0x05, 0x52, + 0x45, 0x53, 0x45, 0x54, 0x10, 0x04, 0x12, 0x0c, 0x0a, 0x08, 0x47, 0x4f, 0x5f, 0x45, 0x52, 0x52, + 0x4f, 0x52, 0x10, 0x05, 0x12, 0x0a, 0x0a, 0x06, 0x44, 0x45, 0x50, 0x4c, 0x4f, 0x59, 0x10, 0x06, + 0x22, 0x91, 0x02, 0x0a, 0x17, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x45, 0x6e, 0x76, 0x69, + 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x0e, 0x0a, 0x02, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x14, 0x0a, 0x05, + 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, + 0x74, 0x65, 0x12, 0x2a, 0x0a, 0x10, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x52, 0x75, 0x6e, + 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x10, 0x63, 0x75, + 0x72, 0x72, 0x65, 0x6e, 0x74, 0x52, 0x75, 0x6e, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x2c, + 0x0a, 0x11, 0x73, 0x74, 0x61, 0x72, 0x74, 0x4f, 0x66, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, + 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x11, 0x73, 0x74, 0x61, 0x72, 0x74, + 0x4f, 0x66, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x28, 0x0a, 0x0f, + 0x65, 0x6e, 0x64, 0x4f, 0x66, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x65, 0x6e, 0x64, 0x4f, 0x66, 0x54, 0x72, 0x61, 0x6e, + 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2e, 0x0a, 0x12, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x69, + 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x12, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x75, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, + 0x74, 0x61, 0x6d, 0x70, 0x22, 0x93, 0x01, 0x0a, 0x18, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x45, + 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, + 0x64, 0x12, 0x3f, 0x0a, 0x0a, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, + 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, + 0x6c, 0x2e, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x4f, 0x70, 0x65, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x12, 0x26, 0x0a, 0x0e, 0x72, 0x65, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, + 0x65, 0x41, 0x6c, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x72, 0x65, 0x63, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x65, 0x41, 0x6c, 0x6c, 0x22, 0xa1, 0x01, 0x0a, 0x14, 0x45, + 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x12, 0x3a, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0e, 0x32, 0x26, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x45, 0x6e, + 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x2e, 0x4f, 0x70, 0x74, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, + 0x1a, 0x0a, 0x08, 0x72, 0x6f, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x08, 0x72, 0x6f, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x31, 0x0a, 0x06, 0x4f, + 0x70, 0x74, 0x79, 0x70, 0x65, 0x12, 0x08, 0x0a, 0x04, 0x4e, 0x4f, 0x4f, 0x50, 0x10, 0x00, 0x12, + 0x0f, 0x0a, 0x0b, 0x52, 0x45, 0x4d, 0x4f, 0x56, 0x45, 0x5f, 0x52, 0x4f, 0x4c, 0x45, 0x10, 0x03, + 0x12, 0x0c, 0x0a, 0x08, 0x41, 0x44, 0x44, 0x5f, 0x52, 0x4f, 0x4c, 0x45, 0x10, 0x04, 0x22, 0x8b, + 0x01, 0x0a, 0x16, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, + 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x4b, 0x0a, 0x10, 0x66, 0x61, 0x69, + 0x6c, 0x65, 0x64, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, + 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x4f, 0x70, 0x65, 0x72, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x10, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x4f, 0x70, 0x65, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0xc1, 0x01, 0x0a, + 0x19, 0x44, 0x65, 0x73, 0x74, 0x72, 0x6f, 0x79, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, + 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x6b, 0x65, + 0x65, 0x70, 0x54, 0x61, 0x73, 0x6b, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x6b, + 0x65, 0x65, 0x70, 0x54, 0x61, 0x73, 0x6b, 0x73, 0x12, 0x30, 0x0a, 0x13, 0x61, 0x6c, 0x6c, 0x6f, + 0x77, 0x49, 0x6e, 0x52, 0x75, 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x65, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x49, 0x6e, 0x52, 0x75, + 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6f, + 0x72, 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, + 0x12, 0x2e, 0x0a, 0x0b, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x55, 0x73, 0x65, 0x72, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x55, + 0x73, 0x65, 0x72, 0x52, 0x0b, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x55, 0x73, 0x65, 0x72, + 0x22, 0x83, 0x01, 0x0a, 0x17, 0x44, 0x65, 0x73, 0x74, 0x72, 0x6f, 0x79, 0x45, 0x6e, 0x76, 0x69, + 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x4a, 0x0a, 0x11, + 0x63, 0x6c, 0x65, 0x61, 0x6e, 0x75, 0x70, 0x54, 0x61, 0x73, 0x6b, 0x73, 0x52, 0x65, 0x70, 0x6c, + 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, + 0x72, 0x6f, 0x6c, 0x2e, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x75, 0x70, 0x54, 0x61, 0x73, 0x6b, 0x73, + 0x52, 0x65, 0x70, 0x6c, 0x79, 0x52, 0x11, 0x63, 0x6c, 0x65, 0x61, 0x6e, 0x75, 0x70, 0x54, 0x61, + 0x73, 0x6b, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x55, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x41, 0x63, 0x74, + 0x69, 0x76, 0x65, 0x44, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x52, 0x65, 0x70, 0x6c, + 0x79, 0x12, 0x1c, 0x0a, 0x09, 0x64, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x64, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x12, + 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x58, 0x0a, + 0x1a, 0x47, 0x65, 0x74, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x44, 0x65, 0x74, + 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x1c, 0x0a, 0x09, 0x64, + 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, + 0x64, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0xcc, 0x01, 0x0a, 0x1f, 0x53, 0x65, 0x74, 0x45, + 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, + 0x74, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x5a, 0x0a, 0x0a, 0x70, + 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x3a, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x53, 0x65, 0x74, 0x45, + 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, + 0x74, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x50, 0x72, 0x6f, 0x70, + 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x70, 0x72, 0x6f, + 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x1a, 0x3d, 0x0a, 0x0f, 0x50, 0x72, 0x6f, 0x70, 0x65, + 0x72, 0x74, 0x69, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, + 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x1f, 0x0a, 0x1d, 0x53, 0x65, 0x74, 0x45, 0x6e, 0x76, + 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, + 0x65, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x73, 0x0a, 0x1f, 0x47, 0x65, 0x74, 0x45, 0x6e, + 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, + 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x71, 0x75, + 0x65, 0x72, 0x69, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x71, 0x75, 0x65, + 0x72, 0x69, 0x65, 0x73, 0x12, 0x26, 0x0a, 0x0e, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x47, + 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x65, 0x78, + 0x63, 0x6c, 0x75, 0x64, 0x65, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x73, 0x22, 0xb8, 0x01, 0x0a, + 0x1d, 0x47, 0x65, 0x74, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x50, + 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x58, + 0x0a, 0x0a, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x38, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x47, + 0x65, 0x74, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x50, 0x72, 0x6f, + 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x2e, 0x50, 0x72, 0x6f, + 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x70, 0x72, + 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x1a, 0x3d, 0x0a, 0x0f, 0x50, 0x72, 0x6f, 0x70, + 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, + 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xd8, 0x02, 0x0a, 0x0d, 0x53, 0x68, 0x6f, 0x72, + 0x74, 0x54, 0x61, 0x73, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, + 0x06, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x6c, + 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x74, 0x61, 0x73, 0x6b, 0x49, 0x64, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x61, 0x73, 0x6b, 0x49, 0x64, 0x12, 0x16, 0x0a, + 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x63, + 0x6c, 0x61, 0x73, 0x73, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, + 0x63, 0x6c, 0x61, 0x73, 0x73, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x45, 0x0a, 0x0e, 0x64, 0x65, 0x70, + 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x18, 0x07, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1d, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x54, 0x61, + 0x73, 0x6b, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, + 0x52, 0x0e, 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, + 0x12, 0x10, 0x0a, 0x03, 0x70, 0x69, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x70, + 0x69, 0x64, 0x12, 0x24, 0x0a, 0x0d, 0x73, 0x61, 0x6e, 0x64, 0x62, 0x6f, 0x78, 0x53, 0x74, 0x64, + 0x6f, 0x75, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x73, 0x61, 0x6e, 0x64, 0x62, + 0x6f, 0x78, 0x53, 0x74, 0x64, 0x6f, 0x75, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6c, 0x61, 0x69, + 0x6d, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x63, 0x6c, 0x61, + 0x69, 0x6d, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x72, 0x69, 0x74, 0x69, 0x63, + 0x61, 0x6c, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x63, 0x72, 0x69, 0x74, 0x69, 0x63, + 0x61, 0x6c, 0x22, 0x84, 0x01, 0x0a, 0x12, 0x54, 0x61, 0x73, 0x6b, 0x44, 0x65, 0x70, 0x6c, 0x6f, + 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1a, 0x0a, 0x08, 0x68, 0x6f, 0x73, + 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x68, 0x6f, 0x73, + 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x49, 0x64, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, + 0x18, 0x0a, 0x07, 0x6f, 0x66, 0x66, 0x65, 0x72, 0x49, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x07, 0x6f, 0x66, 0x66, 0x65, 0x72, 0x49, 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x65, 0x78, 0x65, + 0x63, 0x75, 0x74, 0x6f, 0x72, 0x49, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x65, + 0x78, 0x65, 0x63, 0x75, 0x74, 0x6f, 0x72, 0x49, 0x64, 0x22, 0x11, 0x0a, 0x0f, 0x47, 0x65, 0x74, + 0x54, 0x61, 0x73, 0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x5d, 0x0a, 0x0d, + 0x47, 0x65, 0x74, 0x54, 0x61, 0x73, 0x6b, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x2e, 0x0a, + 0x05, 0x74, 0x61, 0x73, 0x6b, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6f, + 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x53, 0x68, 0x6f, 0x72, 0x74, 0x54, 0x61, + 0x73, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x05, 0x74, 0x61, 0x73, 0x6b, 0x73, 0x12, 0x1c, 0x0a, + 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x28, 0x0a, 0x0e, 0x47, + 0x65, 0x74, 0x54, 0x61, 0x73, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, + 0x06, 0x74, 0x61, 0x73, 0x6b, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, + 0x61, 0x73, 0x6b, 0x49, 0x64, 0x22, 0x55, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x54, 0x61, 0x73, 0x6b, + 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x27, 0x0a, 0x04, 0x74, 0x61, 0x73, 0x6b, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, + 0x54, 0x61, 0x73, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x04, 0x74, 0x61, 0x73, 0x6b, 0x12, 0x1c, + 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x7d, 0x0a, 0x0b, + 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x10, 0x0a, 0x03, 0x65, + 0x6e, 0x76, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x03, 0x65, 0x6e, 0x76, 0x12, 0x14, 0x0a, + 0x05, 0x73, 0x68, 0x65, 0x6c, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x73, 0x68, + 0x65, 0x6c, 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x72, 0x67, + 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x61, 0x72, + 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x22, 0x4d, 0x0a, 0x0b, 0x43, + 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, + 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, + 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x22, 0xbe, 0x03, 0x0a, 0x08, 0x54, + 0x61, 0x73, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x36, 0x0a, 0x09, 0x73, 0x68, 0x6f, 0x72, 0x74, + 0x49, 0x6e, 0x66, 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6f, 0x32, 0x63, + 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x53, 0x68, 0x6f, 0x72, 0x74, 0x54, 0x61, 0x73, 0x6b, + 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x09, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, + 0x40, 0x0a, 0x0f, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, + 0x6c, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, + 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x49, 0x6e, 0x66, 0x6f, + 0x52, 0x0f, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, + 0x73, 0x12, 0x42, 0x0a, 0x10, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x43, 0x68, 0x61, + 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6f, 0x32, + 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x49, + 0x6e, 0x66, 0x6f, 0x52, 0x10, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x43, 0x68, 0x61, + 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x12, 0x38, 0x0a, 0x0b, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, + 0x49, 0x6e, 0x66, 0x6f, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6f, 0x32, 0x63, + 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x49, 0x6e, + 0x66, 0x6f, 0x52, 0x0b, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x49, 0x6e, 0x66, 0x6f, 0x12, + 0x1a, 0x0a, 0x08, 0x74, 0x61, 0x73, 0x6b, 0x50, 0x61, 0x74, 0x68, 0x18, 0x06, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x08, 0x74, 0x61, 0x73, 0x6b, 0x50, 0x61, 0x74, 0x68, 0x12, 0x14, 0x0a, 0x05, 0x65, + 0x6e, 0x76, 0x49, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x6e, 0x76, 0x49, + 0x64, 0x12, 0x43, 0x0a, 0x0a, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, + 0x09, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, + 0x6c, 0x2e, 0x54, 0x61, 0x73, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x50, 0x72, 0x6f, 0x70, 0x65, + 0x72, 0x74, 0x69, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x70, + 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x1a, 0x3d, 0x0a, 0x0f, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, + 0x74, 0x69, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x3a, 0x02, 0x38, 0x01, 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x22, 0x2f, 0x0a, 0x13, 0x43, + 0x6c, 0x65, 0x61, 0x6e, 0x75, 0x70, 0x54, 0x61, 0x73, 0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x74, 0x61, 0x73, 0x6b, 0x49, 0x64, 0x73, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x09, 0x52, 0x07, 0x74, 0x61, 0x73, 0x6b, 0x49, 0x64, 0x73, 0x22, 0xab, 0x01, 0x0a, + 0x11, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x75, 0x70, 0x54, 0x61, 0x73, 0x6b, 0x73, 0x52, 0x65, 0x70, + 0x6c, 0x79, 0x12, 0x3a, 0x0a, 0x0b, 0x6b, 0x69, 0x6c, 0x6c, 0x65, 0x64, 0x54, 0x61, 0x73, 0x6b, + 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, + 0x72, 0x6f, 0x6c, 0x2e, 0x53, 0x68, 0x6f, 0x72, 0x74, 0x54, 0x61, 0x73, 0x6b, 0x49, 0x6e, 0x66, + 0x6f, 0x52, 0x0b, 0x6b, 0x69, 0x6c, 0x6c, 0x65, 0x64, 0x54, 0x61, 0x73, 0x6b, 0x73, 0x12, 0x3c, + 0x0a, 0x0c, 0x72, 0x75, 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x54, 0x61, 0x73, 0x6b, 0x73, 0x18, 0x02, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, + 0x2e, 0x53, 0x68, 0x6f, 0x72, 0x74, 0x54, 0x61, 0x73, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0c, + 0x72, 0x75, 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x54, 0x61, 0x73, 0x6b, 0x73, 0x12, 0x1c, 0x0a, 0x09, + 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x43, 0x0a, 0x0f, 0x47, 0x65, + 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, + 0x05, 0x65, 0x6e, 0x76, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x6e, + 0x76, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x74, 0x68, 0x53, 0x70, 0x65, 0x63, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x74, 0x68, 0x53, 0x70, 0x65, 0x63, 0x22, + 0xd3, 0x05, 0x0a, 0x08, 0x52, 0x6f, 0x6c, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x12, 0x0a, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, + 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, + 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1a, + 0x0a, 0x08, 0x66, 0x75, 0x6c, 0x6c, 0x50, 0x61, 0x74, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x08, 0x66, 0x75, 0x6c, 0x6c, 0x50, 0x61, 0x74, 0x68, 0x12, 0x18, 0x0a, 0x07, 0x74, 0x61, + 0x73, 0x6b, 0x49, 0x64, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x74, 0x61, 0x73, + 0x6b, 0x49, 0x64, 0x73, 0x12, 0x29, 0x0a, 0x05, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x18, 0x06, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, + 0x52, 0x6f, 0x6c, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x05, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x12, + 0x3d, 0x0a, 0x08, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x21, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x52, 0x6f, + 0x6c, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x45, + 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x12, 0x31, + 0x0a, 0x04, 0x76, 0x61, 0x72, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6f, + 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x52, 0x6f, 0x6c, 0x65, 0x49, 0x6e, 0x66, + 0x6f, 0x2e, 0x56, 0x61, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x04, 0x76, 0x61, 0x72, + 0x73, 0x12, 0x3d, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x56, 0x61, 0x72, 0x73, 0x18, 0x09, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, + 0x52, 0x6f, 0x6c, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x56, 0x61, 0x72, + 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x56, 0x61, 0x72, 0x73, + 0x12, 0x58, 0x0a, 0x11, 0x63, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x64, + 0x53, 0x74, 0x61, 0x63, 0x6b, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x6f, 0x32, + 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x52, 0x6f, 0x6c, 0x65, 0x49, 0x6e, 0x66, 0x6f, + 0x2e, 0x43, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x64, 0x53, 0x74, 0x61, + 0x63, 0x6b, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x11, 0x63, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x69, + 0x64, 0x61, 0x74, 0x65, 0x64, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, + 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x3b, 0x0a, 0x0d, + 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, + 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, + 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x37, 0x0a, 0x09, 0x56, 0x61, 0x72, + 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, + 0x38, 0x01, 0x1a, 0x3b, 0x0a, 0x0d, 0x55, 0x73, 0x65, 0x72, 0x56, 0x61, 0x72, 0x73, 0x45, 0x6e, + 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, + 0x44, 0x0a, 0x16, 0x43, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x64, 0x53, + 0x74, 0x61, 0x63, 0x6b, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x58, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x6c, 0x65, + 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x29, 0x0a, 0x05, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x18, + 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, + 0x6c, 0x2e, 0x52, 0x6f, 0x6c, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x05, 0x72, 0x6f, 0x6c, 0x65, + 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, + 0xc9, 0x01, 0x0a, 0x1b, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x54, + 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x20, 0x0a, 0x0b, 0x72, 0x65, 0x70, 0x6f, 0x50, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x72, 0x65, 0x70, 0x6f, 0x50, 0x61, 0x74, 0x74, 0x65, 0x72, + 0x6e, 0x12, 0x28, 0x0a, 0x0f, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x74, + 0x74, 0x65, 0x72, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x72, 0x65, 0x76, 0x69, + 0x73, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x12, 0x20, 0x0a, 0x0b, 0x61, + 0x6c, 0x6c, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x0b, 0x61, 0x6c, 0x6c, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x65, 0x73, 0x12, 0x18, 0x0a, + 0x07, 0x61, 0x6c, 0x6c, 0x54, 0x61, 0x67, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, + 0x61, 0x6c, 0x6c, 0x54, 0x61, 0x67, 0x73, 0x12, 0x22, 0x0a, 0x0c, 0x61, 0x6c, 0x6c, 0x57, 0x6f, + 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x61, + 0x6c, 0x6c, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x73, 0x22, 0xae, 0x04, 0x0a, 0x0e, + 0x56, 0x61, 0x72, 0x53, 0x70, 0x65, 0x63, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x22, + 0x0a, 0x0c, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x56, 0x61, 0x6c, + 0x75, 0x65, 0x12, 0x32, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, + 0x32, 0x1e, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x56, 0x61, 0x72, + 0x53, 0x70, 0x65, 0x63, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x54, 0x79, 0x70, 0x65, + 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x12, 0x20, 0x0a, 0x0b, + 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3a, + 0x0a, 0x06, 0x77, 0x69, 0x64, 0x67, 0x65, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x22, + 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x56, 0x61, 0x72, 0x53, 0x70, + 0x65, 0x63, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x55, 0x69, 0x57, 0x69, 0x64, 0x67, + 0x65, 0x74, 0x52, 0x06, 0x77, 0x69, 0x64, 0x67, 0x65, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x61, + 0x6e, 0x65, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x70, 0x61, 0x6e, 0x65, 0x6c, + 0x12, 0x24, 0x0a, 0x0d, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x56, 0x61, 0x6c, 0x75, 0x65, + 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, + 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, + 0x08, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x1c, 0x0a, 0x09, + 0x76, 0x69, 0x73, 0x69, 0x62, 0x6c, 0x65, 0x49, 0x66, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x09, 0x76, 0x69, 0x73, 0x69, 0x62, 0x6c, 0x65, 0x49, 0x66, 0x12, 0x1c, 0x0a, 0x09, 0x65, 0x6e, + 0x61, 0x62, 0x6c, 0x65, 0x64, 0x49, 0x66, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x65, + 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x49, 0x66, 0x12, 0x12, 0x0a, 0x04, 0x72, 0x6f, 0x77, 0x73, + 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x72, 0x6f, 0x77, 0x73, 0x22, 0x71, 0x0a, 0x08, + 0x55, 0x69, 0x57, 0x69, 0x64, 0x67, 0x65, 0x74, 0x12, 0x0b, 0x0a, 0x07, 0x65, 0x64, 0x69, 0x74, + 0x42, 0x6f, 0x78, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x73, 0x6c, 0x69, 0x64, 0x65, 0x72, 0x10, + 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x6c, 0x69, 0x73, 0x74, 0x42, 0x6f, 0x78, 0x10, 0x02, 0x12, 0x0f, + 0x0a, 0x0b, 0x64, 0x72, 0x6f, 0x70, 0x44, 0x6f, 0x77, 0x6e, 0x42, 0x6f, 0x78, 0x10, 0x03, 0x12, + 0x0c, 0x0a, 0x08, 0x63, 0x6f, 0x6d, 0x62, 0x6f, 0x42, 0x6f, 0x78, 0x10, 0x04, 0x12, 0x12, 0x0a, + 0x0e, 0x72, 0x61, 0x64, 0x69, 0x6f, 0x42, 0x75, 0x74, 0x74, 0x6f, 0x6e, 0x42, 0x6f, 0x78, 0x10, + 0x05, 0x12, 0x0c, 0x0a, 0x08, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x42, 0x6f, 0x78, 0x10, 0x06, 0x22, + 0x3b, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0a, 0x0a, 0x06, 0x73, 0x74, 0x72, 0x69, 0x6e, + 0x67, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x10, 0x01, 0x12, + 0x08, 0x0a, 0x04, 0x62, 0x6f, 0x6f, 0x6c, 0x10, 0x02, 0x12, 0x08, 0x0a, 0x04, 0x6c, 0x69, 0x73, + 0x74, 0x10, 0x03, 0x12, 0x07, 0x0a, 0x03, 0x6d, 0x61, 0x70, 0x10, 0x04, 0x22, 0xaf, 0x02, 0x0a, + 0x14, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, + 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x72, 0x65, 0x70, 0x6f, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x72, 0x65, 0x70, 0x6f, 0x12, 0x1a, 0x0a, 0x08, 0x74, 0x65, 0x6d, + 0x70, 0x6c, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x65, 0x6d, + 0x70, 0x6c, 0x61, 0x74, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, + 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, + 0x6e, 0x12, 0x4f, 0x0a, 0x0a, 0x76, 0x61, 0x72, 0x53, 0x70, 0x65, 0x63, 0x4d, 0x61, 0x70, 0x18, + 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, + 0x6c, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, + 0x74, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x56, 0x61, 0x72, 0x53, 0x70, 0x65, 0x63, 0x4d, 0x61, + 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x76, 0x61, 0x72, 0x53, 0x70, 0x65, 0x63, 0x4d, + 0x61, 0x70, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, + 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x58, 0x0a, 0x0f, 0x56, 0x61, 0x72, 0x53, 0x70, 0x65, 0x63, 0x4d, + 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2f, 0x0a, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, + 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x56, 0x61, 0x72, 0x53, 0x70, 0x65, 0x63, 0x4d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x88, + 0x01, 0x0a, 0x19, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x54, 0x65, + 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x4d, 0x0a, 0x11, + 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, + 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, + 0x72, 0x6f, 0x6c, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x54, 0x65, 0x6d, 0x70, + 0x6c, 0x61, 0x74, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x11, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, + 0x6f, 0x77, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x74, + 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, + 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x36, 0x0a, 0x10, 0x4c, 0x69, 0x73, + 0x74, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x22, 0x0a, + 0x0c, 0x67, 0x65, 0x74, 0x52, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x0c, 0x67, 0x65, 0x74, 0x52, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, + 0x73, 0x22, 0x80, 0x01, 0x0a, 0x08, 0x52, 0x65, 0x70, 0x6f, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x12, + 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x07, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x12, 0x28, 0x0a, 0x0f, + 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x52, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x52, 0x65, + 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, + 0x6f, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x76, 0x69, 0x73, + 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x8f, 0x01, 0x0a, 0x0e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x70, + 0x6f, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x29, 0x0a, 0x05, 0x72, 0x65, 0x70, 0x6f, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, + 0x6f, 0x6c, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x05, 0x72, 0x65, 0x70, + 0x6f, 0x73, 0x12, 0x34, 0x0a, 0x15, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x44, 0x65, 0x66, 0x61, + 0x75, 0x6c, 0x74, 0x52, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x15, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, + 0x52, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x4e, 0x0a, 0x0e, 0x41, 0x64, 0x64, 0x52, 0x65, 0x70, + 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x28, 0x0a, 0x0f, + 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x52, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x52, 0x65, + 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x70, 0x0a, 0x0c, 0x41, 0x64, 0x64, 0x52, 0x65, 0x70, + 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x2e, 0x0a, 0x12, 0x6e, 0x65, 0x77, 0x44, 0x65, 0x66, + 0x61, 0x75, 0x6c, 0x74, 0x52, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x12, 0x6e, 0x65, 0x77, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x52, 0x65, + 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, + 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x29, 0x0a, 0x11, 0x52, 0x65, 0x6d, 0x6f, + 0x76, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, + 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x69, 0x6e, + 0x64, 0x65, 0x78, 0x22, 0x57, 0x0a, 0x0f, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x65, 0x70, + 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x26, 0x0a, 0x0e, 0x6e, 0x65, 0x77, 0x44, 0x65, 0x66, + 0x61, 0x75, 0x6c, 0x74, 0x52, 0x65, 0x70, 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, + 0x6e, 0x65, 0x77, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x52, 0x65, 0x70, 0x6f, 0x12, 0x1c, + 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x2b, 0x0a, 0x13, + 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x05, 0x52, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x22, 0x2d, 0x0a, 0x15, 0x53, 0x65, 0x74, + 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x52, 0x65, 0x70, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x22, 0x3d, 0x0a, 0x1f, 0x53, 0x65, 0x74, 0x47, + 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x52, 0x65, 0x76, 0x69, + 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x72, + 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, + 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x51, 0x0a, 0x1d, 0x53, 0x65, 0x74, 0x52, 0x65, + 0x70, 0x6f, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x52, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, + 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6e, 0x64, 0x65, + 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x1a, + 0x0a, 0x08, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x08, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x4f, 0x0a, 0x1b, 0x53, 0x65, + 0x74, 0x52, 0x65, 0x70, 0x6f, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x52, 0x65, 0x76, 0x69, + 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x6e, 0x66, + 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x1c, 0x0a, + 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x07, 0x0a, 0x05, 0x45, + 0x6d, 0x70, 0x74, 0x79, 0x22, 0xec, 0x01, 0x0a, 0x1b, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x6e, 0x74, + 0x65, 0x67, 0x72, 0x61, 0x74, 0x65, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, + 0x65, 0x70, 0x6c, 0x79, 0x12, 0x50, 0x0a, 0x08, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x34, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, + 0x6f, 0x6c, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x65, + 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x2e, 0x53, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x73, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, + 0x74, 0x61, 0x6d, 0x70, 0x1a, 0x5d, 0x0a, 0x0d, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x36, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, + 0x6f, 0x6c, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x65, 0x64, 0x53, 0x65, 0x72, + 0x76, 0x69, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, + 0x02, 0x38, 0x01, 0x22, 0x9f, 0x01, 0x0a, 0x15, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, + 0x65, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x12, 0x0a, + 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x65, + 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, + 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x28, 0x0a, 0x0f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x04, 0x64, 0x61, 0x74, 0x61, 0x32, 0xeb, 0x10, 0x0a, 0x07, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, + 0x6c, 0x12, 0x5a, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x77, 0x6f, 0x72, + 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x22, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, + 0x6c, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x6e, + 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6f, 0x32, 0x63, 0x6f, + 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x77, 0x6f, + 0x72, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x57, 0x0a, + 0x0f, 0x47, 0x65, 0x74, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x73, + 0x12, 0x21, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x47, 0x65, 0x74, + 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, + 0x47, 0x65, 0x74, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, + 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x60, 0x0a, 0x12, 0x4e, 0x65, 0x77, 0x41, 0x75, 0x74, + 0x6f, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x24, 0x2e, 0x6f, + 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x4e, 0x65, 0x77, 0x41, 0x75, 0x74, 0x6f, + 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x4e, + 0x65, 0x77, 0x41, 0x75, 0x74, 0x6f, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, + 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x54, 0x0a, 0x0e, 0x4e, 0x65, 0x77, 0x45, + 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x20, 0x2e, 0x6f, 0x32, 0x63, + 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x4e, 0x65, 0x77, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, + 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x6f, + 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x4e, 0x65, 0x77, 0x45, 0x6e, 0x76, 0x69, + 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x54, + 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, + 0x12, 0x20, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x47, 0x65, 0x74, + 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x47, + 0x65, 0x74, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x70, + 0x6c, 0x79, 0x22, 0x00, 0x12, 0x60, 0x0a, 0x12, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x45, + 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x24, 0x2e, 0x6f, 0x32, 0x63, + 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x45, 0x6e, + 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x22, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x43, 0x6f, 0x6e, + 0x74, 0x72, 0x6f, 0x6c, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x52, + 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x60, 0x0a, 0x12, 0x44, 0x65, 0x73, 0x74, 0x72, 0x6f, + 0x79, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x24, 0x2e, 0x6f, + 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x44, 0x65, 0x73, 0x74, 0x72, 0x6f, 0x79, + 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x44, + 0x65, 0x73, 0x74, 0x72, 0x6f, 0x79, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, + 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x4c, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x41, + 0x63, 0x74, 0x69, 0x76, 0x65, 0x44, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x12, 0x10, + 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, + 0x1a, 0x22, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x47, 0x65, 0x74, + 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x44, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x52, + 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x52, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x41, 0x76, 0x61, + 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x44, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x12, + 0x10, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x45, 0x6d, 0x70, 0x74, + 0x79, 0x1a, 0x25, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x47, 0x65, + 0x74, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x44, 0x65, 0x74, 0x65, 0x63, 0x74, + 0x6f, 0x72, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x59, 0x0a, 0x13, 0x4e, 0x65, + 0x77, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x41, 0x73, 0x79, 0x6e, + 0x63, 0x12, 0x20, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x4e, 0x65, + 0x77, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, + 0x4e, 0x65, 0x77, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, + 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x42, 0x0a, 0x08, 0x47, 0x65, 0x74, 0x54, 0x61, 0x73, 0x6b, + 0x73, 0x12, 0x1a, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x47, 0x65, + 0x74, 0x54, 0x61, 0x73, 0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, + 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x61, 0x73, + 0x6b, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x3f, 0x0a, 0x07, 0x47, 0x65, 0x74, + 0x54, 0x61, 0x73, 0x6b, 0x12, 0x19, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, + 0x2e, 0x47, 0x65, 0x74, 0x54, 0x61, 0x73, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x17, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x47, 0x65, 0x74, 0x54, + 0x61, 0x73, 0x6b, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x4e, 0x0a, 0x0c, 0x43, 0x6c, + 0x65, 0x61, 0x6e, 0x75, 0x70, 0x54, 0x61, 0x73, 0x6b, 0x73, 0x12, 0x1e, 0x2e, 0x6f, 0x32, 0x63, + 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x75, 0x70, 0x54, 0x61, + 0x73, 0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6f, 0x32, 0x63, + 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x75, 0x70, 0x54, 0x61, + 0x73, 0x6b, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x42, 0x0a, 0x08, 0x47, 0x65, + 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x12, 0x1a, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, + 0x6f, 0x6c, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x47, + 0x65, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x66, + 0x0a, 0x14, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x54, 0x65, 0x6d, + 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x12, 0x26, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, + 0x6f, 0x6c, 0x2e, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x54, 0x65, + 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, + 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x47, 0x65, 0x74, 0x57, 0x6f, + 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x52, + 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x45, 0x0a, 0x09, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, + 0x70, 0x6f, 0x73, 0x12, 0x1b, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, + 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x19, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x4c, 0x69, 0x73, + 0x74, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x3f, 0x0a, + 0x07, 0x41, 0x64, 0x64, 0x52, 0x65, 0x70, 0x6f, 0x12, 0x19, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, + 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x41, 0x64, 0x64, 0x52, 0x65, 0x70, 0x6f, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, + 0x41, 0x64, 0x64, 0x52, 0x65, 0x70, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x48, + 0x0a, 0x0a, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x12, 0x1c, 0x2e, 0x6f, + 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, + 0x65, 0x70, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6f, 0x32, 0x63, + 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x65, 0x70, + 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x42, 0x0a, 0x0c, 0x52, 0x65, 0x66, 0x72, + 0x65, 0x73, 0x68, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x12, 0x1e, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, + 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x52, 0x65, 0x70, 0x6f, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x10, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, + 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x46, 0x0a, 0x0e, + 0x53, 0x65, 0x74, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x52, 0x65, 0x70, 0x6f, 0x12, 0x20, + 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x53, 0x65, 0x74, 0x44, 0x65, + 0x66, 0x61, 0x75, 0x6c, 0x74, 0x52, 0x65, 0x70, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x10, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x45, 0x6d, 0x70, + 0x74, 0x79, 0x22, 0x00, 0x12, 0x5a, 0x0a, 0x18, 0x53, 0x65, 0x74, 0x47, 0x6c, 0x6f, 0x62, 0x61, + 0x6c, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x52, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, + 0x12, 0x2a, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x53, 0x65, 0x74, + 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x52, 0x65, 0x76, + 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x10, 0x2e, 0x6f, + 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, + 0x12, 0x6c, 0x0a, 0x16, 0x53, 0x65, 0x74, 0x52, 0x65, 0x70, 0x6f, 0x44, 0x65, 0x66, 0x61, 0x75, + 0x6c, 0x74, 0x52, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x28, 0x2e, 0x6f, 0x32, 0x63, + 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x53, 0x65, 0x74, 0x52, 0x65, 0x70, 0x6f, 0x44, 0x65, + 0x66, 0x61, 0x75, 0x6c, 0x74, 0x52, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, + 0x2e, 0x53, 0x65, 0x74, 0x52, 0x65, 0x70, 0x6f, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x52, + 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x3b, + 0x0a, 0x09, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x12, 0x1b, 0x2e, 0x6f, 0x32, + 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, + 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0d, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, + 0x73, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x22, 0x00, 0x30, 0x01, 0x12, 0x53, 0x0a, 0x15, 0x47, + 0x65, 0x74, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x65, 0x64, 0x53, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x73, 0x12, 0x10, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, + 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x26, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, + 0x6f, 0x6c, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x65, + 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, + 0x12, 0x42, 0x0a, 0x08, 0x54, 0x65, 0x61, 0x72, 0x64, 0x6f, 0x77, 0x6e, 0x12, 0x1a, 0x2e, 0x6f, + 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x54, 0x65, 0x61, 0x72, 0x64, 0x6f, 0x77, + 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, + 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x54, 0x65, 0x61, 0x72, 0x64, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x70, + 0x6c, 0x79, 0x22, 0x00, 0x12, 0x5d, 0x0a, 0x11, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x45, 0x6e, + 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x23, 0x2e, 0x6f, 0x32, 0x63, 0x6f, + 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x45, 0x6e, 0x76, 0x69, + 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, + 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x4d, 0x6f, 0x64, 0x69, 0x66, + 0x79, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x70, 0x6c, + 0x79, 0x22, 0x00, 0x42, 0x54, 0x0a, 0x22, 0x63, 0x68, 0x2e, 0x63, 0x65, 0x72, 0x6e, 0x2e, 0x61, + 0x6c, 0x69, 0x63, 0x65, 0x2e, 0x6f, 0x32, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, + 0x72, 0x70, 0x63, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5a, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, + 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x41, 0x6c, 0x69, 0x63, 0x65, 0x4f, 0x32, 0x47, 0x72, 0x6f, + 0x75, 0x70, 0x2f, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2f, 0x63, 0x6f, 0x72, 0x65, 0x2f, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x3b, 0x70, 0x62, 0x50, 0x00, 0x62, 0x06, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x33, } var ( - ErrInvalidLengthO2Control = fmt.Errorf("proto: negative length found during unmarshaling") - ErrIntOverflowO2Control = fmt.Errorf("proto: integer overflow") + file_protos_o2control_proto_rawDescOnce sync.Once + file_protos_o2control_proto_rawDescData = file_protos_o2control_proto_rawDesc ) + +func file_protos_o2control_proto_rawDescGZIP() []byte { + file_protos_o2control_proto_rawDescOnce.Do(func() { + file_protos_o2control_proto_rawDescData = protoimpl.X.CompressGZIP(file_protos_o2control_proto_rawDescData) + }) + return file_protos_o2control_proto_rawDescData +} + +var file_protos_o2control_proto_enumTypes = make([]protoimpl.EnumInfo, 4) +var file_protos_o2control_proto_msgTypes = make([]protoimpl.MessageInfo, 76) +var file_protos_o2control_proto_goTypes = []interface{}{ + (ControlEnvironmentRequest_Optype)(0), // 0: o2control.ControlEnvironmentRequest.Optype + (EnvironmentOperation_Optype)(0), // 1: o2control.EnvironmentOperation.Optype + (VarSpecMessage_UiWidget)(0), // 2: o2control.VarSpecMessage.UiWidget + (VarSpecMessage_Type)(0), // 3: o2control.VarSpecMessage.Type + (*SubscribeRequest)(nil), // 4: o2control.SubscribeRequest + (*GetFrameworkInfoRequest)(nil), // 5: o2control.GetFrameworkInfoRequest + (*Version)(nil), // 6: o2control.Version + (*GetFrameworkInfoReply)(nil), // 7: o2control.GetFrameworkInfoReply + (*TeardownRequest)(nil), // 8: o2control.TeardownRequest + (*TeardownReply)(nil), // 9: o2control.TeardownReply + (*GetEnvironmentsRequest)(nil), // 10: o2control.GetEnvironmentsRequest + (*GetEnvironmentsReply)(nil), // 11: o2control.GetEnvironmentsReply + (*EnvironmentInfo)(nil), // 12: o2control.EnvironmentInfo + (*NewEnvironmentRequest)(nil), // 13: o2control.NewEnvironmentRequest + (*NewEnvironmentReply)(nil), // 14: o2control.NewEnvironmentReply + (*NewAutoEnvironmentRequest)(nil), // 15: o2control.NewAutoEnvironmentRequest + (*NewAutoEnvironmentReply)(nil), // 16: o2control.NewAutoEnvironmentReply + (*GetEnvironmentRequest)(nil), // 17: o2control.GetEnvironmentRequest + (*GetEnvironmentReply)(nil), // 18: o2control.GetEnvironmentReply + (*ControlEnvironmentRequest)(nil), // 19: o2control.ControlEnvironmentRequest + (*ControlEnvironmentReply)(nil), // 20: o2control.ControlEnvironmentReply + (*ModifyEnvironmentRequest)(nil), // 21: o2control.ModifyEnvironmentRequest + (*EnvironmentOperation)(nil), // 22: o2control.EnvironmentOperation + (*ModifyEnvironmentReply)(nil), // 23: o2control.ModifyEnvironmentReply + (*DestroyEnvironmentRequest)(nil), // 24: o2control.DestroyEnvironmentRequest + (*DestroyEnvironmentReply)(nil), // 25: o2control.DestroyEnvironmentReply + (*GetActiveDetectorsReply)(nil), // 26: o2control.GetActiveDetectorsReply + (*GetAvailableDetectorsReply)(nil), // 27: o2control.GetAvailableDetectorsReply + (*SetEnvironmentPropertiesRequest)(nil), // 28: o2control.SetEnvironmentPropertiesRequest + (*SetEnvironmentPropertiesReply)(nil), // 29: o2control.SetEnvironmentPropertiesReply + (*GetEnvironmentPropertiesRequest)(nil), // 30: o2control.GetEnvironmentPropertiesRequest + (*GetEnvironmentPropertiesReply)(nil), // 31: o2control.GetEnvironmentPropertiesReply + (*ShortTaskInfo)(nil), // 32: o2control.ShortTaskInfo + (*TaskDeploymentInfo)(nil), // 33: o2control.TaskDeploymentInfo + (*GetTasksRequest)(nil), // 34: o2control.GetTasksRequest + (*GetTasksReply)(nil), // 35: o2control.GetTasksReply + (*GetTaskRequest)(nil), // 36: o2control.GetTaskRequest + (*GetTaskReply)(nil), // 37: o2control.GetTaskReply + (*CommandInfo)(nil), // 38: o2control.CommandInfo + (*ChannelInfo)(nil), // 39: o2control.ChannelInfo + (*TaskInfo)(nil), // 40: o2control.TaskInfo + (*CleanupTasksRequest)(nil), // 41: o2control.CleanupTasksRequest + (*CleanupTasksReply)(nil), // 42: o2control.CleanupTasksReply + (*GetRolesRequest)(nil), // 43: o2control.GetRolesRequest + (*RoleInfo)(nil), // 44: o2control.RoleInfo + (*GetRolesReply)(nil), // 45: o2control.GetRolesReply + (*GetWorkflowTemplatesRequest)(nil), // 46: o2control.GetWorkflowTemplatesRequest + (*VarSpecMessage)(nil), // 47: o2control.VarSpecMessage + (*WorkflowTemplateInfo)(nil), // 48: o2control.WorkflowTemplateInfo + (*GetWorkflowTemplatesReply)(nil), // 49: o2control.GetWorkflowTemplatesReply + (*ListReposRequest)(nil), // 50: o2control.ListReposRequest + (*RepoInfo)(nil), // 51: o2control.RepoInfo + (*ListReposReply)(nil), // 52: o2control.ListReposReply + (*AddRepoRequest)(nil), // 53: o2control.AddRepoRequest + (*AddRepoReply)(nil), // 54: o2control.AddRepoReply + (*RemoveRepoRequest)(nil), // 55: o2control.RemoveRepoRequest + (*RemoveRepoReply)(nil), // 56: o2control.RemoveRepoReply + (*RefreshReposRequest)(nil), // 57: o2control.RefreshReposRequest + (*SetDefaultRepoRequest)(nil), // 58: o2control.SetDefaultRepoRequest + (*SetGlobalDefaultRevisionRequest)(nil), // 59: o2control.SetGlobalDefaultRevisionRequest + (*SetRepoDefaultRevisionRequest)(nil), // 60: o2control.SetRepoDefaultRevisionRequest + (*SetRepoDefaultRevisionReply)(nil), // 61: o2control.SetRepoDefaultRevisionReply + (*Empty)(nil), // 62: o2control.Empty + (*ListIntegratedServicesReply)(nil), // 63: o2control.ListIntegratedServicesReply + (*IntegratedServiceInfo)(nil), // 64: o2control.IntegratedServiceInfo + nil, // 65: o2control.EnvironmentInfo.DefaultsEntry + nil, // 66: o2control.EnvironmentInfo.VarsEntry + nil, // 67: o2control.EnvironmentInfo.UserVarsEntry + nil, // 68: o2control.EnvironmentInfo.IntegratedServicesDataEntry + nil, // 69: o2control.NewEnvironmentRequest.VarsEntry + nil, // 70: o2control.NewAutoEnvironmentRequest.VarsEntry + nil, // 71: o2control.SetEnvironmentPropertiesRequest.PropertiesEntry + nil, // 72: o2control.GetEnvironmentPropertiesReply.PropertiesEntry + nil, // 73: o2control.TaskInfo.PropertiesEntry + nil, // 74: o2control.RoleInfo.DefaultsEntry + nil, // 75: o2control.RoleInfo.VarsEntry + nil, // 76: o2control.RoleInfo.UserVarsEntry + nil, // 77: o2control.RoleInfo.ConsolidatedStackEntry + nil, // 78: o2control.WorkflowTemplateInfo.VarSpecMapEntry + nil, // 79: o2control.ListIntegratedServicesReply.ServicesEntry + (*protos.User)(nil), // 80: common.User + (*protos.Event)(nil), // 81: events.Event +} +var file_protos_o2control_proto_depIdxs = []int32{ + 6, // 0: o2control.GetFrameworkInfoReply.version:type_name -> o2control.Version + 12, // 1: o2control.GetEnvironmentsReply.environments:type_name -> o2control.EnvironmentInfo + 32, // 2: o2control.EnvironmentInfo.tasks:type_name -> o2control.ShortTaskInfo + 65, // 3: o2control.EnvironmentInfo.defaults:type_name -> o2control.EnvironmentInfo.DefaultsEntry + 66, // 4: o2control.EnvironmentInfo.vars:type_name -> o2control.EnvironmentInfo.VarsEntry + 67, // 5: o2control.EnvironmentInfo.userVars:type_name -> o2control.EnvironmentInfo.UserVarsEntry + 68, // 6: o2control.EnvironmentInfo.integratedServicesData:type_name -> o2control.EnvironmentInfo.IntegratedServicesDataEntry + 69, // 7: o2control.NewEnvironmentRequest.vars:type_name -> o2control.NewEnvironmentRequest.VarsEntry + 80, // 8: o2control.NewEnvironmentRequest.requestUser:type_name -> common.User + 12, // 9: o2control.NewEnvironmentReply.environment:type_name -> o2control.EnvironmentInfo + 70, // 10: o2control.NewAutoEnvironmentRequest.vars:type_name -> o2control.NewAutoEnvironmentRequest.VarsEntry + 80, // 11: o2control.NewAutoEnvironmentRequest.requestUser:type_name -> common.User + 12, // 12: o2control.GetEnvironmentReply.environment:type_name -> o2control.EnvironmentInfo + 44, // 13: o2control.GetEnvironmentReply.workflow:type_name -> o2control.RoleInfo + 0, // 14: o2control.ControlEnvironmentRequest.type:type_name -> o2control.ControlEnvironmentRequest.Optype + 80, // 15: o2control.ControlEnvironmentRequest.requestUser:type_name -> common.User + 22, // 16: o2control.ModifyEnvironmentRequest.operations:type_name -> o2control.EnvironmentOperation + 1, // 17: o2control.EnvironmentOperation.type:type_name -> o2control.EnvironmentOperation.Optype + 22, // 18: o2control.ModifyEnvironmentReply.failedOperations:type_name -> o2control.EnvironmentOperation + 80, // 19: o2control.DestroyEnvironmentRequest.requestUser:type_name -> common.User + 42, // 20: o2control.DestroyEnvironmentReply.cleanupTasksReply:type_name -> o2control.CleanupTasksReply + 71, // 21: o2control.SetEnvironmentPropertiesRequest.properties:type_name -> o2control.SetEnvironmentPropertiesRequest.PropertiesEntry + 72, // 22: o2control.GetEnvironmentPropertiesReply.properties:type_name -> o2control.GetEnvironmentPropertiesReply.PropertiesEntry + 33, // 23: o2control.ShortTaskInfo.deploymentInfo:type_name -> o2control.TaskDeploymentInfo + 32, // 24: o2control.GetTasksReply.tasks:type_name -> o2control.ShortTaskInfo + 40, // 25: o2control.GetTaskReply.task:type_name -> o2control.TaskInfo + 32, // 26: o2control.TaskInfo.shortInfo:type_name -> o2control.ShortTaskInfo + 39, // 27: o2control.TaskInfo.inboundChannels:type_name -> o2control.ChannelInfo + 39, // 28: o2control.TaskInfo.outboundChannels:type_name -> o2control.ChannelInfo + 38, // 29: o2control.TaskInfo.commandInfo:type_name -> o2control.CommandInfo + 73, // 30: o2control.TaskInfo.properties:type_name -> o2control.TaskInfo.PropertiesEntry + 32, // 31: o2control.CleanupTasksReply.killedTasks:type_name -> o2control.ShortTaskInfo + 32, // 32: o2control.CleanupTasksReply.runningTasks:type_name -> o2control.ShortTaskInfo + 44, // 33: o2control.RoleInfo.roles:type_name -> o2control.RoleInfo + 74, // 34: o2control.RoleInfo.defaults:type_name -> o2control.RoleInfo.DefaultsEntry + 75, // 35: o2control.RoleInfo.vars:type_name -> o2control.RoleInfo.VarsEntry + 76, // 36: o2control.RoleInfo.userVars:type_name -> o2control.RoleInfo.UserVarsEntry + 77, // 37: o2control.RoleInfo.consolidatedStack:type_name -> o2control.RoleInfo.ConsolidatedStackEntry + 44, // 38: o2control.GetRolesReply.roles:type_name -> o2control.RoleInfo + 3, // 39: o2control.VarSpecMessage.type:type_name -> o2control.VarSpecMessage.Type + 2, // 40: o2control.VarSpecMessage.widget:type_name -> o2control.VarSpecMessage.UiWidget + 78, // 41: o2control.WorkflowTemplateInfo.varSpecMap:type_name -> o2control.WorkflowTemplateInfo.VarSpecMapEntry + 48, // 42: o2control.GetWorkflowTemplatesReply.workflowTemplates:type_name -> o2control.WorkflowTemplateInfo + 51, // 43: o2control.ListReposReply.repos:type_name -> o2control.RepoInfo + 79, // 44: o2control.ListIntegratedServicesReply.services:type_name -> o2control.ListIntegratedServicesReply.ServicesEntry + 47, // 45: o2control.WorkflowTemplateInfo.VarSpecMapEntry.value:type_name -> o2control.VarSpecMessage + 64, // 46: o2control.ListIntegratedServicesReply.ServicesEntry.value:type_name -> o2control.IntegratedServiceInfo + 5, // 47: o2control.Control.GetFrameworkInfo:input_type -> o2control.GetFrameworkInfoRequest + 10, // 48: o2control.Control.GetEnvironments:input_type -> o2control.GetEnvironmentsRequest + 15, // 49: o2control.Control.NewAutoEnvironment:input_type -> o2control.NewAutoEnvironmentRequest + 13, // 50: o2control.Control.NewEnvironment:input_type -> o2control.NewEnvironmentRequest + 17, // 51: o2control.Control.GetEnvironment:input_type -> o2control.GetEnvironmentRequest + 19, // 52: o2control.Control.ControlEnvironment:input_type -> o2control.ControlEnvironmentRequest + 24, // 53: o2control.Control.DestroyEnvironment:input_type -> o2control.DestroyEnvironmentRequest + 62, // 54: o2control.Control.GetActiveDetectors:input_type -> o2control.Empty + 62, // 55: o2control.Control.GetAvailableDetectors:input_type -> o2control.Empty + 13, // 56: o2control.Control.NewEnvironmentAsync:input_type -> o2control.NewEnvironmentRequest + 34, // 57: o2control.Control.GetTasks:input_type -> o2control.GetTasksRequest + 36, // 58: o2control.Control.GetTask:input_type -> o2control.GetTaskRequest + 41, // 59: o2control.Control.CleanupTasks:input_type -> o2control.CleanupTasksRequest + 43, // 60: o2control.Control.GetRoles:input_type -> o2control.GetRolesRequest + 46, // 61: o2control.Control.GetWorkflowTemplates:input_type -> o2control.GetWorkflowTemplatesRequest + 50, // 62: o2control.Control.ListRepos:input_type -> o2control.ListReposRequest + 53, // 63: o2control.Control.AddRepo:input_type -> o2control.AddRepoRequest + 55, // 64: o2control.Control.RemoveRepo:input_type -> o2control.RemoveRepoRequest + 57, // 65: o2control.Control.RefreshRepos:input_type -> o2control.RefreshReposRequest + 58, // 66: o2control.Control.SetDefaultRepo:input_type -> o2control.SetDefaultRepoRequest + 59, // 67: o2control.Control.SetGlobalDefaultRevision:input_type -> o2control.SetGlobalDefaultRevisionRequest + 60, // 68: o2control.Control.SetRepoDefaultRevision:input_type -> o2control.SetRepoDefaultRevisionRequest + 4, // 69: o2control.Control.Subscribe:input_type -> o2control.SubscribeRequest + 62, // 70: o2control.Control.GetIntegratedServices:input_type -> o2control.Empty + 8, // 71: o2control.Control.Teardown:input_type -> o2control.TeardownRequest + 21, // 72: o2control.Control.ModifyEnvironment:input_type -> o2control.ModifyEnvironmentRequest + 7, // 73: o2control.Control.GetFrameworkInfo:output_type -> o2control.GetFrameworkInfoReply + 11, // 74: o2control.Control.GetEnvironments:output_type -> o2control.GetEnvironmentsReply + 16, // 75: o2control.Control.NewAutoEnvironment:output_type -> o2control.NewAutoEnvironmentReply + 14, // 76: o2control.Control.NewEnvironment:output_type -> o2control.NewEnvironmentReply + 18, // 77: o2control.Control.GetEnvironment:output_type -> o2control.GetEnvironmentReply + 20, // 78: o2control.Control.ControlEnvironment:output_type -> o2control.ControlEnvironmentReply + 25, // 79: o2control.Control.DestroyEnvironment:output_type -> o2control.DestroyEnvironmentReply + 26, // 80: o2control.Control.GetActiveDetectors:output_type -> o2control.GetActiveDetectorsReply + 27, // 81: o2control.Control.GetAvailableDetectors:output_type -> o2control.GetAvailableDetectorsReply + 14, // 82: o2control.Control.NewEnvironmentAsync:output_type -> o2control.NewEnvironmentReply + 35, // 83: o2control.Control.GetTasks:output_type -> o2control.GetTasksReply + 37, // 84: o2control.Control.GetTask:output_type -> o2control.GetTaskReply + 42, // 85: o2control.Control.CleanupTasks:output_type -> o2control.CleanupTasksReply + 45, // 86: o2control.Control.GetRoles:output_type -> o2control.GetRolesReply + 49, // 87: o2control.Control.GetWorkflowTemplates:output_type -> o2control.GetWorkflowTemplatesReply + 52, // 88: o2control.Control.ListRepos:output_type -> o2control.ListReposReply + 54, // 89: o2control.Control.AddRepo:output_type -> o2control.AddRepoReply + 56, // 90: o2control.Control.RemoveRepo:output_type -> o2control.RemoveRepoReply + 62, // 91: o2control.Control.RefreshRepos:output_type -> o2control.Empty + 62, // 92: o2control.Control.SetDefaultRepo:output_type -> o2control.Empty + 62, // 93: o2control.Control.SetGlobalDefaultRevision:output_type -> o2control.Empty + 61, // 94: o2control.Control.SetRepoDefaultRevision:output_type -> o2control.SetRepoDefaultRevisionReply + 81, // 95: o2control.Control.Subscribe:output_type -> events.Event + 63, // 96: o2control.Control.GetIntegratedServices:output_type -> o2control.ListIntegratedServicesReply + 9, // 97: o2control.Control.Teardown:output_type -> o2control.TeardownReply + 23, // 98: o2control.Control.ModifyEnvironment:output_type -> o2control.ModifyEnvironmentReply + 73, // [73:99] is the sub-list for method output_type + 47, // [47:73] is the sub-list for method input_type + 47, // [47:47] is the sub-list for extension type_name + 47, // [47:47] is the sub-list for extension extendee + 0, // [0:47] is the sub-list for field type_name +} + +func init() { file_protos_o2control_proto_init() } +func file_protos_o2control_proto_init() { + if File_protos_o2control_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_protos_o2control_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SubscribeRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetFrameworkInfoRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Version); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetFrameworkInfoReply); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TeardownRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TeardownReply); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetEnvironmentsRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetEnvironmentsReply); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*EnvironmentInfo); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NewEnvironmentRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NewEnvironmentReply); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NewAutoEnvironmentRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NewAutoEnvironmentReply); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetEnvironmentRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetEnvironmentReply); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ControlEnvironmentRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ControlEnvironmentReply); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ModifyEnvironmentRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*EnvironmentOperation); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ModifyEnvironmentReply); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DestroyEnvironmentRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DestroyEnvironmentReply); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetActiveDetectorsReply); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetAvailableDetectorsReply); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SetEnvironmentPropertiesRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SetEnvironmentPropertiesReply); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetEnvironmentPropertiesRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetEnvironmentPropertiesReply); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[28].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ShortTaskInfo); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[29].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TaskDeploymentInfo); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[30].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetTasksRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[31].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetTasksReply); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[32].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetTaskRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[33].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetTaskReply); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[34].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CommandInfo); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[35].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ChannelInfo); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[36].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TaskInfo); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[37].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CleanupTasksRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[38].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CleanupTasksReply); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[39].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetRolesRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[40].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RoleInfo); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[41].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetRolesReply); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[42].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetWorkflowTemplatesRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[43].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*VarSpecMessage); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[44].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*WorkflowTemplateInfo); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[45].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetWorkflowTemplatesReply); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[46].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ListReposRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[47].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RepoInfo); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[48].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ListReposReply); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[49].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AddRepoRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[50].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AddRepoReply); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[51].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RemoveRepoRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[52].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RemoveRepoReply); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[53].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RefreshReposRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[54].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SetDefaultRepoRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[55].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SetGlobalDefaultRevisionRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[56].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SetRepoDefaultRevisionRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[57].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SetRepoDefaultRevisionReply); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[58].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Empty); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[59].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ListIntegratedServicesReply); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[60].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*IntegratedServiceInfo); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_protos_o2control_proto_rawDesc, + NumEnums: 4, + NumMessages: 76, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_protos_o2control_proto_goTypes, + DependencyIndexes: file_protos_o2control_proto_depIdxs, + EnumInfos: file_protos_o2control_proto_enumTypes, + MessageInfos: file_protos_o2control_proto_msgTypes, + }.Build() + File_protos_o2control_proto = out.File + file_protos_o2control_proto_rawDesc = nil + file_protos_o2control_proto_goTypes = nil + file_protos_o2control_proto_depIdxs = nil +} diff --git a/coconut/protos/o2control_grpc.pb.go b/coconut/protos/o2control_grpc.pb.go new file mode 100644 index 000000000..1208a45bc --- /dev/null +++ b/coconut/protos/o2control_grpc.pb.go @@ -0,0 +1,1100 @@ +// +// === This file is part of ALICE O² === +// +// Copyright 2018 CERN and copyright holders of ALICE O². +// Author: Teo Mrnjavac +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// In applying this license CERN does not waive the privileges and +// immunities granted to it by virtue of its status as an +// Intergovernmental Organization or submit itself to any jurisdiction. + +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.3.0 +// - protoc v5.29.3 +// source: protos/o2control.proto + +package pb + +import ( + context "context" + protos "github.com/AliceO2Group/Control/common/protos" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +const ( + Control_GetFrameworkInfo_FullMethodName = "/o2control.Control/GetFrameworkInfo" + Control_GetEnvironments_FullMethodName = "/o2control.Control/GetEnvironments" + Control_NewAutoEnvironment_FullMethodName = "/o2control.Control/NewAutoEnvironment" + Control_NewEnvironment_FullMethodName = "/o2control.Control/NewEnvironment" + Control_GetEnvironment_FullMethodName = "/o2control.Control/GetEnvironment" + Control_ControlEnvironment_FullMethodName = "/o2control.Control/ControlEnvironment" + Control_DestroyEnvironment_FullMethodName = "/o2control.Control/DestroyEnvironment" + Control_GetActiveDetectors_FullMethodName = "/o2control.Control/GetActiveDetectors" + Control_GetAvailableDetectors_FullMethodName = "/o2control.Control/GetAvailableDetectors" + Control_NewEnvironmentAsync_FullMethodName = "/o2control.Control/NewEnvironmentAsync" + Control_GetTasks_FullMethodName = "/o2control.Control/GetTasks" + Control_GetTask_FullMethodName = "/o2control.Control/GetTask" + Control_CleanupTasks_FullMethodName = "/o2control.Control/CleanupTasks" + Control_GetRoles_FullMethodName = "/o2control.Control/GetRoles" + Control_GetWorkflowTemplates_FullMethodName = "/o2control.Control/GetWorkflowTemplates" + Control_ListRepos_FullMethodName = "/o2control.Control/ListRepos" + Control_AddRepo_FullMethodName = "/o2control.Control/AddRepo" + Control_RemoveRepo_FullMethodName = "/o2control.Control/RemoveRepo" + Control_RefreshRepos_FullMethodName = "/o2control.Control/RefreshRepos" + Control_SetDefaultRepo_FullMethodName = "/o2control.Control/SetDefaultRepo" + Control_SetGlobalDefaultRevision_FullMethodName = "/o2control.Control/SetGlobalDefaultRevision" + Control_SetRepoDefaultRevision_FullMethodName = "/o2control.Control/SetRepoDefaultRevision" + Control_Subscribe_FullMethodName = "/o2control.Control/Subscribe" + Control_GetIntegratedServices_FullMethodName = "/o2control.Control/GetIntegratedServices" + Control_Teardown_FullMethodName = "/o2control.Control/Teardown" + Control_ModifyEnvironment_FullMethodName = "/o2control.Control/ModifyEnvironment" +) + +// ControlClient is the client API for Control service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type ControlClient interface { + GetFrameworkInfo(ctx context.Context, in *GetFrameworkInfoRequest, opts ...grpc.CallOption) (*GetFrameworkInfoReply, error) + GetEnvironments(ctx context.Context, in *GetEnvironmentsRequest, opts ...grpc.CallOption) (*GetEnvironmentsReply, error) + // Creates a new environment which automatically follows one STANDBY->RUNNING->DONE cycle in the state machine. + // It returns only once the environment reaches the CONFIGURED state or upon any earlier failure. + NewAutoEnvironment(ctx context.Context, in *NewAutoEnvironmentRequest, opts ...grpc.CallOption) (*NewAutoEnvironmentReply, error) + // Creates a new environment. + // It returns only once the environment reaches the CONFIGURED state or upon any earlier failure. + NewEnvironment(ctx context.Context, in *NewEnvironmentRequest, opts ...grpc.CallOption) (*NewEnvironmentReply, error) + GetEnvironment(ctx context.Context, in *GetEnvironmentRequest, opts ...grpc.CallOption) (*GetEnvironmentReply, error) + ControlEnvironment(ctx context.Context, in *ControlEnvironmentRequest, opts ...grpc.CallOption) (*ControlEnvironmentReply, error) + DestroyEnvironment(ctx context.Context, in *DestroyEnvironmentRequest, opts ...grpc.CallOption) (*DestroyEnvironmentReply, error) + GetActiveDetectors(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*GetActiveDetectorsReply, error) + GetAvailableDetectors(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*GetAvailableDetectorsReply, error) + // Creates a new environment. + // It returns once an environment ID is created and continues the creation asynchronously to the call. + // The environment will be listed in GetEnvironments() only once the workflow is loaded and deployment starts. + NewEnvironmentAsync(ctx context.Context, in *NewEnvironmentRequest, opts ...grpc.CallOption) (*NewEnvironmentReply, error) + GetTasks(ctx context.Context, in *GetTasksRequest, opts ...grpc.CallOption) (*GetTasksReply, error) + GetTask(ctx context.Context, in *GetTaskRequest, opts ...grpc.CallOption) (*GetTaskReply, error) + CleanupTasks(ctx context.Context, in *CleanupTasksRequest, opts ...grpc.CallOption) (*CleanupTasksReply, error) + GetRoles(ctx context.Context, in *GetRolesRequest, opts ...grpc.CallOption) (*GetRolesReply, error) + GetWorkflowTemplates(ctx context.Context, in *GetWorkflowTemplatesRequest, opts ...grpc.CallOption) (*GetWorkflowTemplatesReply, error) + ListRepos(ctx context.Context, in *ListReposRequest, opts ...grpc.CallOption) (*ListReposReply, error) + AddRepo(ctx context.Context, in *AddRepoRequest, opts ...grpc.CallOption) (*AddRepoReply, error) + RemoveRepo(ctx context.Context, in *RemoveRepoRequest, opts ...grpc.CallOption) (*RemoveRepoReply, error) + RefreshRepos(ctx context.Context, in *RefreshReposRequest, opts ...grpc.CallOption) (*Empty, error) + SetDefaultRepo(ctx context.Context, in *SetDefaultRepoRequest, opts ...grpc.CallOption) (*Empty, error) + SetGlobalDefaultRevision(ctx context.Context, in *SetGlobalDefaultRevisionRequest, opts ...grpc.CallOption) (*Empty, error) + SetRepoDefaultRevision(ctx context.Context, in *SetRepoDefaultRevisionRequest, opts ...grpc.CallOption) (*SetRepoDefaultRevisionReply, error) + Subscribe(ctx context.Context, in *SubscribeRequest, opts ...grpc.CallOption) (Control_SubscribeClient, error) + GetIntegratedServices(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*ListIntegratedServicesReply, error) + // Reserved and not implemented: + Teardown(ctx context.Context, in *TeardownRequest, opts ...grpc.CallOption) (*TeardownReply, error) + ModifyEnvironment(ctx context.Context, in *ModifyEnvironmentRequest, opts ...grpc.CallOption) (*ModifyEnvironmentReply, error) +} + +type controlClient struct { + cc grpc.ClientConnInterface +} + +func NewControlClient(cc grpc.ClientConnInterface) ControlClient { + return &controlClient{cc} +} + +func (c *controlClient) GetFrameworkInfo(ctx context.Context, in *GetFrameworkInfoRequest, opts ...grpc.CallOption) (*GetFrameworkInfoReply, error) { + out := new(GetFrameworkInfoReply) + err := c.cc.Invoke(ctx, Control_GetFrameworkInfo_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *controlClient) GetEnvironments(ctx context.Context, in *GetEnvironmentsRequest, opts ...grpc.CallOption) (*GetEnvironmentsReply, error) { + out := new(GetEnvironmentsReply) + err := c.cc.Invoke(ctx, Control_GetEnvironments_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *controlClient) NewAutoEnvironment(ctx context.Context, in *NewAutoEnvironmentRequest, opts ...grpc.CallOption) (*NewAutoEnvironmentReply, error) { + out := new(NewAutoEnvironmentReply) + err := c.cc.Invoke(ctx, Control_NewAutoEnvironment_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *controlClient) NewEnvironment(ctx context.Context, in *NewEnvironmentRequest, opts ...grpc.CallOption) (*NewEnvironmentReply, error) { + out := new(NewEnvironmentReply) + err := c.cc.Invoke(ctx, Control_NewEnvironment_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *controlClient) GetEnvironment(ctx context.Context, in *GetEnvironmentRequest, opts ...grpc.CallOption) (*GetEnvironmentReply, error) { + out := new(GetEnvironmentReply) + err := c.cc.Invoke(ctx, Control_GetEnvironment_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *controlClient) ControlEnvironment(ctx context.Context, in *ControlEnvironmentRequest, opts ...grpc.CallOption) (*ControlEnvironmentReply, error) { + out := new(ControlEnvironmentReply) + err := c.cc.Invoke(ctx, Control_ControlEnvironment_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *controlClient) DestroyEnvironment(ctx context.Context, in *DestroyEnvironmentRequest, opts ...grpc.CallOption) (*DestroyEnvironmentReply, error) { + out := new(DestroyEnvironmentReply) + err := c.cc.Invoke(ctx, Control_DestroyEnvironment_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *controlClient) GetActiveDetectors(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*GetActiveDetectorsReply, error) { + out := new(GetActiveDetectorsReply) + err := c.cc.Invoke(ctx, Control_GetActiveDetectors_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *controlClient) GetAvailableDetectors(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*GetAvailableDetectorsReply, error) { + out := new(GetAvailableDetectorsReply) + err := c.cc.Invoke(ctx, Control_GetAvailableDetectors_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *controlClient) NewEnvironmentAsync(ctx context.Context, in *NewEnvironmentRequest, opts ...grpc.CallOption) (*NewEnvironmentReply, error) { + out := new(NewEnvironmentReply) + err := c.cc.Invoke(ctx, Control_NewEnvironmentAsync_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *controlClient) GetTasks(ctx context.Context, in *GetTasksRequest, opts ...grpc.CallOption) (*GetTasksReply, error) { + out := new(GetTasksReply) + err := c.cc.Invoke(ctx, Control_GetTasks_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *controlClient) GetTask(ctx context.Context, in *GetTaskRequest, opts ...grpc.CallOption) (*GetTaskReply, error) { + out := new(GetTaskReply) + err := c.cc.Invoke(ctx, Control_GetTask_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *controlClient) CleanupTasks(ctx context.Context, in *CleanupTasksRequest, opts ...grpc.CallOption) (*CleanupTasksReply, error) { + out := new(CleanupTasksReply) + err := c.cc.Invoke(ctx, Control_CleanupTasks_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *controlClient) GetRoles(ctx context.Context, in *GetRolesRequest, opts ...grpc.CallOption) (*GetRolesReply, error) { + out := new(GetRolesReply) + err := c.cc.Invoke(ctx, Control_GetRoles_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *controlClient) GetWorkflowTemplates(ctx context.Context, in *GetWorkflowTemplatesRequest, opts ...grpc.CallOption) (*GetWorkflowTemplatesReply, error) { + out := new(GetWorkflowTemplatesReply) + err := c.cc.Invoke(ctx, Control_GetWorkflowTemplates_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *controlClient) ListRepos(ctx context.Context, in *ListReposRequest, opts ...grpc.CallOption) (*ListReposReply, error) { + out := new(ListReposReply) + err := c.cc.Invoke(ctx, Control_ListRepos_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *controlClient) AddRepo(ctx context.Context, in *AddRepoRequest, opts ...grpc.CallOption) (*AddRepoReply, error) { + out := new(AddRepoReply) + err := c.cc.Invoke(ctx, Control_AddRepo_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *controlClient) RemoveRepo(ctx context.Context, in *RemoveRepoRequest, opts ...grpc.CallOption) (*RemoveRepoReply, error) { + out := new(RemoveRepoReply) + err := c.cc.Invoke(ctx, Control_RemoveRepo_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *controlClient) RefreshRepos(ctx context.Context, in *RefreshReposRequest, opts ...grpc.CallOption) (*Empty, error) { + out := new(Empty) + err := c.cc.Invoke(ctx, Control_RefreshRepos_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *controlClient) SetDefaultRepo(ctx context.Context, in *SetDefaultRepoRequest, opts ...grpc.CallOption) (*Empty, error) { + out := new(Empty) + err := c.cc.Invoke(ctx, Control_SetDefaultRepo_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *controlClient) SetGlobalDefaultRevision(ctx context.Context, in *SetGlobalDefaultRevisionRequest, opts ...grpc.CallOption) (*Empty, error) { + out := new(Empty) + err := c.cc.Invoke(ctx, Control_SetGlobalDefaultRevision_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *controlClient) SetRepoDefaultRevision(ctx context.Context, in *SetRepoDefaultRevisionRequest, opts ...grpc.CallOption) (*SetRepoDefaultRevisionReply, error) { + out := new(SetRepoDefaultRevisionReply) + err := c.cc.Invoke(ctx, Control_SetRepoDefaultRevision_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *controlClient) Subscribe(ctx context.Context, in *SubscribeRequest, opts ...grpc.CallOption) (Control_SubscribeClient, error) { + stream, err := c.cc.NewStream(ctx, &Control_ServiceDesc.Streams[0], Control_Subscribe_FullMethodName, opts...) + if err != nil { + return nil, err + } + x := &controlSubscribeClient{stream} + if err := x.ClientStream.SendMsg(in); err != nil { + return nil, err + } + if err := x.ClientStream.CloseSend(); err != nil { + return nil, err + } + return x, nil +} + +type Control_SubscribeClient interface { + Recv() (*protos.Event, error) + grpc.ClientStream +} + +type controlSubscribeClient struct { + grpc.ClientStream +} + +func (x *controlSubscribeClient) Recv() (*protos.Event, error) { + m := new(protos.Event) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +func (c *controlClient) GetIntegratedServices(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*ListIntegratedServicesReply, error) { + out := new(ListIntegratedServicesReply) + err := c.cc.Invoke(ctx, Control_GetIntegratedServices_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *controlClient) Teardown(ctx context.Context, in *TeardownRequest, opts ...grpc.CallOption) (*TeardownReply, error) { + out := new(TeardownReply) + err := c.cc.Invoke(ctx, Control_Teardown_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *controlClient) ModifyEnvironment(ctx context.Context, in *ModifyEnvironmentRequest, opts ...grpc.CallOption) (*ModifyEnvironmentReply, error) { + out := new(ModifyEnvironmentReply) + err := c.cc.Invoke(ctx, Control_ModifyEnvironment_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// ControlServer is the server API for Control service. +// All implementations should embed UnimplementedControlServer +// for forward compatibility +type ControlServer interface { + GetFrameworkInfo(context.Context, *GetFrameworkInfoRequest) (*GetFrameworkInfoReply, error) + GetEnvironments(context.Context, *GetEnvironmentsRequest) (*GetEnvironmentsReply, error) + // Creates a new environment which automatically follows one STANDBY->RUNNING->DONE cycle in the state machine. + // It returns only once the environment reaches the CONFIGURED state or upon any earlier failure. + NewAutoEnvironment(context.Context, *NewAutoEnvironmentRequest) (*NewAutoEnvironmentReply, error) + // Creates a new environment. + // It returns only once the environment reaches the CONFIGURED state or upon any earlier failure. + NewEnvironment(context.Context, *NewEnvironmentRequest) (*NewEnvironmentReply, error) + GetEnvironment(context.Context, *GetEnvironmentRequest) (*GetEnvironmentReply, error) + ControlEnvironment(context.Context, *ControlEnvironmentRequest) (*ControlEnvironmentReply, error) + DestroyEnvironment(context.Context, *DestroyEnvironmentRequest) (*DestroyEnvironmentReply, error) + GetActiveDetectors(context.Context, *Empty) (*GetActiveDetectorsReply, error) + GetAvailableDetectors(context.Context, *Empty) (*GetAvailableDetectorsReply, error) + // Creates a new environment. + // It returns once an environment ID is created and continues the creation asynchronously to the call. + // The environment will be listed in GetEnvironments() only once the workflow is loaded and deployment starts. + NewEnvironmentAsync(context.Context, *NewEnvironmentRequest) (*NewEnvironmentReply, error) + GetTasks(context.Context, *GetTasksRequest) (*GetTasksReply, error) + GetTask(context.Context, *GetTaskRequest) (*GetTaskReply, error) + CleanupTasks(context.Context, *CleanupTasksRequest) (*CleanupTasksReply, error) + GetRoles(context.Context, *GetRolesRequest) (*GetRolesReply, error) + GetWorkflowTemplates(context.Context, *GetWorkflowTemplatesRequest) (*GetWorkflowTemplatesReply, error) + ListRepos(context.Context, *ListReposRequest) (*ListReposReply, error) + AddRepo(context.Context, *AddRepoRequest) (*AddRepoReply, error) + RemoveRepo(context.Context, *RemoveRepoRequest) (*RemoveRepoReply, error) + RefreshRepos(context.Context, *RefreshReposRequest) (*Empty, error) + SetDefaultRepo(context.Context, *SetDefaultRepoRequest) (*Empty, error) + SetGlobalDefaultRevision(context.Context, *SetGlobalDefaultRevisionRequest) (*Empty, error) + SetRepoDefaultRevision(context.Context, *SetRepoDefaultRevisionRequest) (*SetRepoDefaultRevisionReply, error) + Subscribe(*SubscribeRequest, Control_SubscribeServer) error + GetIntegratedServices(context.Context, *Empty) (*ListIntegratedServicesReply, error) + // Reserved and not implemented: + Teardown(context.Context, *TeardownRequest) (*TeardownReply, error) + ModifyEnvironment(context.Context, *ModifyEnvironmentRequest) (*ModifyEnvironmentReply, error) +} + +// UnimplementedControlServer should be embedded to have forward compatible implementations. +type UnimplementedControlServer struct { +} + +func (UnimplementedControlServer) GetFrameworkInfo(context.Context, *GetFrameworkInfoRequest) (*GetFrameworkInfoReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetFrameworkInfo not implemented") +} +func (UnimplementedControlServer) GetEnvironments(context.Context, *GetEnvironmentsRequest) (*GetEnvironmentsReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetEnvironments not implemented") +} +func (UnimplementedControlServer) NewAutoEnvironment(context.Context, *NewAutoEnvironmentRequest) (*NewAutoEnvironmentReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method NewAutoEnvironment not implemented") +} +func (UnimplementedControlServer) NewEnvironment(context.Context, *NewEnvironmentRequest) (*NewEnvironmentReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method NewEnvironment not implemented") +} +func (UnimplementedControlServer) GetEnvironment(context.Context, *GetEnvironmentRequest) (*GetEnvironmentReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetEnvironment not implemented") +} +func (UnimplementedControlServer) ControlEnvironment(context.Context, *ControlEnvironmentRequest) (*ControlEnvironmentReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method ControlEnvironment not implemented") +} +func (UnimplementedControlServer) DestroyEnvironment(context.Context, *DestroyEnvironmentRequest) (*DestroyEnvironmentReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method DestroyEnvironment not implemented") +} +func (UnimplementedControlServer) GetActiveDetectors(context.Context, *Empty) (*GetActiveDetectorsReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetActiveDetectors not implemented") +} +func (UnimplementedControlServer) GetAvailableDetectors(context.Context, *Empty) (*GetAvailableDetectorsReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetAvailableDetectors not implemented") +} +func (UnimplementedControlServer) NewEnvironmentAsync(context.Context, *NewEnvironmentRequest) (*NewEnvironmentReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method NewEnvironmentAsync not implemented") +} +func (UnimplementedControlServer) GetTasks(context.Context, *GetTasksRequest) (*GetTasksReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetTasks not implemented") +} +func (UnimplementedControlServer) GetTask(context.Context, *GetTaskRequest) (*GetTaskReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetTask not implemented") +} +func (UnimplementedControlServer) CleanupTasks(context.Context, *CleanupTasksRequest) (*CleanupTasksReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method CleanupTasks not implemented") +} +func (UnimplementedControlServer) GetRoles(context.Context, *GetRolesRequest) (*GetRolesReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetRoles not implemented") +} +func (UnimplementedControlServer) GetWorkflowTemplates(context.Context, *GetWorkflowTemplatesRequest) (*GetWorkflowTemplatesReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetWorkflowTemplates not implemented") +} +func (UnimplementedControlServer) ListRepos(context.Context, *ListReposRequest) (*ListReposReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method ListRepos not implemented") +} +func (UnimplementedControlServer) AddRepo(context.Context, *AddRepoRequest) (*AddRepoReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method AddRepo not implemented") +} +func (UnimplementedControlServer) RemoveRepo(context.Context, *RemoveRepoRequest) (*RemoveRepoReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method RemoveRepo not implemented") +} +func (UnimplementedControlServer) RefreshRepos(context.Context, *RefreshReposRequest) (*Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method RefreshRepos not implemented") +} +func (UnimplementedControlServer) SetDefaultRepo(context.Context, *SetDefaultRepoRequest) (*Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method SetDefaultRepo not implemented") +} +func (UnimplementedControlServer) SetGlobalDefaultRevision(context.Context, *SetGlobalDefaultRevisionRequest) (*Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method SetGlobalDefaultRevision not implemented") +} +func (UnimplementedControlServer) SetRepoDefaultRevision(context.Context, *SetRepoDefaultRevisionRequest) (*SetRepoDefaultRevisionReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method SetRepoDefaultRevision not implemented") +} +func (UnimplementedControlServer) Subscribe(*SubscribeRequest, Control_SubscribeServer) error { + return status.Errorf(codes.Unimplemented, "method Subscribe not implemented") +} +func (UnimplementedControlServer) GetIntegratedServices(context.Context, *Empty) (*ListIntegratedServicesReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetIntegratedServices not implemented") +} +func (UnimplementedControlServer) Teardown(context.Context, *TeardownRequest) (*TeardownReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method Teardown not implemented") +} +func (UnimplementedControlServer) ModifyEnvironment(context.Context, *ModifyEnvironmentRequest) (*ModifyEnvironmentReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method ModifyEnvironment not implemented") +} + +// UnsafeControlServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to ControlServer will +// result in compilation errors. +type UnsafeControlServer interface { + mustEmbedUnimplementedControlServer() +} + +func RegisterControlServer(s grpc.ServiceRegistrar, srv ControlServer) { + s.RegisterService(&Control_ServiceDesc, srv) +} + +func _Control_GetFrameworkInfo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetFrameworkInfoRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ControlServer).GetFrameworkInfo(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Control_GetFrameworkInfo_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).GetFrameworkInfo(ctx, req.(*GetFrameworkInfoRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Control_GetEnvironments_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetEnvironmentsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ControlServer).GetEnvironments(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Control_GetEnvironments_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).GetEnvironments(ctx, req.(*GetEnvironmentsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Control_NewAutoEnvironment_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(NewAutoEnvironmentRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ControlServer).NewAutoEnvironment(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Control_NewAutoEnvironment_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).NewAutoEnvironment(ctx, req.(*NewAutoEnvironmentRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Control_NewEnvironment_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(NewEnvironmentRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ControlServer).NewEnvironment(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Control_NewEnvironment_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).NewEnvironment(ctx, req.(*NewEnvironmentRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Control_GetEnvironment_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetEnvironmentRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ControlServer).GetEnvironment(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Control_GetEnvironment_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).GetEnvironment(ctx, req.(*GetEnvironmentRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Control_ControlEnvironment_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ControlEnvironmentRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ControlServer).ControlEnvironment(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Control_ControlEnvironment_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).ControlEnvironment(ctx, req.(*ControlEnvironmentRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Control_DestroyEnvironment_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DestroyEnvironmentRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ControlServer).DestroyEnvironment(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Control_DestroyEnvironment_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).DestroyEnvironment(ctx, req.(*DestroyEnvironmentRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Control_GetActiveDetectors_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(Empty) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ControlServer).GetActiveDetectors(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Control_GetActiveDetectors_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).GetActiveDetectors(ctx, req.(*Empty)) + } + return interceptor(ctx, in, info, handler) +} + +func _Control_GetAvailableDetectors_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(Empty) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ControlServer).GetAvailableDetectors(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Control_GetAvailableDetectors_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).GetAvailableDetectors(ctx, req.(*Empty)) + } + return interceptor(ctx, in, info, handler) +} + +func _Control_NewEnvironmentAsync_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(NewEnvironmentRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ControlServer).NewEnvironmentAsync(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Control_NewEnvironmentAsync_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).NewEnvironmentAsync(ctx, req.(*NewEnvironmentRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Control_GetTasks_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetTasksRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ControlServer).GetTasks(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Control_GetTasks_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).GetTasks(ctx, req.(*GetTasksRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Control_GetTask_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetTaskRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ControlServer).GetTask(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Control_GetTask_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).GetTask(ctx, req.(*GetTaskRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Control_CleanupTasks_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CleanupTasksRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ControlServer).CleanupTasks(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Control_CleanupTasks_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).CleanupTasks(ctx, req.(*CleanupTasksRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Control_GetRoles_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetRolesRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ControlServer).GetRoles(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Control_GetRoles_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).GetRoles(ctx, req.(*GetRolesRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Control_GetWorkflowTemplates_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetWorkflowTemplatesRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ControlServer).GetWorkflowTemplates(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Control_GetWorkflowTemplates_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).GetWorkflowTemplates(ctx, req.(*GetWorkflowTemplatesRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Control_ListRepos_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListReposRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ControlServer).ListRepos(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Control_ListRepos_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).ListRepos(ctx, req.(*ListReposRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Control_AddRepo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(AddRepoRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ControlServer).AddRepo(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Control_AddRepo_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).AddRepo(ctx, req.(*AddRepoRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Control_RemoveRepo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RemoveRepoRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ControlServer).RemoveRepo(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Control_RemoveRepo_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).RemoveRepo(ctx, req.(*RemoveRepoRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Control_RefreshRepos_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RefreshReposRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ControlServer).RefreshRepos(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Control_RefreshRepos_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).RefreshRepos(ctx, req.(*RefreshReposRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Control_SetDefaultRepo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SetDefaultRepoRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ControlServer).SetDefaultRepo(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Control_SetDefaultRepo_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).SetDefaultRepo(ctx, req.(*SetDefaultRepoRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Control_SetGlobalDefaultRevision_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SetGlobalDefaultRevisionRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ControlServer).SetGlobalDefaultRevision(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Control_SetGlobalDefaultRevision_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).SetGlobalDefaultRevision(ctx, req.(*SetGlobalDefaultRevisionRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Control_SetRepoDefaultRevision_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SetRepoDefaultRevisionRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ControlServer).SetRepoDefaultRevision(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Control_SetRepoDefaultRevision_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).SetRepoDefaultRevision(ctx, req.(*SetRepoDefaultRevisionRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Control_Subscribe_Handler(srv interface{}, stream grpc.ServerStream) error { + m := new(SubscribeRequest) + if err := stream.RecvMsg(m); err != nil { + return err + } + return srv.(ControlServer).Subscribe(m, &controlSubscribeServer{stream}) +} + +type Control_SubscribeServer interface { + Send(*protos.Event) error + grpc.ServerStream +} + +type controlSubscribeServer struct { + grpc.ServerStream +} + +func (x *controlSubscribeServer) Send(m *protos.Event) error { + return x.ServerStream.SendMsg(m) +} + +func _Control_GetIntegratedServices_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(Empty) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ControlServer).GetIntegratedServices(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Control_GetIntegratedServices_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).GetIntegratedServices(ctx, req.(*Empty)) + } + return interceptor(ctx, in, info, handler) +} + +func _Control_Teardown_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(TeardownRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ControlServer).Teardown(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Control_Teardown_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).Teardown(ctx, req.(*TeardownRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Control_ModifyEnvironment_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ModifyEnvironmentRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ControlServer).ModifyEnvironment(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Control_ModifyEnvironment_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).ModifyEnvironment(ctx, req.(*ModifyEnvironmentRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// Control_ServiceDesc is the grpc.ServiceDesc for Control service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var Control_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "o2control.Control", + HandlerType: (*ControlServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "GetFrameworkInfo", + Handler: _Control_GetFrameworkInfo_Handler, + }, + { + MethodName: "GetEnvironments", + Handler: _Control_GetEnvironments_Handler, + }, + { + MethodName: "NewAutoEnvironment", + Handler: _Control_NewAutoEnvironment_Handler, + }, + { + MethodName: "NewEnvironment", + Handler: _Control_NewEnvironment_Handler, + }, + { + MethodName: "GetEnvironment", + Handler: _Control_GetEnvironment_Handler, + }, + { + MethodName: "ControlEnvironment", + Handler: _Control_ControlEnvironment_Handler, + }, + { + MethodName: "DestroyEnvironment", + Handler: _Control_DestroyEnvironment_Handler, + }, + { + MethodName: "GetActiveDetectors", + Handler: _Control_GetActiveDetectors_Handler, + }, + { + MethodName: "GetAvailableDetectors", + Handler: _Control_GetAvailableDetectors_Handler, + }, + { + MethodName: "NewEnvironmentAsync", + Handler: _Control_NewEnvironmentAsync_Handler, + }, + { + MethodName: "GetTasks", + Handler: _Control_GetTasks_Handler, + }, + { + MethodName: "GetTask", + Handler: _Control_GetTask_Handler, + }, + { + MethodName: "CleanupTasks", + Handler: _Control_CleanupTasks_Handler, + }, + { + MethodName: "GetRoles", + Handler: _Control_GetRoles_Handler, + }, + { + MethodName: "GetWorkflowTemplates", + Handler: _Control_GetWorkflowTemplates_Handler, + }, + { + MethodName: "ListRepos", + Handler: _Control_ListRepos_Handler, + }, + { + MethodName: "AddRepo", + Handler: _Control_AddRepo_Handler, + }, + { + MethodName: "RemoveRepo", + Handler: _Control_RemoveRepo_Handler, + }, + { + MethodName: "RefreshRepos", + Handler: _Control_RefreshRepos_Handler, + }, + { + MethodName: "SetDefaultRepo", + Handler: _Control_SetDefaultRepo_Handler, + }, + { + MethodName: "SetGlobalDefaultRevision", + Handler: _Control_SetGlobalDefaultRevision_Handler, + }, + { + MethodName: "SetRepoDefaultRevision", + Handler: _Control_SetRepoDefaultRevision_Handler, + }, + { + MethodName: "GetIntegratedServices", + Handler: _Control_GetIntegratedServices_Handler, + }, + { + MethodName: "Teardown", + Handler: _Control_Teardown_Handler, + }, + { + MethodName: "ModifyEnvironment", + Handler: _Control_ModifyEnvironment_Handler, + }, + }, + Streams: []grpc.StreamDesc{ + { + StreamName: "Subscribe", + Handler: _Control_Subscribe_Handler, + ServerStreams: true, + }, + }, + Metadata: "protos/o2control.proto", +} diff --git a/common/cmdinfo.go b/common/cmdinfo.go index a86ed0d86..7fe890aa2 100644 --- a/common/cmdinfo.go +++ b/common/cmdinfo.go @@ -26,14 +26,21 @@ package common import ( "strings" + + "github.com/AliceO2Group/Control/common/logger/infologger" + log "github.com/sirupsen/logrus" ) +type LogTaskOutput int + type CommandInfo struct { Env []string `json:"env,omitempty" yaml:"env,omitempty"` Shell *bool `json:"shell,omitempty" yaml:"shell,omitempty"` Value *string `json:"value,omitempty" yaml:"value,omitempty"` - Arguments []string `json:"arguments,omitempty" yaml:"arguments,omitempty"` User *string `json:"user,omitempty" yaml:"user,omitempty"` + Arguments []string `json:"arguments,omitempty" yaml:"arguments,omitempty"` + Stdout *string `json:"stdout,omitempty" yaml:"stdout,omitempty"` + Stderr *string `json:"stderr,omitempty" yaml:"stderr,omitempty"` } func (m *CommandInfo) UnmarshalYAML(unmarshal func(interface{}) error) (err error) { @@ -41,8 +48,11 @@ func (m *CommandInfo) UnmarshalYAML(unmarshal func(interface{}) error) (err erro Env []string `json:"env,omitempty" yaml:"env,omitempty"` Shell *string `json:"shell,omitempty" yaml:"shell,omitempty"` Value *string `json:"value,omitempty" yaml:"value,omitempty"` - Arguments []string `json:"arguments,omitempty" yaml:"arguments,omitempty"` User *string `json:"user,omitempty" yaml:"user,omitempty"` + Arguments []string `json:"arguments,omitempty" yaml:"arguments,omitempty"` + Log *string `json:"log,omitempty" yaml:"log,omitempty"` + Stdout *string `json:"stdout,omitempty" yaml:"stdout,omitempty"` + Stderr *string `json:"stderr,omitempty" yaml:"stderr,omitempty"` } aux := _commandInfo{} err = unmarshal(&aux) @@ -58,6 +68,16 @@ func (m *CommandInfo) UnmarshalYAML(unmarshal func(interface{}) error) (err erro m.Value = aux.Value m.Arguments = aux.Arguments m.User = aux.User + m.Stdout = aux.Stdout + m.Stderr = aux.Stderr + + // the stdout field used to be called log + if aux.Log != nil && aux.Stdout == nil { + log.WithField("command", m.Value). + WithField("level", infologger.IL_Devel). + Warn("the 'log' field in the task template is deprecated, please use 'stdout' and 'stderr' instead") + m.Stdout = aux.Log + } return } @@ -68,8 +88,10 @@ func (m *CommandInfo) Copy() *CommandInfo { Env: append([]string{}, m.Env...), Shell: new(bool), Value: new(string), - Arguments: append([]string{}, m.Arguments...), User: new(string), + Arguments: append([]string{}, m.Arguments...), + Stdout: m.Stdout, + Stderr: m.Stderr, } if m.Shell != nil { *cmd.Shell = *m.Shell @@ -94,26 +116,34 @@ func (m *CommandInfo) Equals(other *CommandInfo) (response bool) { return false } - for i, _ := range m.Env { + for i := range m.Env { if m.Env[i] != other.Env[i] { return false } } - for i, _ := range m.Arguments { + for i := range m.Arguments { if m.Arguments[i] != other.Arguments[i] { return false } } if !((m.Value == nil && other.Value == nil) || - *m.Value == *other.Value) { + *m.Value == *other.Value) { return false } if !((m.User == nil && other.User == nil) || - *m.User == *other.User) { + *m.User == *other.User) { return false } if !((m.Shell == nil && other.Shell == nil) || - *m.Shell == *other.Shell) { + *m.Shell == *other.Shell) { + return false + } + if !((m.Stdout == nil && other.Stdout == nil) || + *m.Stdout == *other.Stdout) { + return false + } + if !((m.Stderr == nil && other.Stderr == nil) || + *m.Stderr == *other.Stderr) { return false } return @@ -137,11 +167,17 @@ func (m *CommandInfo) UpdateFrom(n *CommandInfo) { if n.User != nil { *m.User = *n.User } + if n.Stdout != nil { + *m.Stdout = *n.Stdout + } + if n.Stderr != nil { + *m.Stderr = *n.Stderr + } } const defaultCommandInfoShell = false -func (m *CommandInfo) GetEnv () []string { +func (m *CommandInfo) GetEnv() []string { if m != nil { return m.Env } @@ -174,4 +210,4 @@ func (m *CommandInfo) GetUser() string { return *m.User } return "" -} \ No newline at end of file +} diff --git a/common/controlmode/controlmode.go b/common/controlmode/controlmode.go index 3ecb99637..1a77e98d6 100644 --- a/common/controlmode/controlmode.go +++ b/common/controlmode/controlmode.go @@ -34,21 +34,35 @@ import ( type ControlMode int -const( +const ( DIRECT ControlMode = iota FAIRMQ + BASIC + HOOK + KUBECTL_DIRECT + KUBECTL_FAIRMQ + KUBERNETES_DIRECT + KUBERNETES_FAIRMQ ) -func (cm *ControlMode) String() string { - if cm == nil { - return "" - } - - switch *cm { +func (cm ControlMode) String() string { + switch cm { case DIRECT: return "direct" case FAIRMQ: return "fairmq" + case BASIC: + return "basic" + case HOOK: + return "hook" + case KUBECTL_DIRECT: + return "kubectl_direct" + case KUBECTL_FAIRMQ: + return "kubectl_fairmq" + case KUBERNETES_DIRECT: + return "kubernetes_direct" + case KUBERNETES_FAIRMQ: + return "kubernetes_fairmq" } return "direct" } @@ -65,6 +79,18 @@ func (cm *ControlMode) UnmarshalText(b []byte) error { *cm = DIRECT case "fairmq": *cm = FAIRMQ + case "basic": + *cm = BASIC + case "hook": + *cm = HOOK + case "kubectl_direct": + *cm = KUBECTL_DIRECT + case "kubectl_fairmq": + *cm = KUBECTL_FAIRMQ + case "kubernetes_direct": + *cm = KUBERNETES_DIRECT + case "kubernetes_fairmq": + *cm = KUBERNETES_FAIRMQ default: *cm = DIRECT } diff --git a/common/doc.go b/common/doc.go index ca4c77169..1e46b6324 100644 --- a/common/doc.go +++ b/common/doc.go @@ -25,3 +25,6 @@ // Package common serves as a catch-all package for sharing types // between O² Control components. package common + +//go:generate protoc -I=./ --go_out=. --go_opt=paths=source_relative --go-grpc_opt=paths=source_relative --go-grpc_out=require_unimplemented_servers=false:. protos/events.proto +//go:generate protoc -I=./ --go_out=. --go_opt=paths=source_relative --go-grpc_opt=paths=source_relative --go-grpc_out=require_unimplemented_servers=false:. protos/common.proto diff --git a/common/event/agentexecutorfailedevent.go b/common/event/agentexecutorfailedevent.go new file mode 100644 index 000000000..6888ec8f7 --- /dev/null +++ b/common/event/agentexecutorfailedevent.go @@ -0,0 +1,67 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2023 CERN and copyright holders of ALICE O². + * Author: Teo Mrnjavac + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package event + +import mesos "github.com/mesos/mesos-go/api/v1/lib" + +type ExecutorFailedEvent struct { + eventBase + ExecutorId mesos.ExecutorID `json:"executorId"` +} + +func NewExecutorFailedEvent(executorId *mesos.ExecutorID) *ExecutorFailedEvent { + return &ExecutorFailedEvent{ + eventBase: *newDeviceEventBase("ExecutorFailedEvent", nil), + ExecutorId: *executorId, + } +} + +func (e *ExecutorFailedEvent) GetName() string { + return e.ExecutorId.GetValue() +} + +func (e *ExecutorFailedEvent) GetId() mesos.ExecutorID { + return e.ExecutorId +} + +type AgentFailedEvent struct { + eventBase + AgentId mesos.AgentID `json:"agentId"` +} + +func NewAgentFailedEvent(agentId *mesos.AgentID) *AgentFailedEvent { + return &AgentFailedEvent{ + eventBase: *newDeviceEventBase("AgentFailedEvent", nil), + AgentId: *agentId, + } +} + +func (e *AgentFailedEvent) GetName() string { + return e.AgentId.GetValue() +} + +func (e *AgentFailedEvent) GetId() mesos.AgentID { + return e.AgentId +} diff --git a/common/event/announcetaskpidevent.go b/common/event/announcetaskpidevent.go new file mode 100644 index 000000000..e97f47b4e --- /dev/null +++ b/common/event/announcetaskpidevent.go @@ -0,0 +1,57 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2020 CERN and copyright holders of ALICE O². + * Author: Miltiadis Alexis + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package event + +import "github.com/AliceO2Group/Control/common/utils" + +type AnnounceTaskPIDEvent struct { + eventBase + TaskId string `json:"taskId"` + PID int32 `json:"pid"` +} + +func (e *AnnounceTaskPIDEvent) GetName() string { + return "ANNOUNCE_TASK_PID" +} + +func (e *AnnounceTaskPIDEvent) GetTaskId() string { + return e.TaskId +} + +func (e *AnnounceTaskPIDEvent) GetTaskPID() int { + return int(e.PID) +} + +func NewAnnounceTaskPIDEvent(id string, pid int32) (e *AnnounceTaskPIDEvent) { + e = &AnnounceTaskPIDEvent{ + eventBase: eventBase{ + Timestamp: utils.NewUnixTimestamp(), + MessageType: "AnnounceTaskPIDEvent", + }, + TaskId: id, + PID: pid, + } + return e +} diff --git a/common/event/deviceevent.go b/common/event/deviceevent.go index d1966f0d9..a496745ae 100644 --- a/common/event/deviceevent.go +++ b/common/event/deviceevent.go @@ -30,21 +30,22 @@ import ( ) type DeviceEventOrigin struct { - AgentId mesos.AgentID `json:"agentId"` - ExecutorId mesos.ExecutorID `json:"executorId"` - TaskId mesos.TaskID `json:"taskId"` + // FIXME: replace these Mesos-tainted string wrappers with plain strings + AgentId mesos.AgentID `json:"agentId"` + ExecutorId mesos.ExecutorID `json:"executorId"` + TaskId mesos.TaskID `json:"taskId"` } type DeviceEvent interface { - GetName() string + Event GetOrigin() DeviceEventOrigin GetType() pb.DeviceEventType } type DeviceEventBase struct { - Type pb.DeviceEventType `json:"type"` - Origin DeviceEventOrigin `json:"origin"` - MessageType string `json:"_messageType"` + eventBase + Type pb.DeviceEventType `json:"type"` + Origin DeviceEventOrigin `json:"origin"` } func (b *DeviceEventBase) GetOrigin() DeviceEventOrigin { @@ -63,12 +64,28 @@ func (b *DeviceEventBase) GetType() pb.DeviceEventType { func NewDeviceEvent(origin DeviceEventOrigin, t pb.DeviceEventType) (de DeviceEvent) { switch t { - case pb.DeviceEventType_END_OF_DATA: - de = &EndOfData{ + case pb.DeviceEventType_END_OF_STREAM: + de = &EndOfStream{ DeviceEventBase: DeviceEventBase{ - Type: t, - Origin: origin, - MessageType: "DeviceEvent", + eventBase: *newDeviceEventBase("DeviceEvent", nil), + Type: t, + Origin: origin, + }, + } + case pb.DeviceEventType_BASIC_TASK_TERMINATED: + de = &BasicTaskTerminated{ + DeviceEventBase: DeviceEventBase{ + eventBase: *newDeviceEventBase("DeviceEvent", nil), + Type: t, + Origin: origin, + }, + } + case pb.DeviceEventType_TASK_INTERNAL_ERROR: + de = &TaskInternalError{ + DeviceEventBase: DeviceEventBase{ + eventBase: *newDeviceEventBase("DeviceEvent", nil), + Type: t, + Origin: origin, }, } case pb.DeviceEventType_NULL_DEVICE_EVENT: @@ -77,10 +94,31 @@ func NewDeviceEvent(origin DeviceEventOrigin, t pb.DeviceEventType) (de DeviceEv return de } -type EndOfData struct { +type EndOfStream struct { DeviceEventBase } -func (e *EndOfData) GetName() string { - return "END_OF_DATA" -} \ No newline at end of file +func (e *EndOfStream) GetName() string { + return "END_OF_STREAM" +} + +type BasicTaskTerminated struct { + DeviceEventBase + ExitCode int `json:"exitCode"` + Stdout string `json:"stdout"` + Stderr string `json:"stderr"` + VoluntaryTermination bool `json:"voluntaryTermination"` + FinalMesosState mesos.TaskState `json:"finalMesosState"` +} + +func (e *BasicTaskTerminated) GetName() string { + return "BASIC_TASK_TERMINATED" +} + +type TaskInternalError struct { + DeviceEventBase +} + +func (e *TaskInternalError) GetName() string { + return "TASK_INTERNAL_ERROR" +} diff --git a/common/event/environmentevents.go b/common/event/environmentevents.go new file mode 100644 index 000000000..e60ab4492 --- /dev/null +++ b/common/event/environmentevents.go @@ -0,0 +1,57 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2020 CERN and copyright holders of ALICE O². + * Author: Miltiadis Alexis + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package event + +type EnvironmentEvent struct { + eventBase + EnvironmentID string + Run uint32 + State string + Error error + Message string +} + +func (e *EnvironmentEvent) GetName() string { + return e.EnvironmentID +} + +func (e *EnvironmentEvent) GetRun() uint32 { + return e.Run +} + +func (e *EnvironmentEvent) GetState() string { + return e.State +} + +func (e *EnvironmentEvent) GetError() string { + if e.Error == nil { + return "" + } + return e.Error.Error() +} + +func (e *EnvironmentEvent) GetMessage() string { + return e.Message +} diff --git a/common/event/event.go b/common/event/event.go new file mode 100644 index 000000000..682a39239 --- /dev/null +++ b/common/event/event.go @@ -0,0 +1,71 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2020 CERN and copyright holders of ALICE O². + * Author: Teo Mrnjavac + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package event + +import ( + "github.com/AliceO2Group/Control/common/utils" +) + +type Event interface { + GetName() string + GetTimestamp() string + GetLabels() map[string]string + SetLabels(map[string]string) +} + +type eventBase struct { + Timestamp string `json:"timestamp"` + MessageType string `json:"_messageType"` + Labels map[string]string `json:"labels,omitempty"` +} + +func (e *eventBase) GetTimestamp() string { + return e.Timestamp +} + +func (e *eventBase) GetLabels() map[string]string { + return e.Labels +} + +func (e *eventBase) SetLabels(labels map[string]string) { + if e.Labels == nil { + e.Labels = make(map[string]string) + } + for k, v := range labels { + e.Labels[k] = v + } +} + +func newDeviceEventBase(messageType string, optionalTimestamp *string) (e *eventBase) { + timestamp := utils.NewUnixTimestamp() + if optionalTimestamp != nil { + timestamp = *optionalTimestamp + } + return &eventBase{ + Timestamp: timestamp, + MessageType: messageType, + Labels: map[string]string{}, + } +} diff --git a/common/event/event_suite_test.go b/common/event/event_suite_test.go new file mode 100644 index 000000000..03af84ee4 --- /dev/null +++ b/common/event/event_suite_test.go @@ -0,0 +1,13 @@ +package event_test + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +func TestEvent(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Event Suite") +} diff --git a/common/event/fifobuffer.go b/common/event/fifobuffer.go new file mode 100644 index 000000000..a49cf4f2b --- /dev/null +++ b/common/event/fifobuffer.go @@ -0,0 +1,87 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2025 CERN and copyright holders of ALICE O². + * Author: Teo Mrnjavac + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package event + +import ( + "math" + "sync" +) + +// This structure is meant to be used as a threadsafe FIFO with builtin waiting for new data +// in its Pop and PopMultiple functions. It is meant to be used with multiple goroutines, it is a +// waste of synchronization mechanisms if used synchronously. +type FifoBuffer[T any] struct { + lock sync.Mutex + cond sync.Cond + + buffer []T +} + +func NewFifoBuffer[T any]() (result FifoBuffer[T]) { + result = FifoBuffer[T]{ + lock: sync.Mutex{}, + } + result.cond = *sync.NewCond(&result.lock) + return +} + +func (this *FifoBuffer[T]) Push(value T) { + this.cond.L.Lock() + this.buffer = append(this.buffer, value) + this.cond.Signal() + this.cond.L.Unlock() +} + +// Blocks until it has some value in internal buffer +func (this *FifoBuffer[T]) PopMultiple(numberToPop uint) (result []T) { + this.cond.L.Lock() + defer this.cond.L.Unlock() + + for len(this.buffer) == 0 { + this.cond.Wait() + // this check is used when ReleaseGoroutines is called on waiting goroutine + if len(this.buffer) == 0 { + return + } + } + + result = make([]T, int(math.Min(float64(numberToPop), float64(len(this.buffer))))) + copy(result, this.buffer[0:len(result)]) + this.buffer = this.buffer[len(result):] + + return +} + +func (this *FifoBuffer[T]) Length() int { + this.cond.L.Lock() + defer this.cond.L.Unlock() + return len(this.buffer) +} + +func (this *FifoBuffer[T]) ReleaseGoroutines() { + this.cond.L.Lock() + this.cond.Broadcast() + this.cond.L.Unlock() +} diff --git a/common/event/fifobuffer_test.go b/common/event/fifobuffer_test.go new file mode 100644 index 000000000..fed318372 --- /dev/null +++ b/common/event/fifobuffer_test.go @@ -0,0 +1,129 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2025 CERN and copyright holders of ALICE O². + * Author: Teo Mrnjavac + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package event + +import ( + "sync" + "time" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +var _ = Describe("FifoBuffer", func() { + When("Poping lower amount of items than inside of a buffer", func() { + It("returns requested items", func() { + buffer := NewFifoBuffer[int]() + buffer.Push(1) + buffer.Push(2) + buffer.Push(3) + + Expect(buffer.Length()).To(Equal(3)) + + results := buffer.PopMultiple(2) + Expect(results).To(Equal([]int{1, 2})) + }) + }) + + When("Poping higher amount of items than inside of a buffer", func() { + It("returns only available items", func() { + buffer := NewFifoBuffer[int]() + buffer.Push(1) + + results := buffer.PopMultiple(2) + Expect(results).To(Equal([]int{1})) + }) + }) + + When("We use buffer with multiple goroutines pushing first (PopMultiple)", func() { + It("is synchronised properly", func() { + buffer := NewFifoBuffer[int]() + channel := make(chan struct{}) + + wg := sync.WaitGroup{} + wg.Add(2) + + go func() { + buffer.Push(1) + channel <- struct{}{} + wg.Done() + }() + + go func() { + <-channel + result := buffer.PopMultiple(42) + Expect(result, 1) + wg.Done() + }() + + wg.Wait() + }) + }) + + When("We use buffer with multiple goroutines popping first", func() { + It("is synchronised properly", func() { + buffer := NewFifoBuffer[int]() + channel := make(chan struct{}) + + wg := sync.WaitGroup{} + wg.Add(2) + + go func() { + // Pop is blocking is we have empty buffer, so we notify before + channel <- struct{}{} + result := buffer.PopMultiple(42) + Expect(result, 1) + wg.Done() + }() + + go func() { + <-channel + buffer.Push(1) + wg.Done() + }() + + wg.Wait() + }) + }) + + When("We block FifoBuffer without data and call Release", func() { + It("releases goroutines properly", func() { + buffer := NewFifoBuffer[int]() + everythingDone := sync.WaitGroup{} + channel := make(chan struct{}) + + everythingDone.Add(1) + go func() { + channel <- struct{}{} + buffer.PopMultiple(42) + everythingDone.Done() + }() + <-channel + time.Sleep(100 * time.Millisecond) + buffer.ReleaseGoroutines() + everythingDone.Wait() + }) + }) +}) diff --git a/common/event/integratedserviceevent.go b/common/event/integratedserviceevent.go new file mode 100644 index 000000000..f236d5e58 --- /dev/null +++ b/common/event/integratedserviceevent.go @@ -0,0 +1,42 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2023 CERN and copyright holders of ALICE O². + * Author: Teo Mrnjavac + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package event + +type IntegratedServiceEvent interface { + Event + GetServiceName() string +} + +type IntegratedServiceEventBase struct { + eventBase + ServiceName string `json:"serviceName"` +} + +func (b *IntegratedServiceEventBase) GetServiceName() string { + if b == nil { + return "" + } + return b.ServiceName +} diff --git a/common/event/reader.go b/common/event/reader.go new file mode 100644 index 000000000..f163e692e --- /dev/null +++ b/common/event/reader.go @@ -0,0 +1,209 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2025 CERN and copyright holders of ALICE O². + * Author: Piotr Konopka + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package event + +import ( + "context" + "fmt" + + "github.com/AliceO2Group/Control/common/event/topic" + "github.com/AliceO2Group/Control/common/logger/infologger" + pb "github.com/AliceO2Group/Control/common/protos" + "github.com/segmentio/kafka-go" + "github.com/spf13/viper" + "google.golang.org/protobuf/proto" +) + +// Reader interface provides methods to read events. +type Reader interface { + // Next should return the next event or cancel if the context is cancelled. + Next(ctx context.Context) (*pb.Event, error) + // Last should return the last available event currently present on the topic (or nil if none) + // or cancel if the context is cancelled. + Last(ctx context.Context) (*pb.Event, error) + Close() error +} + +// DummyReader is an implementation of Reader that returns no events. +type DummyReader struct{} + +func (*DummyReader) Next(context.Context) (*pb.Event, error) { return nil, nil } +func (*DummyReader) Last(context.Context) (*pb.Event, error) { return nil, nil } +func (*DummyReader) Close() error { return nil } + +// KafkaReader reads events from Kafka and provides a blocking, cancellable API to fetch events. +type KafkaReader struct { + *kafka.Reader + topic string + brokers []string + groupID string +} + +// NewReaderWithTopic creates a KafkaReader for the provided topic and starts it. +func NewReaderWithTopic(topic topic.Topic, groupID string) *KafkaReader { + cfg := kafka.ReaderConfig{ + Brokers: viper.GetStringSlice("kafkaEndpoints"), + Topic: string(topic), + GroupID: groupID, + MinBytes: 1, + MaxBytes: 10e7, + } + + rk := &KafkaReader{ + Reader: kafka.NewReader(cfg), + topic: string(topic), + brokers: append([]string{}, cfg.Brokers...), + groupID: groupID, + } + return rk +} + +// Next blocks until the next event is available or ctx is cancelled. +func (r *KafkaReader) Next(ctx context.Context) (*pb.Event, error) { + if r == nil { + return nil, fmt.Errorf("nil reader") + } + msg, err := r.ReadMessage(ctx) + if err != nil { + return nil, err + } + return kafkaMessageToEvent(msg) +} + +// Last fetches the last available message on the topic (considering all partitions). +// If multiple partitions have data, the event with the greatest message timestamp is returned. +func (r *KafkaReader) Last(ctx context.Context) (*pb.Event, error) { + if r == nil { + return nil, fmt.Errorf("nil reader") + } + partitions, err := r.readPartitions() + if err != nil { + return nil, err + } + var latestEvt *pb.Event + var latestEvtTimeNs int64 + for _, p := range partitions { + if p.Topic != r.topic { + continue + } + first, last, err := r.readFirstAndLast(p.ID) + if err != nil { + log.WithField(infologger.Level, infologger.IL_Devel).WithError(err). + Warnf("failed to read offsets for %s[%d]", r.topic, p.ID) + continue + } + if last <= first { + continue + } + msg, err := r.readAtOffset(ctx, p.ID, last-1) + if err != nil { + log.WithError(err). + WithField(infologger.Level, infologger.IL_Devel). + Warnf("failed to read last message for %s[%d] at offset %d", r.topic, p.ID, last-1) + continue + } + evt, err := kafkaMessageToEvent(msg) + if err != nil { + log.WithError(err). + WithField(infologger.Level, infologger.IL_Devel). + Warnf("failed to decode last message for %s[%d]", r.topic, p.ID) + continue + } + currentEvtTimeNs := msg.Time.UnixNano() + if latestEvt == nil || currentEvtTimeNs > latestEvtTimeNs { + latestEvt = evt + latestEvtTimeNs = currentEvtTimeNs + } + } + return latestEvt, nil +} + +// Close stops the reader. +func (r *KafkaReader) Close() error { + if r == nil { + return nil + } + return r.Reader.Close() +} + +func kafkaMessageToEvent(m kafka.Message) (*pb.Event, error) { + var evt pb.Event + if err := proto.Unmarshal(m.Value, &evt); err != nil { + return nil, fmt.Errorf("failed to unmarshal kafka message: %w", err) + } + return &evt, nil +} + +func (r *KafkaReader) brokerAddr() (string, error) { + if len(r.brokers) == 0 { + return "", fmt.Errorf("no kafka brokers configured") + } + return r.brokers[0], nil +} + +func (r *KafkaReader) readPartitions() ([]kafka.Partition, error) { + addr, err := r.brokerAddr() + if err != nil { + return nil, err + } + conn, err := kafka.Dial("tcp", addr) + if err != nil { + return nil, err + } + defer conn.Close() + return conn.ReadPartitions(r.topic) +} + +func (r *KafkaReader) readFirstAndLast(partition int) (int64, int64, error) { + addr, err := r.brokerAddr() + if err != nil { + return 0, 0, err + } + conn, err := kafka.DialLeader(context.Background(), "tcp", addr, r.topic, partition) + if err != nil { + return 0, 0, err + } + defer conn.Close() + first, last, err := conn.ReadOffsets() + return first, last, err +} + +func (r *KafkaReader) readAtOffset(ctx context.Context, partition int, offset int64) (kafka.Message, error) { + if offset < 0 { + return kafka.Message{}, fmt.Errorf("invalid offset %d", offset) + } + kr := kafka.NewReader(kafka.ReaderConfig{ + Brokers: append([]string{}, r.brokers...), + Topic: r.topic, + Partition: partition, + MinBytes: 1, + MaxBytes: 10e6, + }) + defer kr.Close() + if err := kr.SetOffset(offset); err != nil { + return kafka.Message{}, err + } + return kr.ReadMessage(ctx) +} diff --git a/common/event/reader_test.go b/common/event/reader_test.go new file mode 100644 index 000000000..751606fd0 --- /dev/null +++ b/common/event/reader_test.go @@ -0,0 +1,48 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2025 CERN and copyright holders of ALICE O². + * Author: Piotr Konopka + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package event + +import ( + pb "github.com/AliceO2Group/Control/common/protos" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "github.com/segmentio/kafka-go" + "google.golang.org/protobuf/proto" +) + +var _ = Describe("Reader", func() { + When("converting kafka message to event", func() { + It("unmarshals protobuf payload correctly", func() { + e := &pb.Event{Payload: &pb.Event_CoreStartEvent{CoreStartEvent: &pb.Ev_MetaEvent_CoreStart{FrameworkId: "z"}}} + b, err := proto.Marshal(e) + Expect(err).To(BeNil()) + + m := kafka.Message{Value: b} + evt, err := kafkaMessageToEvent(m) + Expect(err).To(BeNil()) + Expect(evt.GetCoreStartEvent().FrameworkId).To(Equal("z")) + }) + }) +}) diff --git a/common/event/roleevent.go b/common/event/roleevent.go new file mode 100644 index 000000000..451086bc5 --- /dev/null +++ b/common/event/roleevent.go @@ -0,0 +1,49 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2020 CERN and copyright holders of ALICE O². + * Author: Miltiadis Alexis + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package event + +type RoleEvent struct { + eventBase + Name string + Status string + State string + RolePath string +} + +func (r *RoleEvent) GetName() string { + return r.Name +} + +func (r *RoleEvent) GetStatus() string { + return r.Status +} + +func (r *RoleEvent) GetState() string { + return r.State +} + +func (r *RoleEvent) GetRolePath() string { + return r.RolePath +} diff --git a/common/event/taskevents.go b/common/event/taskevents.go new file mode 100644 index 000000000..2ed1e7780 --- /dev/null +++ b/common/event/taskevents.go @@ -0,0 +1,59 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2020 CERN and copyright holders of ALICE O². + * Author: Miltiadis Alexis + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package event + +type TaskEvent struct { + eventBase + Name string + TaskID string + Status string + State string + Hostname string + ClassName string +} + +func (r *TaskEvent) GetName() string { + return r.Name +} + +func (r *TaskEvent) GetTaskID() string { + return r.TaskID +} + +func (r *TaskEvent) GetStatus() string { + return r.Status +} + +func (r *TaskEvent) GetState() string { + return r.State +} + +func (r *TaskEvent) GetHostname() string { + return r.Hostname +} + +func (r *TaskEvent) GetClassName() string { + return r.ClassName +} diff --git a/common/event/tasksreleasedevent.go b/common/event/tasksreleasedevent.go new file mode 100644 index 000000000..b35cd524a --- /dev/null +++ b/common/event/tasksreleasedevent.go @@ -0,0 +1,75 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2020 CERN and copyright holders of ALICE O². + * Author: Teo Mrnjavac + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package event + +import ( + "github.com/AliceO2Group/Control/common/utils" + "github.com/AliceO2Group/Control/common/utils/uid" +) + +type TasksReleasedEvent struct { + eventBase + EnvironmentId uid.ID `json:"environmentId"` + TaskIdsReleased []string `json:"taskIdsReleased"` + TaskReleaseErrors map[string]error `json:"taskReleaseErrors"` +} + +func (tr *TasksReleasedEvent) GetName() string { + return "TASK_RELEASED" +} + +func (tr *TasksReleasedEvent) GetEnvironmentId() uid.ID { + if tr == nil { + return "" + } + return tr.EnvironmentId +} + +func (tr *TasksReleasedEvent) GetTaskIds() []string { + if tr == nil { + return nil + } + return tr.TaskIdsReleased +} + +func (tr *TasksReleasedEvent) GetTaskReleaseErrors() map[string]error { + if tr == nil { + return nil + } + return tr.TaskReleaseErrors +} + +func NewTasksReleasedEvent(envId uid.ID, taskIdsReleased []string, taskReleaseErrors map[string]error) (tr *TasksReleasedEvent) { + tr = &TasksReleasedEvent{ + eventBase: eventBase{ + Timestamp: utils.NewUnixTimestamp(), + MessageType: "TasksReleasedEvent", + }, + EnvironmentId: envId, + TaskIdsReleased: taskIdsReleased, + TaskReleaseErrors: taskReleaseErrors, + } + return tr +} diff --git a/common/event/taskstatechangedevent.go b/common/event/taskstatechangedevent.go new file mode 100644 index 000000000..1f757d996 --- /dev/null +++ b/common/event/taskstatechangedevent.go @@ -0,0 +1,75 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2020 CERN and copyright holders of ALICE O². + * Author: Miltiadis Alexis + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package event + +import ( + "github.com/AliceO2Group/Control/common/utils" + "github.com/AliceO2Group/Control/common/utils/uid" +) + +type TasksStateChangedEvent struct { + eventBase + EnvironmentId uid.ID `json:"environmentId"` + TaskIdsStateChanged []string `json:"taskIdsStatesChanged"` + TaskStateChangedErr error `json:"taskStateChangedErr"` +} + +func (tr *TasksStateChangedEvent) GetName() string { + return "TASK_STATE" +} + +func (tr *TasksStateChangedEvent) GetEnvironmentId() uid.ID { + if tr == nil { + return "" + } + return tr.EnvironmentId +} + +func (tr *TasksStateChangedEvent) GetTaskIds() []string { + if tr == nil { + return nil + } + return tr.TaskIdsStateChanged +} + +func (tr *TasksStateChangedEvent) GetTasksStateChangedError() error { + if tr == nil { + return nil + } + return tr.TaskStateChangedErr +} + +func NewTasksStateChangedEvent(envId uid.ID, taskIdsChangeState []string, taskStateChangedErr error) (tr *TasksStateChangedEvent) { + tr = &TasksStateChangedEvent{ + eventBase: eventBase{ + Timestamp: utils.NewUnixTimestamp(), + MessageType: "TasksStateEvent", + }, + EnvironmentId: envId, + TaskIdsStateChanged: taskIdsChangeState, + TaskStateChangedErr: taskStateChangedErr, + } + return tr +} diff --git a/common/event/topic/topic.go b/common/event/topic/topic.go new file mode 100644 index 000000000..b71936ad8 --- /dev/null +++ b/common/event/topic/topic.go @@ -0,0 +1,43 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2024 CERN and copyright holders of ALICE O². + * Author: Teo Mrnjavac + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +// Package topic defines constants and types for event system topics. +package topic + +type Topic string + +const ( + Separator = "." // used to separate topic segments + Root Topic = "aliecs" + + Run Topic = Root + Separator + "run" // currently unused, intended for detailed run information e.g. SOR, SOEOR, EOEOR + Environment Topic = Root + Separator + "environment" + Role Topic = Root + Separator + "role" // currently unused, intended for role state change events + Task Topic = Root + Separator + "task" + Call Topic = Root + Separator + "call" + + Core Topic = Root + Separator + "core" + + IntegratedService Topic = Root + Separator + "integrated_service" +) diff --git a/common/event/writer.go b/common/event/writer.go new file mode 100644 index 000000000..b0cb571a6 --- /dev/null +++ b/common/event/writer.go @@ -0,0 +1,272 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2024 CERN and copyright holders of ALICE O². + * Author: Teo Mrnjavac + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +// Package event provides event publishing and streaming functionality for +// O² Control components, supporting Kafka and dummy event writers. +package event + +import ( + "context" + "fmt" + "sync" + "time" + + "github.com/AliceO2Group/Control/common/event/topic" + "github.com/AliceO2Group/Control/common/logger" + "github.com/AliceO2Group/Control/common/logger/infologger" + "github.com/AliceO2Group/Control/common/monitoring" + pb "github.com/AliceO2Group/Control/common/protos" + "github.com/segmentio/kafka-go" + "github.com/sirupsen/logrus" + "github.com/spf13/viper" + "google.golang.org/protobuf/proto" +) + +var ( + log = logger.New(logrus.StandardLogger(), "event") + KAFKAWRITER = "kafka_writer" + KAFKAPREPARE = "kafka_prepare" + writerBatchsize = 100 +) + +type Writer interface { + WriteEvent(e interface{}) + WriteEventWithTimestamp(e interface{}, timestamp time.Time) + Close() +} + +type DummyWriter struct{} + +func (*DummyWriter) WriteEvent(interface{}) {} +func (*DummyWriter) WriteEventWithTimestamp(interface{}, time.Time) {} +func (*DummyWriter) Close() {} + +// Kafka writer is used to convert events from events.proto into kafka messages and to write them. +// it is built with 2 workers: +// +// #1 is gathering kafka.Message from any goroutine which sends message into buffered channel and puts them into FifoBuffer. +// #2 is poping any messages from FifoBuffer and sends them to Kafka +// +// The reason for this setup over setting Async: true in kafka.Writer is the ability to have some error handling +// of failed messages. Moreover if we used only one worker that gathers messages from channel and then sends them directly to Kafka, +// we would block whole core if we receive lot of messages at once. So we split functionality into two workers: one is +// putting all messages into the buffer, so if we have a lot of messages buffer just grows without blocking whole core and the +// second does all the sending. This setup allows us to gather messages from any amount of goroutines without blocking/losing messages. +// Another benefit is batching messages instead of writing them one by one. +type KafkaWriter struct { + *kafka.Writer + toBatchMessagesChan chan kafka.Message + messageBuffer FifoBuffer[kafka.Message] + // NOTE: we use settable callback in order to be able to test writing of messages via KafkaWriter, without necessity of setting up cluster + writeFunction func([]kafka.Message) + runningWorkers sync.WaitGroup + batchingLoopDoneCh chan struct{} +} + +func (w *KafkaWriter) newMetric(name string) monitoring.Metric { + metric := monitoring.NewMetric(name) + metric.AddTag("topic", w.Topic) + return metric +} + +func NewWriterWithTopic(topic topic.Topic) *KafkaWriter { + writer := &KafkaWriter{ + Writer: &kafka.Writer{ + Addr: kafka.TCP(viper.GetStringSlice("kafkaEndpoints")...), + Topic: string(topic), + Balancer: &kafka.Hash{}, + AllowAutoTopicCreation: true, + BatchTimeout: time.Millisecond, // we are batching the messages ourselves and writing sync into writer, so long timeout is not necessary + BatchSize: writerBatchsize, + }, + toBatchMessagesChan: make(chan kafka.Message, 100000), + messageBuffer: NewFifoBuffer[kafka.Message](), + runningWorkers: sync.WaitGroup{}, + batchingLoopDoneCh: make(chan struct{}, 1), + } + + writer.writeFunction = func(messages []kafka.Message) { + metric := writer.newMetric(KAFKAWRITER) + metric.SetFieldUInt64("messages_sent", uint64(len(messages))) + metric.SetFieldUInt64("messages_failed", 0) + + metricDuration := writer.newMetric(KAFKAWRITER) + defer monitoring.TimerSendHist(&metricDuration, monitoring.Nanosecond)() + + if err := writer.WriteMessages(context.Background(), messages...); err != nil { + metric.SetFieldUInt64("messages_failed", uint64(len(messages))) + log.Errorf("failed to write %d messages to kafka with error: %v", len(messages), err) + } + + monitoring.Send(&metric) + } + + go writer.writingLoop() + go writer.batchingLoop() + + return writer +} + +func (w *KafkaWriter) Close() { + if w != nil { + // We are waiting until both loops (batching and writing) are done + w.runningWorkers.Add(2) + close(w.toBatchMessagesChan) + w.runningWorkers.Wait() + err := w.Writer.Close() + if err != nil { + log.WithField(infologger.Level, infologger.IL_Devel). + Errorf("failed to close writer: %v", err) + } + } +} + +func (w *KafkaWriter) WriteEvent(e interface{}) { + if w != nil { + w.WriteEventWithTimestamp(e, time.Now()) + } +} + +func (w *KafkaWriter) writingLoop() { + for { + select { + case <-w.batchingLoopDoneCh: + w.runningWorkers.Done() + return + default: + messagesToSend := w.messageBuffer.PopMultiple(uint(writerBatchsize)) + if len(messagesToSend) == 0 { + continue + } + w.writeFunction(messagesToSend) + } + } +} + +func (w *KafkaWriter) batchingLoop() { + for message := range w.toBatchMessagesChan { + w.messageBuffer.Push(message) + } + w.batchingLoopDoneCh <- struct{}{} + w.messageBuffer.ReleaseGoroutines() + w.runningWorkers.Done() +} + +type HasEnvID interface { + GetEnvironmentId() string +} + +func extractAndConvertEnvID[T HasEnvID](object T) []byte { + envID := []byte(object.GetEnvironmentId()) + if len(envID) > 0 { + return envID + } + return nil +} + +// TODO: there should be written test to verify converting all of these messages +func internalEventToKafkaEvent(internalEvent interface{}, timestamp time.Time) (kafkaEvent *pb.Event, key []byte, err error) { + kafkaEvent = &pb.Event{ + Timestamp: timestamp.UnixMilli(), + TimestampNano: timestamp.UnixNano(), + } + + switch e := internalEvent.(type) { + case *pb.Ev_MetaEvent_CoreStart: + kafkaEvent.Payload = &pb.Event_CoreStartEvent{CoreStartEvent: e} + case *pb.Ev_MetaEvent_MesosHeartbeat: + kafkaEvent.Payload = &pb.Event_MesosHeartbeatEvent{MesosHeartbeatEvent: e} + case *pb.Ev_MetaEvent_FrameworkEvent: + kafkaEvent.Payload = &pb.Event_FrameworkEvent{FrameworkEvent: e} + case *pb.Ev_TaskEvent: + key = []byte(e.Taskid) + if len(key) == 0 { + key = nil + } + kafkaEvent.Payload = &pb.Event_TaskEvent{TaskEvent: e} + case *pb.Ev_RoleEvent: + key = extractAndConvertEnvID(e) + kafkaEvent.Payload = &pb.Event_RoleEvent{RoleEvent: e} + case *pb.Ev_EnvironmentEvent: + key = extractAndConvertEnvID(e) + kafkaEvent.Payload = &pb.Event_EnvironmentEvent{EnvironmentEvent: e} + case *pb.Ev_CallEvent: + key = extractAndConvertEnvID(e) + kafkaEvent.Payload = &pb.Event_CallEvent{CallEvent: e} + case *pb.Ev_IntegratedServiceEvent: + key = extractAndConvertEnvID(e) + kafkaEvent.Payload = &pb.Event_IntegratedServiceEvent{IntegratedServiceEvent: e} + case *pb.Ev_RunEvent: + key = extractAndConvertEnvID(e) + kafkaEvent.Payload = &pb.Event_RunEvent{RunEvent: e} + default: + err = fmt.Errorf("unsupported event type") + } + + return +} + +func kafkaEventToKafkaMessage(kafkaEvent *pb.Event, key []byte) (kafka.Message, error) { + data, err := proto.Marshal(kafkaEvent) + if err != nil { + return kafka.Message{}, fmt.Errorf("failed to marshal event: %w", err) + } + + message := kafka.Message{ + Value: data, + } + + if key != nil { + message.Key = key + } + + return message, nil +} + +func (w *KafkaWriter) WriteEventWithTimestamp(e interface{}, timestamp time.Time) { + if w == nil { + return + } + + metric := w.newMetric(KAFKAPREPARE) + + defer monitoring.TimerSendHist(&metric, monitoring.Nanosecond)() + + wrappedEvent, key, err := internalEventToKafkaEvent(e, timestamp) + if err != nil { + log.WithField("event", e). + WithField("level", infologger.IL_Support). + Errorf("Failed to convert event to kafka event: %s", err.Error()) + return + } + + message, err := kafkaEventToKafkaMessage(wrappedEvent, key) + if err != nil { + log.WithField("event", e). + WithField("level", infologger.IL_Support). + Errorf("Failed to convert kafka event to message: %s", err.Error()) + return + } + w.toBatchMessagesChan <- message +} diff --git a/common/event/writer_test.go b/common/event/writer_test.go new file mode 100644 index 000000000..1a6363a85 --- /dev/null +++ b/common/event/writer_test.go @@ -0,0 +1,68 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2025 CERN and copyright holders of ALICE O². + * Author: Teo Mrnjavac + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package event + +import ( + "sync" + + pb "github.com/AliceO2Group/Control/common/protos" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "github.com/segmentio/kafka-go" + "google.golang.org/protobuf/proto" +) + +var _ = Describe("Writer", func() { + When("event is written into writer", func() { + It("transforms it to kafka message and sends it", func() { + channel := make(chan struct{}) + writer := KafkaWriter{} + writer.toBatchMessagesChan = make(chan kafka.Message, 100) + writer.messageBuffer = NewFifoBuffer[kafka.Message]() + writer.Writer = &kafka.Writer{} + writer.Topic = "testtopic" + writer.runningWorkers = sync.WaitGroup{} + writer.batchingLoopDoneCh = make(chan struct{}, 1) + + writer.writeFunction = func(messages []kafka.Message) { + Expect(len(messages)).To(Equal(1)) + event := &pb.Event{} + err := proto.Unmarshal(messages[0].Value, event) + Expect(err).To(BeNil()) + Expect(event.GetCoreStartEvent().FrameworkId).To(Equal("FrameworkId")) + channel <- struct{}{} + } + + go writer.writingLoop() + go writer.batchingLoop() + + event := &pb.Ev_MetaEvent_CoreStart{FrameworkId: "FrameworkId"} + + writer.WriteEvent(event) + <-channel + writer.Close() + }) + }) +}) diff --git a/common/gera/map.go b/common/gera/map.go new file mode 100644 index 000000000..10084265a --- /dev/null +++ b/common/gera/map.go @@ -0,0 +1,343 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2020-2024 CERN and copyright holders of ALICE O². + * Author: Teo Mrnjavac + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +// Package gera implements a hierarchical key-value store. +// +// A gera.Map uses a map[string]interface{} as backing store, and it can wrap other gera.Map instances. +// Values in child maps override any value provided by a gera.Map that's wrapped in the hierarchy. +// +// The name is reminiscent of hiera, as in hierarchical, but it was deemed desirable to avoid future confusion with the +// Hiera KV store used by Puppet, a different product altogether, so instead of the ancient Greek root, "gera" comes +// from the Italian root instead where "hi" becomes a soft "g". +package gera + +import ( + "sync" + + "dario.cat/mergo" +) + +type Map[K comparable, V any] interface { + Wrap(m Map[K, V]) Map[K, V] + IsHierarchyRoot() bool + HierarchyContains(m Map[K, V]) bool + Unwrap() Map[K, V] + + Has(key K) bool + Len() int + + Get(key K) (V, bool) + Set(key K, value V) bool + Del(key K) bool + + // Flattened should return a flat representation of the tree in the root direction, + // where children kv pairs overwrite parent kv pairs. + Flattened() (map[K]V, error) + // FlattenedParent should return a flat representation of the tree in the root direction, + FlattenedParent() (map[K]V, error) + WrappedAndFlattened(m Map[K, V]) (map[K]V, error) + + Raw() map[K]V + Copy() Map[K, V] + RawCopy() map[K]V +} + +func MakeMap[K comparable, V any]() *WrapMap[K, V] { + return &WrapMap[K, V]{ + theMap: make(map[K]V), + parent: nil, + } +} + +func MakeMapWithMap[K comparable, V any](fromMap map[K]V) *WrapMap[K, V] { + myMap := &WrapMap[K, V]{ + theMap: fromMap, + parent: nil, + } + return myMap +} + +func FlattenStack[K comparable, V any](maps ...Map[K, V]) (flattened map[K]V, err error) { + flattenedMap := MakeMap[K, V]() + for _, oneMap := range maps { + var localFlattened map[K]V + localFlattened, err = oneMap.Flattened() + if err != nil { + return + } + flattenedMap = MakeMapWithMap(localFlattened).Wrap(flattenedMap).(*WrapMap[K, V]) + } + + flattened, err = flattenedMap.Flattened() + return +} + +func MakeMapWithMapCopy[K comparable, V any](fromMap map[K]V) *WrapMap[K, V] { + newBackingMap := make(map[K]V) + for k, v := range fromMap { + newBackingMap[k] = v + } + + return MakeMapWithMap(newBackingMap) +} + +type WrapMap[K comparable, V any] struct { + theMap map[K]V + parent Map[K, V] + + unmarshalYAML func(w Map[K, V], unmarshal func(interface{}) error) error + marshalYAML func(w Map[K, V]) (interface{}, error) + + mu sync.RWMutex +} + +func (w *WrapMap[K, V]) WithUnmarshalYAML(unmarshalYAML func(w Map[K, V], unmarshal func(interface{}) error) error) *WrapMap[K, V] { + w.unmarshalYAML = unmarshalYAML + return w +} + +func (w *WrapMap[K, V]) WithMarshalYAML(marshalYAML func(w Map[K, V]) (interface{}, error)) *WrapMap[K, V] { + w.marshalYAML = marshalYAML + return w +} + +func (w *WrapMap[K, V]) UnmarshalYAML(unmarshal func(interface{}) error) error { + if w.unmarshalYAML != nil { + return w.unmarshalYAML(w, unmarshal) + } + + m := make(map[K]V) + err := unmarshal(&m) + if err == nil { + *w = WrapMap[K, V]{ + theMap: m, + parent: nil, + } + } + return err +} + +func (w *WrapMap[K, V]) MarshalYAML() (interface{}, error) { + if w.marshalYAML != nil { + return w.marshalYAML(w) + } + + return w.theMap, nil +} + +func (w *WrapMap[K, V]) IsHierarchyRoot() bool { + if w == nil || w.parent != nil { + return false + } + return true +} + +func (w *WrapMap[K, V]) HierarchyContains(m Map[K, V]) bool { + if w == nil || w.parent == nil { + return false + } + if w == m { + return true + } + if w.parent == m { + return true + } + return w.parent.HierarchyContains(m) +} + +// Wraps this map around the gera.Map m, which becomes the new parent. +// Returns a pointer to the composite map (i.e. to itself in its new state). +func (w *WrapMap[K, V]) Wrap(m Map[K, V]) Map[K, V] { + if w == nil { + return nil + } + w.parent = m + return w +} + +// Unwraps this map from its parent. +// Returns a pointer to the former parent which was just unwrapped. +func (w *WrapMap[K, V]) Unwrap() Map[K, V] { + if w == nil { + return nil + } + p := w.parent + w.parent = nil + return p +} + +func (w *WrapMap[K, V]) Get(key K) (value V, ok bool) { + if w == nil || w.theMap == nil { + return value, false + } + + w.mu.RLock() + defer w.mu.RUnlock() + + if val, ok := w.theMap[key]; ok { + return val, true + } + if w.parent != nil { + return w.parent.Get(key) + } + return value, false +} + +func (w *WrapMap[K, V]) Set(key K, value V) (ok bool) { + if w == nil || w.theMap == nil { + return false + } + + w.mu.Lock() + defer w.mu.Unlock() + + w.theMap[key] = value + return true +} + +func (w *WrapMap[K, V]) Del(key K) (ok bool) { + if w == nil || w.theMap == nil { + return false + } + + w.mu.Lock() + defer w.mu.Unlock() + + if _, exists := w.theMap[key]; exists { + delete(w.theMap, key) + } + return true +} + +func (w *WrapMap[K, V]) Has(key K) bool { + _, ok := w.Get(key) + return ok +} + +func (w *WrapMap[K, V]) Len() int { + if w == nil || w.theMap == nil { + return 0 + } + flattened, err := w.Flattened() + if err != nil { + return 0 + } + return len(flattened) +} + +func (w *WrapMap[K, V]) Flattened() (map[K]V, error) { + if w == nil { + return nil, nil + } + + w.mu.RLock() + defer w.mu.RUnlock() + + thisMapCopy := make(map[K]V) + for k, v := range w.theMap { + thisMapCopy[k] = v + } + if w.parent == nil { + return thisMapCopy, nil + } + + flattenedParent, err := w.parent.Flattened() + if err != nil { + return thisMapCopy, err + } + + err = mergo.Merge(&flattenedParent, thisMapCopy, mergo.WithOverride) + return flattenedParent, err +} + +func (w *WrapMap[K, V]) FlattenedParent() (map[K]V, error) { + if w == nil { + return nil, nil + } + + if w.parent == nil { + return make(map[K]V), nil + } + + return w.parent.Flattened() +} + +func (w *WrapMap[K, V]) WrappedAndFlattened(m Map[K, V]) (map[K]V, error) { + if w == nil { + return nil, nil + } + + w.mu.RLock() + + thisMapCopy := make(map[K]V) + for k, v := range w.theMap { + thisMapCopy[k] = v + } + + w.mu.RUnlock() + + if m == nil { + return thisMapCopy, nil + } + + flattenedM, err := m.Flattened() + if err != nil { + return thisMapCopy, err + } + + err = mergo.Merge(&flattenedM, thisMapCopy, mergo.WithOverride) + return flattenedM, err +} + +func (w *WrapMap[K, V]) Raw() map[K]V { // allows unmutexed access to map, can be unsafe! + if w == nil { + return nil + } + return w.theMap +} + +func (w *WrapMap[K, V]) Copy() Map[K, V] { + if w == nil { + return nil + } + + w.mu.RLock() + defer w.mu.RUnlock() + + newMap := &WrapMap[K, V]{ + theMap: make(map[K]V, len(w.theMap)), + parent: w.parent, + } + for k, v := range w.theMap { + newMap.theMap[k] = v + } + return newMap +} + +func (w *WrapMap[K, V]) RawCopy() map[K]V { // always safe + if w == nil { + return nil + } + return w.Copy().Raw() +} diff --git a/common/gera/map_test.go b/common/gera/map_test.go new file mode 100644 index 000000000..187c7446e --- /dev/null +++ b/common/gera/map_test.go @@ -0,0 +1,626 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2020-2024 CERN and copyright holders of ALICE O². + * Author: Teo Mrnjavac + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package gera + +import ( + "maps" + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "gopkg.in/yaml.v3" +) + +var _ = Describe("hierarchical key-value store", func() { + const ( + testPayloadDefaultsYAML1 = ` +############################### +# General Configuration Panel +############################### +dcs_enabled: !public + value: "false" + type: bool + label: "DCS" + description: "Enable/disable DCS SOR/EOR commands" + widget: checkBox + panel: General_Configuration + index: 0 +dd_enabled: !public + value: "true" + type: bool + label: "Data Distribution (FLP)" + description: "Enable/disable Data Distribution components running on FLPs (StfBuilder and StfSender)" + widget: checkBox + panel: General_Configuration + index: 1 +pdp_workflow_parameters: !public + value: "QC,CALIB,GPU,CTF,EVENT_DISPLAY" + type: string + label: "Workflow parameters" + description: "Comma-separated list of workflow parameters. Valid parameters are: QC,CALIB,GPU,CTF,EVENT_DISPLAY." + widget: editBox + panel: "EPNs_Workflows" + index: 603 + rows: 10 +user: flp +extra_env_vars: "" +` + testPayloadDefaultsYAML2 = ` +detector: "" +dpl_workflow: "{{ util.PrefixedOverride( 'dpl_workflow', 'ctp' ) }}" +dpl_command: "{{ util.PrefixedOverride( 'dpl_command', 'ctp' ) }}" +stfs_shm_segment_size: "{{ ctp_stfs_shm_segment_size }}" +it: "{{ ctp_readout_host }}" +user: "epn" +` + testPayloadVarsYAML1 = ` +auto_stop_enabled: "{{ auto_stop_timeout != 'none' }}" +ddsched_enabled: "{{ epn_enabled == 'true' && dd_enabled == 'true' }}" +odc_enabled: "{{ epn_enabled }}" +odc_topology_fullname: '{{ epn_enabled == "true" ? odc.GenerateEPNTopologyFullname() : "" }}' +` + testPayloadVarsYAML2 = ` +detector: "{{ctp_readout_enabled == 'true' ? inventory.DetectorForHost( ctp_readout_host ) : \"\" }}" +# dpl_workflow is set to ctp_dpl_workflow +dpl_workflow: "12345" +dpl_command: "" +stfs_shm_segment_size: "{{ ctp_stfs_shm_segment_size }}" +` + testPayloadUserVarsYAML1 = ` +ccdb_enabled: "true" +ccdb_host: "" +dd_enabled: "false" +pdp_workflow_parameters: "" +` + ) + + var ( + stringMap *WrapMap[string, string] + err error + ) + + customUnmarshalYAML := func(w Map[string, string], unmarshal func(interface{}) error) error { + nodes := make(map[string]yaml.Node) + err := unmarshal(&nodes) + if err == nil { + m := make(map[string]string) + for k, v := range nodes { + if v.Kind == yaml.ScalarNode { + m[k] = v.Value + } else if v.Kind == yaml.MappingNode && v.Tag == "!public" { + type auxType struct { + Value string + } + var aux auxType + err = v.Decode(&aux) + if err != nil { + continue + } + m[k] = aux.Value + } + } + + wPtr := w.(*WrapMap[string, string]) + *wPtr = WrapMap[string, string]{ + theMap: m, + parent: nil, + } + } else { + wPtr := w.(*WrapMap[string, string]) + *wPtr = WrapMap[string, string]{ + theMap: make(map[string]string), + parent: nil, + } + } + return err + } + + stringMap = MakeMap[string, string]() + + When("unmarshaling a YAML document into an empty Map", func() { + BeforeEach(func() { + stringMap = MakeMap[string, string]() + Expect(stringMap).NotTo(BeNil()) + }) + It("should unmarshal correctly with the default unmarshaler if the YAML document is a flat key-value map", func() { + err = yaml.Unmarshal([]byte(testPayloadDefaultsYAML2), stringMap) + Expect(err).NotTo(HaveOccurred()) + Expect(stringMap).NotTo(BeNil()) + value, ok := stringMap.Get("odc_enabled") + Expect(value).To(BeEmpty()) + Expect(ok).NotTo(BeTrue()) + value, ok = stringMap.Get("detector") + Expect(ok).To(BeTrue()) + Expect(value).To(Equal("")) + value, ok = stringMap.Get("dpl_workflow") + Expect(ok).To(BeTrue()) + Expect(value).To(Equal("{{ util.PrefixedOverride( 'dpl_workflow', 'ctp' ) }}")) + }) + It("should fail to unmarshal with the default unmarshaler if the YAML document is a tree with YAML tags", func() { + err = yaml.Unmarshal([]byte(testPayloadDefaultsYAML1), stringMap) + Expect(err).To(HaveOccurred()) + }) + It("should unmarshal correctly with a custom unmarshaler if the YAML document is a flat key-value map", func() { + stringMap = stringMap.WithUnmarshalYAML(customUnmarshalYAML) + err = yaml.Unmarshal([]byte(testPayloadDefaultsYAML2), stringMap) + Expect(err).NotTo(HaveOccurred()) + Expect(stringMap).NotTo(BeNil()) + value, ok := stringMap.Get("odc_enabled") + Expect(value).To(BeEmpty()) + Expect(ok).NotTo(BeTrue()) + value, ok = stringMap.Get("detector") + Expect(ok).To(BeTrue()) + Expect(value).To(Equal("")) + value, ok = stringMap.Get("dpl_workflow") + Expect(ok).To(BeTrue()) + Expect(value).To(Equal("{{ util.PrefixedOverride( 'dpl_workflow', 'ctp' ) }}")) + }) + It("should unmarshal correctly with a custom unmarshaler if the YAML document is a tree with YAML tags", func() { + stringMap = stringMap.WithUnmarshalYAML(customUnmarshalYAML) + err = yaml.Unmarshal([]byte(testPayloadDefaultsYAML1), stringMap) + Expect(err).NotTo(HaveOccurred()) + Expect(stringMap).NotTo(BeNil()) + value, ok := stringMap.Get("odc_enabled") + Expect(value).To(BeEmpty()) + Expect(ok).NotTo(BeTrue()) + value, ok = stringMap.Get("dcs_enabled") + Expect(ok).To(BeTrue()) + Expect(value).To(Equal("false")) + value, ok = stringMap.Get("dd_enabled") + Expect(ok).To(BeTrue()) + Expect(value).To(Equal("true")) + }) + }) + When("marshaling into a YAML document from a Map", func() { + BeforeEach(func() { + stringMap = MakeMap[string, string]() + Expect(stringMap).NotTo(BeNil()) + err = yaml.Unmarshal([]byte(testPayloadDefaultsYAML2), stringMap) + Expect(err).NotTo(HaveOccurred()) + Expect(stringMap).NotTo(BeNil()) + }) + + It("should marshal correctly", func() { + marshaledYAML, err := yaml.Marshal(stringMap) + Expect(err).NotTo(HaveOccurred()) + Expect(marshaledYAML).NotTo(BeEmpty()) + + reUnmarshaledMap := make(map[string]string) + err = yaml.Unmarshal(marshaledYAML, &reUnmarshaledMap) + Expect(err).NotTo(HaveOccurred()) + Expect(reUnmarshaledMap).NotTo(BeEmpty()) + + unmarshaledTestPayloadDefaultsYAML2 := make(map[string]string) + err = yaml.Unmarshal([]byte(testPayloadDefaultsYAML2), &unmarshaledTestPayloadDefaultsYAML2) + Expect(err).NotTo(HaveOccurred()) + Expect(unmarshaledTestPayloadDefaultsYAML2).NotTo(BeEmpty()) + Expect(maps.Equal(reUnmarshaledMap, unmarshaledTestPayloadDefaultsYAML2)).To(BeTrue()) + }) + }) + When("accessing the structure's underlying map", func() { + BeforeEach(func() { + stringMap = MakeMap[string, string]() + Expect(stringMap).NotTo(BeNil()) + err = yaml.Unmarshal([]byte(testPayloadDefaultsYAML2), stringMap) + Expect(err).NotTo(HaveOccurred()) + Expect(stringMap).NotTo(BeNil()) + }) + + It("should correctly return a reference to the raw map", func() { + rawMap := stringMap.Raw() + Expect(rawMap).NotTo(BeNil()) + Expect(len(rawMap)).To(Equal(6)) + value, ok := rawMap["odc_enabled"] + Expect(ok).NotTo(BeTrue()) + value, ok = rawMap["detector"] + Expect(ok).To(BeTrue()) + Expect(value).To(Equal("")) + value, ok = rawMap["dpl_workflow"] + Expect(ok).To(BeTrue()) + Expect(value).To(Equal("{{ util.PrefixedOverride( 'dpl_workflow', 'ctp' ) }}")) + value2, ok := stringMap.Get("dpl_workflow") + Expect(ok).To(BeTrue()) + Expect(value2).To(Equal(value)) + rawMap["detector"] = "test" + value, ok = stringMap.Get("detector") + Expect(ok).To(BeTrue()) + Expect(value).To(Equal("test")) + }) + + It("should correctly return a copy of the whole structure", func() { + mapCopy := stringMap.Copy() + Expect(mapCopy).NotTo(BeNil()) + Expect(mapCopy == stringMap).NotTo(BeTrue()) + Expect(mapCopy).To(Equal(stringMap)) + Expect(mapCopy.Raw()).To(Equal(stringMap.Raw())) + Expect(mapCopy.Len()).To(Equal(stringMap.Len())) + mapCopy.Set("detector", "test") + Expect(mapCopy).NotTo(Equal(stringMap)) + Expect(mapCopy.Raw()).NotTo(Equal(stringMap.Raw())) + Expect(mapCopy.Len()).To(Equal(stringMap.Len())) + mapCopy.Set("detector2", "test2") + Expect(mapCopy.Len()).To(Equal(stringMap.Len() + 1)) + }) + + It("should correctly return a copy of the underlying map", func() { + rawCopy := stringMap.RawCopy() + Expect(rawCopy).NotTo(BeNil()) + Expect(len(rawCopy)).To(Equal(6)) + rawCopy["detector"] = "test" + value, ok := stringMap.Get("detector") + Expect(ok).To(BeTrue()) + Expect(value).To(Equal("")) + }) + }) + When("wrapping and unwrapping maps", func() { + defaults1 := MakeMap[string, string]() + Expect(defaults1).NotTo(BeNil()) + + defaults1 = defaults1.WithUnmarshalYAML(customUnmarshalYAML) + err = yaml.Unmarshal([]byte(testPayloadDefaultsYAML1), defaults1) + Expect(err).NotTo(HaveOccurred()) + Expect(defaults1).NotTo(BeNil()) + + defaults2 := MakeMap[string, string]() + Expect(defaults2).NotTo(BeNil()) + + err = yaml.Unmarshal([]byte(testPayloadDefaultsYAML2), defaults2) + Expect(err).NotTo(HaveOccurred()) + Expect(defaults2).NotTo(BeNil()) + + vars1 := MakeMap[string, string]() + Expect(vars1).NotTo(BeNil()) + + err = yaml.Unmarshal([]byte(testPayloadVarsYAML1), vars1) + Expect(err).NotTo(HaveOccurred()) + Expect(vars1).NotTo(BeNil()) + + vars2 := MakeMap[string, string]() + Expect(vars2).NotTo(BeNil()) + + err = yaml.Unmarshal([]byte(testPayloadVarsYAML2), vars2) + Expect(err).NotTo(HaveOccurred()) + Expect(vars2).NotTo(BeNil()) + + userVars1 := MakeMap[string, string]() + Expect(userVars1).NotTo(BeNil()) + + err = yaml.Unmarshal([]byte(testPayloadUserVarsYAML1), userVars1) + Expect(err).NotTo(HaveOccurred()) + Expect(userVars1).NotTo(BeNil()) + + var wrappedDefaults, wrappedVars, wrappedUserVars, wrappedAll Map[string, string] + + It("should wrap correctly", func() { + wrappedDefaults = defaults2.Wrap(defaults1) + Expect(wrappedDefaults).NotTo(BeNil()) + Expect(wrappedDefaults).To(Equal(defaults2)) + + wrappedVars = vars2.Wrap(vars1) + Expect(wrappedVars).NotTo(BeNil()) + Expect(wrappedVars).To(Equal(vars2)) + + wrappedUserVars = userVars1 + }) + + It("should unwrap correctly", func() { + unwrapped := vars2.Unwrap() + Expect(unwrapped).NotTo(BeNil()) + Expect(unwrapped).To(Equal(vars1)) + + wrappedVars = vars2.Wrap(vars1) + Expect(wrappedVars).NotTo(BeNil()) + }) + + var flattenedDefaults, flattenedVars, flattenedUserVars, flattenedAll map[string]string + + It("should flatten correctly", func() { + flattenedDefaults, err = wrappedDefaults.Flattened() + Expect(err).NotTo(HaveOccurred()) + Expect(flattenedDefaults).To(Equal(map[string]string{ + "detector": "", + "dpl_workflow": "{{ util.PrefixedOverride( 'dpl_workflow', 'ctp' ) }}", + "dpl_command": "{{ util.PrefixedOverride( 'dpl_command', 'ctp' ) }}", + "stfs_shm_segment_size": "{{ ctp_stfs_shm_segment_size }}", + "it": "{{ ctp_readout_host }}", + "dcs_enabled": "false", + "dd_enabled": "true", + "pdp_workflow_parameters": "QC,CALIB,GPU,CTF,EVENT_DISPLAY", + "user": "epn", + "extra_env_vars": "", + })) + + flattenedVars, err = wrappedVars.Flattened() + Expect(err).NotTo(HaveOccurred()) + Expect(flattenedVars).To(Equal(map[string]string{ + "detector": "{{ctp_readout_enabled == 'true' ? inventory.DetectorForHost( ctp_readout_host ) : \"\" }}", + "dpl_workflow": "12345", + "dpl_command": "", + "stfs_shm_segment_size": "{{ ctp_stfs_shm_segment_size }}", + "auto_stop_enabled": "{{ auto_stop_timeout != 'none' }}", + "ddsched_enabled": "{{ epn_enabled == 'true' && dd_enabled == 'true' }}", + "odc_enabled": "{{ epn_enabled }}", + "odc_topology_fullname": "{{ epn_enabled == \"true\" ? odc.GenerateEPNTopologyFullname() : \"\" }}", + })) + + flattenedUserVars, err = wrappedUserVars.Flattened() + Expect(err).NotTo(HaveOccurred()) + }) + + It("should wrap correctly from previously flattened maps", func() { + wrappedAll = MakeMapWithMap(flattenedUserVars).Wrap(MakeMapWithMap(flattenedVars).Wrap(MakeMapWithMap(flattenedDefaults))) + Expect(wrappedAll).NotTo(BeNil()) + flattenedAll, err = wrappedAll.Flattened() + Expect(err).NotTo(HaveOccurred()) + + Expect(flattenedAll).To(Equal(map[string]string{ + "detector": "{{ctp_readout_enabled == 'true' ? inventory.DetectorForHost( ctp_readout_host ) : \"\" }}", + "dpl_workflow": "12345", + "dpl_command": "", + "stfs_shm_segment_size": "{{ ctp_stfs_shm_segment_size }}", + "it": "{{ ctp_readout_host }}", + "auto_stop_enabled": "{{ auto_stop_timeout != 'none' }}", + "ddsched_enabled": "{{ epn_enabled == 'true' && dd_enabled == 'true' }}", + "odc_enabled": "{{ epn_enabled }}", + "odc_topology_fullname": "{{ epn_enabled == \"true\" ? odc.GenerateEPNTopologyFullname() : \"\" }}", + "dcs_enabled": "false", + "dd_enabled": "false", + "pdp_workflow_parameters": "", + "user": "epn", + "extra_env_vars": "", + "ccdb_enabled": "true", + "ccdb_host": "", + })) + }) + + It("should flatten stack correctly", func() { + flattenedStack, err := FlattenStack(wrappedDefaults, wrappedVars, wrappedUserVars) + Expect(err).NotTo(HaveOccurred()) + Expect(flattenedAll).To(Equal(flattenedStack)) + }) + + It("should flatten parent correctly", func() { + flattenedParent, err := wrappedAll.FlattenedParent() + Expect(err).NotTo(HaveOccurred()) + + wrappedParents := MakeMapWithMap(flattenedVars).Wrap(MakeMapWithMap(flattenedDefaults)) + flattenedWrappedParents, err := wrappedParents.Flattened() + Expect(flattenedParent).To(Equal(flattenedWrappedParents)) + }) + + It("should wrap and flatten correctly", func() { + flattenedParent, err := wrappedAll.FlattenedParent() + Expect(err).NotTo(HaveOccurred()) + + wrappedAndFlattenedParents, err := MakeMapWithMap(flattenedVars).WrappedAndFlattened(MakeMapWithMap(flattenedDefaults)) + Expect(err).NotTo(HaveOccurred()) + + Expect(wrappedAndFlattenedParents).To(Equal(flattenedParent)) + }) + + It("should correctly look into its hierarchy", func() { + ok := wrappedDefaults.HierarchyContains(defaults1) + Expect(ok).To(BeTrue()) + + ok = wrappedDefaults.HierarchyContains(defaults2) + Expect(ok).To(BeTrue()) + + ok = wrappedDefaults.HierarchyContains(vars1) + Expect(ok).NotTo(BeTrue()) + + ok = wrappedDefaults.IsHierarchyRoot() + Expect(ok).To(BeFalse()) + + ok = defaults2.IsHierarchyRoot() + Expect(ok).To(BeFalse()) + + ok = defaults1.IsHierarchyRoot() + Expect(ok).To(BeTrue()) + + ok = userVars1.IsHierarchyRoot() + Expect(ok).To(BeTrue()) + }) + + It("should correctly perform KV Has operation", func() { + Expect(wrappedAll.Has("odc_enabled")).To(BeTrue()) + Expect(wrappedAll.Has("ccdb_host")).To(BeTrue()) + Expect(wrappedAll.Has("detector")).To(BeTrue()) + Expect(wrappedAll.Has("it")).To(BeTrue()) + Expect(wrappedAll.Has("pdp_workflow_parameters")).To(BeTrue()) + Expect(wrappedAll.Has("invalid_key")).To(BeFalse()) + Expect(wrappedAll.Has("")).To(BeFalse()) + }) + + It("should correctly perform KV Len operation", func() { + Expect(defaults1.Len()).To(Equal(5)) + Expect(len(defaults2.Raw())).To(Equal(6)) + Expect(defaults2.Len()).To(Equal(10)) + Expect(wrappedVars.Len()).To(Equal(8)) + Expect(wrappedAll.Len()).To(Equal(16)) + }) + + It("should correctly perform KV Get operations", func() { + value, ok := wrappedAll.Get("odc_enabled") + Expect(ok).To(BeTrue()) + Expect(value).To(Equal("{{ epn_enabled }}")) + + value, ok = wrappedAll.Get("detector") + Expect(ok).To(BeTrue()) + Expect(value).To(Equal("{{ctp_readout_enabled == 'true' ? inventory.DetectorForHost( ctp_readout_host ) : \"\" }}")) + + value, ok = wrappedAll.Get("dpl_workflow") + Expect(ok).To(BeTrue()) + Expect(value).To(Equal("12345")) + + value, ok = wrappedAll.Get("ccdb_enabled") + Expect(ok).To(BeTrue()) + Expect(value).To(Equal("true")) + + value, ok = wrappedAll.Get("ccdb_host") + Expect(ok).To(BeTrue()) + Expect(value).To(Equal("")) + + value, ok = defaults1.Get("it") + Expect(ok).To(BeFalse()) + Expect(value).To(BeEmpty()) + + value, ok = defaults1.Get("pdp_workflow_parameters") + Expect(ok).To(BeTrue()) + Expect(value).To(Equal("QC,CALIB,GPU,CTF,EVENT_DISPLAY")) + + value, ok = wrappedAll.Get("pdp_workflow_parameters") + Expect(ok).To(BeTrue()) + Expect(value).To(Equal("")) + }) + + It("should correctly perform KV Set operations", func() { + ok := userVars1.Set("detector", "") + Expect(ok).To(BeTrue()) + + ok = userVars1.Set("dpl_workflow", "someValue") + Expect(ok).To(BeTrue()) + + ok = defaults2.Set("user", "someone") + Expect(ok).To(BeTrue()) + + ok = defaults2.Set("dpl_command", "someCommand") + Expect(ok).To(BeTrue()) + + // We need to reflatten after setting new values + flattenedDefaults, err = wrappedDefaults.Flattened() + Expect(err).NotTo(HaveOccurred()) + + flattenedVars, err = wrappedVars.Flattened() + Expect(err).NotTo(HaveOccurred()) + + flattenedUserVars, err = wrappedUserVars.Flattened() + Expect(err).NotTo(HaveOccurred()) + + wrappedAll = MakeMapWithMap(flattenedUserVars).Wrap(MakeMapWithMap(flattenedVars).Wrap(MakeMapWithMap(flattenedDefaults))) + + value, ok := wrappedAll.Get("detector") + Expect(ok).To(BeTrue()) + Expect(value).To(BeEmpty()) + + value, ok = wrappedAll.Get("dpl_workflow") + Expect(ok).To(BeTrue()) + Expect(value).To(Equal("someValue")) + + value, ok = wrappedAll.Get("user") + Expect(ok).To(BeTrue()) + Expect(value).To(Equal("someone")) + + value, ok = wrappedAll.Get("dpl_command") + Expect(ok).To(BeTrue()) + Expect(value).To(Equal("")) + + flattenedStack, err := FlattenStack(wrappedDefaults, wrappedVars, wrappedUserVars) + Expect(err).NotTo(HaveOccurred()) + + // ouch!!! this is a bug in the original code + value, ok = flattenedStack["detector"] + Expect(ok).To(BeTrue()) + Expect(value).To(BeEmpty()) + + value, ok = flattenedStack["dpl_workflow"] + Expect(ok).To(BeTrue()) + Expect(value).To(Equal("someValue")) + + value, ok = flattenedStack["user"] + Expect(ok).To(BeTrue()) + Expect(value).To(Equal("someone")) + + value, ok = flattenedStack["dpl_command"] + Expect(ok).To(BeTrue()) + Expect(value).To(Equal("")) + }) + + It("should correctly perform KV Del operations", func() { + ok := userVars1.Del("detector") + Expect(ok).To(BeTrue()) + + ok = userVars1.Del("dpl_workflow") + Expect(ok).To(BeTrue()) + + ok = defaults2.Del("user") + Expect(ok).To(BeTrue()) + + ok = defaults2.Del("dpl_command") + Expect(ok).To(BeTrue()) + + // We need to reflatten after deleting values + flattenedDefaults, err = wrappedDefaults.Flattened() + Expect(err).NotTo(HaveOccurred()) + + flattenedVars, err = wrappedVars.Flattened() + Expect(err).NotTo(HaveOccurred()) + + flattenedUserVars, err = wrappedUserVars.Flattened() + Expect(err).NotTo(HaveOccurred()) + + wrappedAll = MakeMapWithMap(flattenedUserVars).Wrap(MakeMapWithMap(flattenedVars).Wrap(MakeMapWithMap(flattenedDefaults))) + + value, ok := wrappedAll.Get("detector") + Expect(ok).To(BeTrue()) + Expect(value).To(Equal("{{ctp_readout_enabled == 'true' ? inventory.DetectorForHost( ctp_readout_host ) : \"\" }}")) + + value, ok = wrappedAll.Get("dpl_workflow") + Expect(ok).To(BeTrue()) + Expect(value).To(Equal("12345")) + + value, ok = wrappedAll.Get("user") + Expect(ok).To(BeTrue()) + Expect(value).To(Equal("flp")) + + value, ok = wrappedAll.Get("dpl_command") + Expect(ok).To(BeTrue()) + Expect(value).To(Equal("")) + + flattenedStack, err := FlattenStack(wrappedDefaults, wrappedVars, wrappedUserVars) + Expect(err).NotTo(HaveOccurred()) + + value, ok = flattenedStack["detector"] + Expect(ok).To(BeTrue()) + Expect(value).To(Equal("{{ctp_readout_enabled == 'true' ? inventory.DetectorForHost( ctp_readout_host ) : \"\" }}")) + + value, ok = flattenedStack["dpl_workflow"] + Expect(ok).To(BeTrue()) + Expect(value).To(Equal("12345")) + + value, ok = flattenedStack["user"] + Expect(ok).To(BeTrue()) + Expect(value).To(Equal("flp")) + + value, ok = flattenedStack["dpl_command"] + Expect(ok).To(BeTrue()) + Expect(value).To(Equal("")) + }) + }) +}) + +func TestMap(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Hierarchical Map Test Suite") +} diff --git a/common/golangmetrics/metrics.go b/common/golangmetrics/metrics.go new file mode 100644 index 000000000..855faeed7 --- /dev/null +++ b/common/golangmetrics/metrics.go @@ -0,0 +1,104 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2025 CERN and copyright holders of ALICE O². + * Author: Michal Tichak + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +// Package golangmetrics provides Go runtime metrics collection and reporting +// functionality for monitoring system resource usage and performance. +package golangmetrics + +import ( + internalmetrics "runtime/metrics" + "time" + + "github.com/AliceO2Group/Control/common/logger" + "github.com/AliceO2Group/Control/common/logger/infologger" + "github.com/AliceO2Group/Control/common/monitoring" + "github.com/sirupsen/logrus" +) + +var ( + endRequestChannel chan struct{} + log = logger.New(logrus.StandardLogger(), "ecsmetrics") +) + +func gather() monitoring.Metric { + samples := []internalmetrics.Sample{ + {Name: "/gc/cycles/total:gc-cycles"}, + {Name: "/memory/classes/other:bytes"}, + {Name: "/memory/classes/total:bytes"}, + {Name: "/sched/goroutines:goroutines"}, + {Name: "/sync/mutex/wait/total:seconds"}, + {Name: "/memory/classes/other:bytes"}, + {Name: "/memory/classes/total:bytes"}, + {Name: "/memory/classes/heap/free:bytes"}, + {Name: "/memory/classes/heap/objects:bytes"}, + {Name: "/memory/classes/heap/released:bytes"}, + {Name: "/memory/classes/heap/stacks:bytes"}, + {Name: "/memory/classes/heap/unused:bytes"}, + } + + internalmetrics.Read(samples) + + metric := monitoring.NewMetric("golangruntimemetrics") + + for _, sample := range samples { + switch sample.Value.Kind() { + case internalmetrics.KindUint64: + metric.SetFieldUInt64(sample.Name, sample.Value.Uint64()) + case internalmetrics.KindFloat64: + metric.SetFieldFloat64(sample.Name, sample.Value.Float64()) + case internalmetrics.KindFloat64Histogram: + log.WithField("level", infologger.IL_Devel).Warningf("Error: Histogram is not supported yet for metric [%s]", sample.Name) + continue + default: + log.WithField("level", infologger.IL_Devel).Warningf("Unsupported kind %v for metric %s\n", sample.Value.Kind(), sample.Name) + continue + } + } + return metric +} + +func Start(period time.Duration) { + log.WithField("level", infologger.IL_Devel).Info("Starting golang metrics reporting") + go func() { + log.Debug("Starting golang metrics goroutine") + for { + select { + case <-endRequestChannel: + log.Debug("ending golang metrics") + endRequestChannel <- struct{}{} + return + default: + log.Debug("sending golang metrics") + metric := gather() + monitoring.Send(&metric) + time.Sleep(period) + } + } + }() +} + +func Stop() { + endRequestChannel <- struct{}{} + <-endRequestChannel +} diff --git a/common/labels.go b/common/labels.go new file mode 100644 index 000000000..0a1381c18 --- /dev/null +++ b/common/labels.go @@ -0,0 +1,54 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2022 CERN and copyright holders of ALICE O². + * Author: Teo Mrnjavac + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package common + +import "github.com/AliceO2Group/Control/common/utils/uid" + +type Labeler interface { + GetLabels() map[string]string +} + +func GetEnvironmentIdFromLabelerType(labeler Labeler) uid.ID { + if labeler != nil && len(labeler.GetLabels()) > 0 { + envIdS, ok := labeler.GetLabels()["environmentId"] + if ok { + envId, err := uid.FromString(envIdS) + if err == nil { + return envId + } + } + } + return uid.NilID() +} + +func GetValueFromLabelerType(labeler Labeler, key string) string { + if labeler != nil && len(labeler.GetLabels()) > 0 { + valueStr, ok := labeler.GetLabels()[key] + if ok { + return valueStr + } + } + return "" +} diff --git a/common/logger/infologger/directhook.go b/common/logger/infologger/directhook.go new file mode 100644 index 000000000..6f78e2473 --- /dev/null +++ b/common/logger/infologger/directhook.go @@ -0,0 +1,293 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2019 CERN and copyright holders of ALICE O². + * Author: Teo Mrnjavac + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package infologger + +import ( + "errors" + "fmt" + "net" + "os" + "os/user" + "regexp" + "runtime" + "sort" + "strconv" + "strings" + + "github.com/AliceO2Group/Control/common/utils" + "github.com/sirupsen/logrus" + "github.com/spf13/viper" +) + +const INFOLOGGER_MAX_MESSAGE_SIZE = 1024 + +var ( + hostname string + Pid string + username string + + logILInfoLevel = []logrus.Level{ + logrus.PanicLevel, + logrus.FatalLevel, + logrus.ErrorLevel, + logrus.WarnLevel, + logrus.InfoLevel, + } + logILAllLevel = logrus.AllLevels + currentIlLevel = logILInfoLevel +) + +func setCurrentILLevelFromViper() { + if viper.GetBool("logAllIL") { + currentIlLevel = logILAllLevel + } else { + currentIlLevel = logILInfoLevel + } +} + +var lineBreaksRe = regexp.MustCompile(`\r?\n`) + +func init() { + var err error + Pid = fmt.Sprintf("%d", os.Getpid()) + + unixUser, _ := user.Current() + if unixUser != nil { + username = unixUser.Username + } + + hostname, err = os.Hostname() + if err != nil { + return + } + + // We only take the short hostname + hostname = strings.Split(hostname, ".")[0] +} + +type sender struct { + stream net.Conn +} + +func newSender(path string) *sender { + stream, err := net.Dial("unix", path) + if err != nil { + fmt.Printf("cannot dial unix socket %s\n", path) + return nil + } + return &sender{ + stream: stream, + } +} + +func (s *sender) Close() error { + return s.stream.Close() +} + +func (s *sender) format(fields map[string]string, version protoVersion) string { + stringLog := string("*" + version) + currentProtocol := protocols[version] + for _, fSpec := range *currentProtocol { + stringLog += "#" + if value, ok := fields[fSpec.name]; ok { + stringLog += value + } + } + + // We sanitize away all linebreaks from the payload, and then we append one. + // This is necessary because IL treats \n as a message terminator character. + stringLog = lineBreaksRe.ReplaceAllString(stringLog, " ") + + return stringLog + "\n" +} + +func (s *sender) Send(fields map[string]string) error { + if s.stream != nil { + _, err := s.stream.Write([]byte(s.format(fields, v14))) + return err + } + return errors.New("could not send log message: InfoLogger socket not available") +} + +type DirectHook struct { + il *sender + system string + facility string + role string +} + +func paddedAbstractSocket(name string) string { + const targetLen = 108 // Linux constant + out := make([]byte, targetLen) + for i := 0; i < targetLen; i++ { + if i < len(name) { + out[i] = name[i] + } else { + out[i] = '\x00' + } + } + return string(out) +} + +func guessSocketPath() string { + if runtime.GOOS == "linux" { + return paddedAbstractSocket("@infoLoggerD") + } else { // macOS + return "/tmp/infoLoggerD.socket" + } +} + +func NewDirectHook(defaultSystem string, defaultFacility string, levelsToLog []logrus.Level) (*DirectHook, error) { + + if levelsToLog == nil { + setCurrentILLevelFromViper() + } else { + currentIlLevel = levelsToLog + } + + socketPath := guessSocketPath() + sender := newSender(socketPath) + if sender == nil { + return nil, fmt.Errorf("cannot instantiate InfoLogger hook on socket %s", socketPath) + } + return &DirectHook{ + il: sender, + system: defaultSystem, + facility: defaultFacility, + role: hostname, + }, nil +} + +func NewDirectHookWithRole(defaultSystem string, defaultFacility string, defaultRole string, levelsToLog []logrus.Level) (*DirectHook, error) { + dh, err := NewDirectHook(defaultSystem, defaultFacility, levelsToLog) + if dh != nil { + dh.role = defaultRole + } + return dh, err +} + +func (h *DirectHook) Levels() []logrus.Level { + return currentIlLevel +} + +func (h *DirectHook) Fire(e *logrus.Entry) error { + // Supported field names: + // Severity, Level, ErrorCode, SourceFile, SourceLine, + // Facility, Role, System, Detector, Partition, Run + // Filled automatically by InfoLogger, do not set: PID, hostName, userName + payload := make(map[string]string) + payload["severity"] = logrusLevelToInfoLoggerSeverity(e.Level) + if _, hasLevel := payload["level"]; !hasLevel { + payload["level"] = logrusEntryToInfoLoggerLevel(e) + } + payload["timestamp"] = utils.NewUnixTimestamp() + payload["hostname"] = hostname + payload["pid"] = Pid + payload["username"] = username + + if e.HasCaller() { + payload["errsource"] = e.Caller.File + payload["errline"] = strconv.Itoa(e.Caller.Line) + } + + var message strings.Builder + message.WriteString(e.Message) + + unmappableFields := make(sort.StringSlice, 0) + + if e.Data != nil { + for k, v := range e.Data { + if k == "prefix" { + continue + } + + if k == "nohooks" { + if vBool, ok := v.(bool); ok { + if vBool { + return nil + } + } + } + + var vStr string + switch v.(type) { + case string: + vStr = v.(string) + case []byte: + vStr = string(v.([]byte)[:]) + case fmt.Stringer: + vStr = v.(fmt.Stringer).String() + default: + vStr = fmt.Sprintf("%v", v) + } + + if _, ok := Fields[k]; ok { + if k == Facility { + payload[Facility] = buildFineFacility(vStr, e.Data) + } else { + payload[k] = vStr + } + } else { // data structure key not mappable to InfoLogger field + unmappableFields = append(unmappableFields, fmt.Sprintf(" %s=\"%s\"", k, vStr)) + } + } + } + + // If a System, Facility or Role isn't passed manually, we fall back on defaults + if _, ok := e.Data[System]; !ok { + payload[System] = h.system + } + if _, ok := e.Data[Facility]; !ok { + payload[Facility] = buildFineFacility(h.facility, e.Data) + } + if _, ok := e.Data[Role]; !ok { + payload[Role] = h.role + } + + unmappableFields.Sort() + + for _, v := range unmappableFields { + message.WriteString(v) + } + + messageStr := message.String() + + // IL won't be happy if we send a message longer than 1024 bytes + if len(messageStr) > INFOLOGGER_MAX_MESSAGE_SIZE { + messageStr = messageStr[:INFOLOGGER_MAX_MESSAGE_SIZE] + } + + payload["message"] = messageStr + + if h.il != nil { + err := h.il.Send(payload) + if err != nil { + return fmt.Errorf("infoLogger hook error: %s", err.Error()) + } + } else { + return fmt.Errorf("infoLogger hook error: sender not available") + } + return nil +} diff --git a/common/logger/infologger/fields.go b/common/logger/infologger/fields.go index 7860adb6b..89a9be669 100644 --- a/common/logger/infologger/fields.go +++ b/common/logger/infologger/fields.go @@ -28,24 +28,24 @@ package infologger // (Severity), Level, ErrorCode, SourceFile, SourceLine, // Facility, Role, System, Detector, Partition, Run -const( - Level = "Level" - ErrorCode = "ErrorCode" - Facility = "Facility" - Role = "Role" - System = "System" - Detector = "Detector" - Partition = "Partition" - Run = "Run" +const ( + Level = "level" + ErrorCode = "errcode" + Facility = "facility" + Role = "rolename" + System = "system" + Detector = "detector" + Partition = "partition" + Run = "run" ) var Fields = map[string]bool{ - Level: true, + Level: true, ErrorCode: true, - Facility: true, - Role: true, - System: true, - Detector: true, + Facility: true, + Role: true, + System: true, + Detector: true, Partition: true, - Run: true, -} \ No newline at end of file + Run: true, +} diff --git a/common/logger/infologger/hook.go b/common/logger/infologger/hook.go index a7576167c..d4791e812 100644 --- a/common/logger/infologger/hook.go +++ b/common/logger/infologger/hook.go @@ -24,6 +24,7 @@ package infologger +/* import ( "fmt" "sort" @@ -34,7 +35,6 @@ import ( "infoLoggerForGo" ) - type Hook struct { infoLogger infoLoggerForGo.InfoLogger system string @@ -131,36 +131,4 @@ func (h *Hook) Fire(e *logrus.Entry) error { } return nil } - - -func logrusLevelToInfoLoggerSeverity(level logrus.Level) string { - switch level { - case logrus.PanicLevel: - return "F" - case logrus.FatalLevel: - return "F" - case logrus.ErrorLevel: - return "E" - case logrus.WarnLevel: - return "W" - case logrus.InfoLevel: - return "I" - case logrus.DebugLevel: - return "D" - case logrus.TraceLevel: - return "D" - default: - return "U" - } -} - -func buildFineFacility(baseFacility string, data logrus.Fields) string { - if data == nil { - return baseFacility - } - - if prefix, ok := data["prefix"]; ok { - return baseFacility + "/" + prefix.(string) - } - return baseFacility -} \ No newline at end of file +*/ diff --git a/common/logger/infologger/protocols.go b/common/logger/infologger/protocols.go new file mode 100644 index 000000000..0b0782052 --- /dev/null +++ b/common/logger/infologger/protocols.go @@ -0,0 +1,88 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2020 CERN and copyright holders of ALICE O². + * Author: Teo Mrnjavac + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +// Package infologger provides InfoLogger protocol implementation for +// integration with the ALICE InfoLogger logging system. +package infologger + +type protoVersion string + +const ( + v14 = protoVersion("1.4") + v13 = protoVersion("1.3") +) + +type fieldType string + +const ( + ft_String = fieldType("String") + ft_Number = fieldType("Number") +) + +type fieldSpec struct { + name string + ftype fieldType +} + +type protoSpec []fieldSpec + +var protocols = map[protoVersion]*protoSpec{ + v13: { + {name: "severity", ftype: ft_String}, + {name: "level", ftype: ft_Number}, + {name: "timestamp", ftype: ft_Number}, + {name: "hostname", ftype: ft_String}, + {name: "rolename", ftype: ft_String}, + {name: "pid", ftype: ft_Number}, + {name: "username", ftype: ft_String}, + {name: "system", ftype: ft_String}, + {name: "facility", ftype: ft_String}, + {name: "detector", ftype: ft_String}, + {name: "partition", ftype: ft_String}, + {name: "dest", ftype: ft_String}, + {name: "run", ftype: ft_Number}, + {name: "errcode", ftype: ft_Number}, + {name: "errline", ftype: ft_Number}, + {name: "errsource", ftype: ft_String}, + {name: "message", ftype: ft_String}, + }, + v14: { + {name: "severity", ftype: ft_String}, + {name: "level", ftype: ft_Number}, + {name: "timestamp", ftype: ft_Number}, + {name: "hostname", ftype: ft_String}, + {name: "rolename", ftype: ft_String}, + {name: "pid", ftype: ft_Number}, + {name: "username", ftype: ft_String}, + {name: "system", ftype: ft_String}, + {name: "facility", ftype: ft_String}, + {name: "detector", ftype: ft_String}, + {name: "partition", ftype: ft_String}, + {name: "run", ftype: ft_Number}, + {name: "errcode", ftype: ft_Number}, + {name: "errline", ftype: ft_Number}, + {name: "errsource", ftype: ft_String}, + {name: "message", ftype: ft_String}, + }, +} diff --git a/common/logger/infologger/utils.go b/common/logger/infologger/utils.go new file mode 100644 index 000000000..0f135bf32 --- /dev/null +++ b/common/logger/infologger/utils.go @@ -0,0 +1,97 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2020 CERN and copyright holders of ALICE O². + * Author: Teo Mrnjavac + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package infologger + +import "github.com/sirupsen/logrus" + +const ( + IL_Ops = 1 + IL_Support = 6 + IL_Devel = 11 + IL_Trace = 21 +) + +// Severity/priority constants: +// https://github.com/AliceO2Group/InfoLogger/blob/master/include/InfoLogger/InfoLogger.hxx + +// Extract InfoLogger level number from logrus Level +func logrusEntryToInfoLoggerLevel(e *logrus.Entry) string { + /// Some predefined constants that may be used to set the message level + /// (i.e. visibility of the message, based on who reads it) + /// The level is an integer in the 1-99 range (1: topmost visibility) + /// The enum below provides the main boundaries for typical operations, + /// and one may use finer granularity within each range. + /// operations (1-5) support (6-10) developer (11-20) trace (21-99). + /// Trace messages should typically not be enabled in normal running conditions, + /// and usually related to debugging activities (also akin to the 'Debug' severity). + // enum Level { + // Ops = 1, + // Support = 6, + // Devel = 11, + // Trace = 21 + // }; + switch e.Level { + case logrus.TraceLevel: + return "21" + case logrus.DebugLevel: + return "11" + default: + return "1" + } + +} + +// Extract InfoLogger severity char from logrus Level +func logrusLevelToInfoLoggerSeverity(level logrus.Level) string { + switch level { + case logrus.PanicLevel: + return "F" + case logrus.FatalLevel: + return "F" + case logrus.ErrorLevel: + return "E" + case logrus.WarnLevel: + return "W" + case logrus.InfoLevel: + return "I" + case logrus.DebugLevel: + return "D" + case logrus.TraceLevel: + return "D" + default: + return "U" + } +} + +func buildFineFacility(baseFacility string, data logrus.Fields) string { + if data == nil { + return baseFacility + } + + if prefix, ok := data["prefix"]; ok { + return baseFacility + "/" + prefix.(string) + } + return baseFacility +} diff --git a/common/logger/loggercopy.go b/common/logger/loggercopy.go new file mode 100644 index 000000000..b1db78c8a --- /dev/null +++ b/common/logger/loggercopy.go @@ -0,0 +1,76 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2022 CERN and copyright holders of ALICE O². + * Author: Teo Mrnjavac + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package logger + +import ( + "bytes" + "sync" + + "github.com/sirupsen/logrus" +) + +// This file implements a workaround for https://github.com/sirupsen/logrus/issues/564 + +type SafeLogrusWriter struct { + Entry *logrus.Entry + PrintFunc func(...interface{}) + buf bytes.Buffer + mu sync.Mutex +} + +func (w *SafeLogrusWriter) Write(b []byte) (int, error) { + w.mu.Lock() + defer w.mu.Unlock() + + origLen := len(b) + for { + if len(b) == 0 { + return origLen, nil + } + i := bytes.IndexByte(b, '\n') + if i < 0 { + w.buf.Write(b) + return origLen, nil + } + + w.buf.Write(b[:i]) + w.alwaysFlush() + b = b[i+1:] + } +} + +func (w *SafeLogrusWriter) alwaysFlush() { + w.PrintFunc(w.buf.String()) + w.buf.Reset() +} + +func (w *SafeLogrusWriter) Flush() { + w.mu.Lock() + defer w.mu.Unlock() + + if w.buf.Len() != 0 { + w.alwaysFlush() + } +} diff --git a/common/monitoring/common.go b/common/monitoring/common.go new file mode 100644 index 000000000..e5c0b1a82 --- /dev/null +++ b/common/monitoring/common.go @@ -0,0 +1,50 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2025 CERN and copyright holders of ALICE O². + * Author: Michal Tichak + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package monitoring + +import ( + "hash/maphash" + "time" +) + +type key struct { + nameTagsHash uint64 + timestamp time.Time +} + +func metricNameTagsToHash(hash *maphash.Hash, metric *Metric) { + _, _ = hash.WriteString(metric.name) + + for _, tag := range metric.tags { + _, _ = hash.WriteString(tag.name) + _, _ = hash.WriteString(tag.value) + } +} + +func hashValueAndReset(hash *maphash.Hash) uint64 { + hashValue := hash.Sum64() + hash.Reset() + return hashValue +} diff --git a/common/monitoring/grpcinterceptor.go b/common/monitoring/grpcinterceptor.go new file mode 100644 index 000000000..bc4d36caa --- /dev/null +++ b/common/monitoring/grpcinterceptor.go @@ -0,0 +1,104 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2025 CERN and copyright holders of ALICE O². + * Author: Michal Tichak + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package monitoring + +import ( + "context" + + "google.golang.org/grpc" +) + +type ( + EnvIDKey struct{} + RunTypeKey struct{} +) + +func AddEnvAndRunType(ctx context.Context, envId, runType string) context.Context { + ctx = context.WithValue(ctx, EnvIDKey{}, envId) + ctx = context.WithValue(ctx, RunTypeKey{}, runType) + return ctx +} + +type measuredClientStream struct { + grpc.ClientStream + ctx context.Context + method string + metricName string +} + +func (t *measuredClientStream) RecvMsg(m interface{}) error { + metric := NewMetric(t.metricName) + metric.AddTag("method", t.method) + if env, ok := t.ctx.Value(EnvIDKey{}).(string); ok { + metric.AddTag("envId", env) + } + if rt, ok := t.ctx.Value(RunTypeKey{}).(string); ok { + metric.AddTag("runtype", rt) + } + defer TimerSendSingle(&metric, Millisecond)() + + err := t.ClientStream.RecvMsg(m) + return err +} + +type NameConvertType func(string) string + +func SetupStreamClientInterceptor(metricName string, convert NameConvertType) grpc.StreamClientInterceptor { + return func( + ctx context.Context, + desc *grpc.StreamDesc, + cc *grpc.ClientConn, + method string, + streamer grpc.Streamer, + opts ...grpc.CallOption, + ) (grpc.ClientStream, error) { + clientStream, err := streamer(ctx, desc, cc, method, opts...) + if err != nil { + return nil, err + } + + return &measuredClientStream{ + ClientStream: clientStream, + ctx: ctx, + method: convert(method), + metricName: metricName, + }, nil + } +} + +func SetupUnaryClientInterceptor(name string, convert NameConvertType) grpc.UnaryClientInterceptor { + return func(ctx context.Context, method string, req, reply any, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error { + metric := NewMetric(name) + metric.AddTag("method", convert(method)) + if env, ok := ctx.Value(EnvIDKey{}).(string); ok { + metric.AddTag("envId", env) + } + if rt, ok := ctx.Value(RunTypeKey{}).(string); ok { + metric.AddTag("runtype", rt) + } + defer TimerSendSingle(&metric, Millisecond)() + return invoker(ctx, method, req, reply, cc, opts...) + } +} diff --git a/common/monitoring/metric.go b/common/monitoring/metric.go new file mode 100644 index 000000000..b0f4f98a7 --- /dev/null +++ b/common/monitoring/metric.go @@ -0,0 +1,155 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2025 CERN and copyright holders of ALICE O². + * Author: Michal Tichak + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package monitoring + +import ( + "fmt" + "io" + "sort" + "time" + + lp "github.com/influxdata/line-protocol/v2/lineprotocol" +) + +type ( + Tag struct { + name string + value string + } + + TagsType []Tag + FieldsType map[string]any +) + +type Metric struct { + name string + fields FieldsType + tags TagsType + timestamp time.Time +} + +// Return empty metric, it is used right now mostly in string +// to report metrics correctly to influxdb use NewECSMetric +func NewDefaultMetric(name string, timestamp time.Time) Metric { + return Metric{ + name: name, timestamp: timestamp, + } +} + +// creates empty metric with tag subsystem=ECS, which +// is used by Telegraf to send metrics from ECS to correct +// bucket +func NewMetric(name string) Metric { + metric := NewDefaultMetric(name, time.Now()) + metric.AddTag("subsystem", "ECS") + return metric +} + +func (metric *Metric) AddTag(tagName string, value string) { + metric.tags = append(metric.tags, Tag{name: tagName, value: value}) +} + +const ( + ERROR = "error" + SUCCESS = "success" + CANCELLED = "cancelled" + TIMEOUT = "timeout" +) + +func (metric *Metric) AddError(err error) { + if err == nil { + metric.AddResult(SUCCESS) + } else { + metric.AddResult(ERROR) + } +} + +func (metric *Metric) AddResult(result string) { + metric.AddTag("result", result) +} + +func (metric *Metric) setField(fieldName string, field any) { + if metric.fields == nil { + metric.fields = make(FieldsType) + } + metric.fields[fieldName] = field +} + +func (metric *Metric) SetFieldInt64(fieldName string, field int64) { + metric.setField(fieldName, field) +} + +func (metric *Metric) SetFieldUInt64(fieldName string, field uint64) { + metric.setField(fieldName, field) +} + +func (metric *Metric) SetFieldFloat64(fieldName string, field float64) { + metric.setField(fieldName, field) +} + +func (metric *Metric) MergeFields(other *Metric) { + for fieldName, field := range other.fields { + if storedField, ok := metric.fields[fieldName]; ok { + switch v := field.(type) { + case int64: + metric.fields[fieldName] = v + storedField.(int64) + case uint64: + metric.fields[fieldName] = v + storedField.(uint64) + case float64: + metric.fields[fieldName] = v + storedField.(float64) + } + } else { + metric.fields[fieldName] = field + } + } +} + +func Format(writer io.Writer, metrics []Metric) error { + var enc lp.Encoder + + for _, metric := range metrics { + // AddTag requires tags sorted lexicografically + sort.Slice(metric.tags, func(i int, j int) bool { + return metric.tags[i].name < metric.tags[j].name + }) + enc.StartLine(metric.name) + for _, tag := range metric.tags { + enc.AddTag(tag.name, tag.value) + } + + for fieldName, field := range metric.fields { + // we cannot panic as we provide accessors only for allowed type with AddField* + enc.AddField(fieldName, lp.MustNewValue(field)) + } + enc.EndLine(metric.timestamp) + } + + if err := enc.Err(); err != nil { + return err + } + + _, err := fmt.Fprintf(writer, "%s", enc.Bytes()) + return err +} diff --git a/common/monitoring/metricsaggregate.go b/common/monitoring/metricsaggregate.go new file mode 100644 index 000000000..7292db761 --- /dev/null +++ b/common/monitoring/metricsaggregate.go @@ -0,0 +1,69 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2025 CERN and copyright holders of ALICE O². + * Author: Michal Tichak + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package monitoring + +import ( + "hash/maphash" + "time" +) + +type bucketsType map[key]*Metric + +type MetricsAggregate struct { + hash maphash.Hash + metricsBuckets bucketsType +} + +func NewMetricsAggregate() *MetricsAggregate { + metrics := &MetricsAggregate{} + metrics.metricsBuckets = make(bucketsType) + return metrics +} + +func (this *MetricsAggregate) AddMetric(metric *Metric) { + metricNameTagsToHash(&this.hash, metric) + hashKey := hashValueAndReset(&this.hash) + + k := key{nameTagsHash: hashKey, timestamp: time.Unix(metric.timestamp.Unix(), 0)} + if storedMetric, ok := this.metricsBuckets[k]; ok { + storedMetric.MergeFields(metric) + } else { + this.metricsBuckets[k] = metric + } +} + +func (this *MetricsAggregate) Clear() { + this.hash.Reset() + clear(this.metricsBuckets) +} + +func (this *MetricsAggregate) GetMetrics() []Metric { + var result []Metric + for key, metric := range this.metricsBuckets { + metric.timestamp = key.timestamp + result = append(result, *metric) + } + return result +} diff --git a/common/monitoring/metricsreservoirsampling.go b/common/monitoring/metricsreservoirsampling.go new file mode 100644 index 000000000..853715dd7 --- /dev/null +++ b/common/monitoring/metricsreservoirsampling.go @@ -0,0 +1,107 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2025 CERN and copyright holders of ALICE O². + * Author: Michal Tichak + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package monitoring + +import ( + "hash/maphash" + "time" +) + +type bucketsReservoirSampleType map[key]*metricReservoirSample + +var reservoirSize uint64 = 1000 + +type metricReservoirSample struct { + metric Metric + reservoir reservoirSampling +} + +type MetricsReservoirSampling struct { + hash maphash.Hash + metricsBuckets bucketsReservoirSampleType +} + +func NewMetricsReservoirSampling() *MetricsReservoirSampling { + metrics := &MetricsReservoirSampling{} + metrics.metricsBuckets = make(bucketsReservoirSampleType) + metrics.hash.SetSeed(maphash.MakeSeed()) + return metrics +} + +func metricFieldToFloat64(field any) float64 { + var asserted float64 + switch v := field.(type) { + case int64: + asserted = float64(v) + case uint64: + asserted = float64(v) + case float64: + asserted = v + } + return asserted +} + +func (this *MetricsReservoirSampling) AddMetric(metric *Metric) { + for fieldName, field := range metric.fields { + metricNameTagsToHash(&this.hash, metric) + _, _ = this.hash.WriteString(fieldName) + k := key{nameTagsHash: hashValueAndReset(&this.hash), timestamp: time.Unix(metric.timestamp.Unix(), 0)} + if storedMetric, ok := this.metricsBuckets[k]; !ok { + newReservoir := newReservoirSampling(fieldName, reservoirSize) + newReservoir.AddPoint(metricFieldToFloat64(field)) + this.metricsBuckets[k] = &metricReservoirSample{metric: *metric, reservoir: newReservoir} + } else { + storedMetric.reservoir.AddPoint(metricFieldToFloat64(field)) + } + } +} + +func (this *MetricsReservoirSampling) Clear() { + this.hash.Reset() + clear(this.metricsBuckets) +} + +func (this *MetricsReservoirSampling) GetMetrics() []Metric { + var result []Metric + for key, reservoirMetric := range this.metricsBuckets { + m := Metric{name: reservoirMetric.metric.name, tags: reservoirMetric.metric.tags, timestamp: key.timestamp} + + mean, median, minimum, p10, p30, p70, p90, maximum, count, poolSize := reservoirMetric.reservoir.GetStats() + + m.SetFieldFloat64(reservoirMetric.reservoir.name+"_mean", mean) + m.SetFieldFloat64(reservoirMetric.reservoir.name+"_median", median) + m.SetFieldFloat64(reservoirMetric.reservoir.name+"_min", minimum) + m.SetFieldFloat64(reservoirMetric.reservoir.name+"_p10", p10) + m.SetFieldFloat64(reservoirMetric.reservoir.name+"_p30", p30) + m.SetFieldFloat64(reservoirMetric.reservoir.name+"_p70", p70) + m.SetFieldFloat64(reservoirMetric.reservoir.name+"_p90", p90) + m.SetFieldFloat64(reservoirMetric.reservoir.name+"_max", maximum) + m.SetFieldUInt64(reservoirMetric.reservoir.name+"_count", count) + m.SetFieldUInt64(reservoirMetric.reservoir.name+"_poolsize", poolSize) + + result = append(result, m) + } + return result +} diff --git a/common/monitoring/monitoring.go b/common/monitoring/monitoring.go new file mode 100644 index 000000000..ed22df9da --- /dev/null +++ b/common/monitoring/monitoring.go @@ -0,0 +1,191 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2025 CERN and copyright holders of ALICE O². + * Author: Michal Tichak + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +// Package monitoring provides monitoring and metrics collection functionality +// for O² Control components. +package monitoring + +import ( + "context" + "fmt" + "net/http" + "sync/atomic" + "time" + + "github.com/AliceO2Group/Control/common/logger" + "github.com/AliceO2Group/Control/common/logger/infologger" + "github.com/sirupsen/logrus" +) + +var ( + // atomic holder for the HTTP server instance + server atomic.Pointer[http.Server] + // objects to store incoming metrics + metricsInternal *MetricsAggregate = NewMetricsAggregate() + metricsHistogramInternal *MetricsReservoirSampling = NewMetricsReservoirSampling() + // channel that is used to request end of metrics server, it sends notification when server ended. + // It needs to be read!!! + endChannel chan struct{} = make(chan struct{}) + + // channel used to send metrics into the event loop + // 100000 was chosen arbitrarily as a number that seemed sensible to be high enough to provide nice buffer if + // multiple goroutines want to send metrics without blocking each other + metricsChannel chan Metric = make(chan Metric, 100000) + + // channel used to send metrics meant to be proceesed as histogram into the event loop + // 100000 was chosen arbitrarily as a number that seemed sensible to be high enough to provide nice buffer if + // multiple goroutines want to send metrics without blocking each other + metricsHistosChannel chan Metric = make(chan Metric, 100000) + + // channel for sending requests to reset actual metrics slice and send it back to caller via metricsExportedToRequest + metricsRequestedChannel chan struct{} = make(chan struct{}) + + // channel used to send metrics to be reported by http request from event loop + metricsExportedToRequest chan []Metric = make(chan []Metric) + + // WaitUntilRunning is waiting until this channel is closed + waitUntilRunningChannel chan struct{} = make(chan struct{}) + + log = logger.New(logrus.StandardLogger(), "metrics").WithField(infologger.Level, infologger.IL_Devel) +) + +// this eventLoop is the main part that processes all metrics send to the package +// 4 events can happen: +// 1. metricsChannel receives message from Send() method. We add the new metric to metrics slice +// 2. metricsHistosChannel receives message from Send() method. We add the new metric to metrics slice +// 3. metricsRequestChannel receives request to dump and request existing metrics. We send shallow copy of existing +// metrics to requestor (via metricsExportedToRequest channel) while resetting current metrics slice +// 4. receive request to stop monitoring via endChannel. We send confirmation through endChannel to notify caller +// that eventLoop stopped +func eventLoop() { + for { + select { + case <-metricsRequestedChannel: + aggregatedMetrics := metricsInternal.GetMetrics() + aggregatedMetrics = append(aggregatedMetrics, metricsHistogramInternal.GetMetrics()...) + metricsInternal.Clear() + metricsHistogramInternal.Clear() + + metricsExportedToRequest <- aggregatedMetrics + + case metric := <-metricsChannel: + metricsInternal.AddMetric(&metric) + + case metric := <-metricsHistosChannel: + metricsHistogramInternal.AddMetric(&metric) + + case <-endChannel: + defer func() { + endChannel <- struct{}{} + }() + return + } + } +} + +func exportMetricsAndReset(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "text/plain") + metricsRequestedChannel <- struct{}{} + metricsToConvert := <-metricsExportedToRequest + if metricsToConvert == nil { + metricsToConvert = make([]Metric, 0) + } + err := Format(w, metricsToConvert) + if err != nil { + log.WithField(infologger.Level, infologger.IL_Devel).Errorf("Failed to export metrics: %v", err) + } +} + +func Send(metric *Metric) { + // drop overflowing messages to not slowdown processing, we don't log so we don't flood IL + select { + case metricsChannel <- *metric: + default: + } +} + +func SendHistogrammable(metric *Metric) { + // drop overflowing messages to not slowdown processing, we don't log so we don't flood IL + select { + case metricsHistosChannel <- *metric: + default: + } +} + +func handleFunc(endpointName string) { + // recover is here to correctly allow multiple Starts and Stops of server + defer func() { + recover() + }() + + http.HandleFunc(endpointName, exportMetricsAndReset) +} + +// \param port port where the scraping endpoint will be created +// \param endpointName name of the endpoint, which must start with a slash eg. "/internalmetrics" +// +// If we attempt send more messages than the size of the buffer, these overflowing messages will be ignored and warning will be logged. +func Run(port uint16, endpointName string) error { + localServer := &http.Server{Addr: fmt.Sprintf(":%d", port)} + // only one Run should initialize and serve + if !server.CompareAndSwap(nil, localServer) { + return nil + } + go eventLoop() + handleFunc(endpointName) + // block until Shutdown is called + close(waitUntilRunningChannel) + return localServer.ListenAndServe() +} + +func Stop() { + localServer := server.Swap(nil) + if localServer == nil { + return + } + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + localServer.Shutdown(ctx) + endChannel <- struct{}{} + _, ok := <-waitUntilRunningChannel + if !ok { + waitUntilRunningChannel = make(chan struct{}) + } + metricsInternal.Clear() + metricsHistogramInternal.Clear() + <-endChannel +} + +// If monitoring is not running it will wait until monitoring is running or +// timeout is triggered. +// \return true if monitoring is running, false if timeout occured +func WaitUntilRunning(timeout time.Duration) bool { + timeoutChan := time.After(timeout) + select { + case <-waitUntilRunningChannel: + return true + case <-timeoutChan: + return false + } +} diff --git a/common/monitoring/monitoring_test.go b/common/monitoring/monitoring_test.go new file mode 100644 index 000000000..2facaf146 --- /dev/null +++ b/common/monitoring/monitoring_test.go @@ -0,0 +1,609 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2025 CERN and copyright holders of ALICE O². + * Author: Michal Tichak + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package monitoring + +import ( + "bytes" + "fmt" + "io" + "math" + "math/rand" + "net/http" + "os" + "runtime/pprof" + "strings" + "testing" + "time" +) + +// blocks until either IsRunning() returns true or timeout is triggered +func isRunningWithTimeout(t *testing.T, timeout time.Duration) { + if !WaitUntilRunning(timeout) { + t.Errorf("Failed to init monitoring library in %v", timeout) + } +} + +// block until either length of metrics is the same as \requiredMessages or timeout is triggered +func hasNumberOfMetrics(t *testing.T, timeout time.Duration, requiredMessages int) { + timeoutChan := time.After(timeout) + for len(metricsInternal.GetMetrics()) != requiredMessages { + select { + case <-timeoutChan: + t.Errorf("Timeout %v triggered when waiting for %v messages, got %v", timeout, requiredMessages, len(metricsInternal.GetMetrics())) + return + + default: + time.Sleep(10 * time.Millisecond) + } + } +} + +func TestSimpleStartStop(t *testing.T) { + go Run(1234, "/random") + isRunningWithTimeout(t, time.Second) + Stop() +} + +func TestStartMultipleStop(t *testing.T) { + go Run(1234, "/random") + isRunningWithTimeout(t, time.Second) + Stop() + Stop() +} + +// decorator function that properly inits and cleans after higher level test of Monitoring package +func testFunction(t *testing.T, testToRun func(*testing.T)) { + go Run(12345, "notimportant") + isRunningWithTimeout(t, time.Second) + testToRun(t) + Stop() + + if len(metricsInternal.GetMetrics()) != 0 { + t.Fatal("didn't clear metrics properly after tests") + } + if len(metricsHistogramInternal.GetMetrics()) != 0 { + t.Fatal("didn't clear histo metrics properly after tests") + } +} + +func TestSendingSingleMetric(t *testing.T) { + testFunction(t, func(t *testing.T) { + metric := &Metric{name: "testsinglemetric"} + Send(metric) + hasNumberOfMetrics(t, time.Second, 1) + + aggregatedMetrics := metricsInternal.GetMetrics() + + if aggregatedMetrics[0].name != "testsinglemetric" { + t.Errorf("Got wrong name %s in stored metric", aggregatedMetrics[0].name) + } + }) +} + +func TestExportingMetrics(t *testing.T) { + testFunction(t, func(t *testing.T) { + metric := &Metric{name: "testexporting"} + Send(metric) + hasNumberOfMetrics(t, time.Second, 1) + + metricsRequestedChannel <- struct{}{} + metricsToExport := <-metricsExportedToRequest + + if len(metricsToExport) != 1 { + t.Fatalf("Got wrong amount of metrics %d, expected 1", len(metricsToExport)) + } + + if metricsToExport[0].name != "testexporting" { + t.Fatalf("Got wrong name of metric %s, expected testexporting", metricsToExport[0].name) + } + }) +} + +func TestHttpRun(t *testing.T) { + port := uint16(9876) + endpoint := "/metrics" + go Run(port, endpoint) + defer Stop() + + isRunningWithTimeout(t, 5*time.Second) + + sendAndTestMetric(t, port, endpoint, "testhttprun1", time.Unix(10, 0), "tag1", "42", "value1", 11) + sendAndTestMetric(t, port, endpoint, "testhttprun2", time.Unix(11, 0), "tag2", "43", "value2", 12) + sendAndTestMetric(t, port, endpoint, "testhttprun3", time.Unix(12, 0), "tag3", "44", "value3", 13) + sendAndTestMetric(t, port, endpoint, "testhttprun4", time.Unix(13, 0), "tag4", "45", "value4", 14) +} + +func sendAndTestMetric(t *testing.T, port uint16, endpoint string, metricName string, timestamp time.Time, tagName string, tagVal string, fieldName string, fieldVal int64) { + metric := Metric{name: metricName, timestamp: timestamp} + metric.AddTag(tagName, tagVal) + metric.SetFieldInt64(fieldName, fieldVal) + Send(&metric) + + response, err := http.Get(fmt.Sprintf("http://localhost:%d%s", port, endpoint)) + if err != nil { + t.Fatalf("Failed to GET metrics at port 9876: %v", err) + } + message, err := io.ReadAll(response.Body) + if err != nil { + t.Fatalf("Failed to read response Body: %v", err) + } + + receivedMetrics, err := parseMultipleLineProtocol(string(message)) + if err != nil { + t.Fatalf("Failed to parse message: %v with err: %v", string(message), err) + } + + receivedMetric := receivedMetrics[0] + + if receivedMetric.Name != metricName { + t.Errorf("Got wrong name of metric %s, expected %s", receivedMetric.Name, metricName) + } + + if receivedMetric.Timestamp != timestamp.UnixNano() { + t.Errorf("Got wrong timestamp of metric %d, expected %v", receivedMetric.Timestamp, timestamp.UnixNano()) + } + + if len(receivedMetric.Tags) != 1 { + t.Errorf("Got wrong number of tags %d, expected 1", len(receivedMetric.Tags)) + } + + if receivedMetric.Tags[tagName] != tagVal { + t.Errorf("Failed to retreive tags: tag1 with value 42, %+v", receivedMetric.Tags) + } + + if len(receivedMetric.Fields) != 1 { + t.Errorf("Got wrong number of values %d, expected 1", len(receivedMetric.Fields)) + } + + if receivedMetric.Fields[fieldName] != fmt.Sprintf("%di", fieldVal) { + t.Errorf("Failed to retreive tags: %s with value %d: %+v", fieldName, fieldVal, receivedMetric.Fields) + } +} + +func parseMultipleLineProtocol(input string) ([]struct { + Name string + Tags map[string]string + Fields map[string]string + Timestamp int64 +}, error, +) { + lines := strings.Split(strings.TrimSpace(input), "\n") + parsed := []struct { + Name string + Tags map[string]string + Fields map[string]string + Timestamp int64 + }{} + + for _, line := range lines { + parts := strings.SplitN(line, " ", 3) + if len(parts) < 3 { + return nil, fmt.Errorf("invalid line protocol format: %s", line) + } + + // Parse measurement and tags + nameTags := strings.Split(parts[0], ",") + name := nameTags[0] + tags := map[string]string{} + for _, tag := range nameTags[1:] { + kv := strings.SplitN(tag, "=", 2) + if len(kv) == 2 { + tags[kv[0]] = kv[1] + } + } + + // Parse fields (leave as raw string for comparison) + fields := map[string]string{} + for _, field := range strings.Split(parts[1], ",") { + kv := strings.SplitN(field, "=", 2) + if len(kv) == 2 { + fields[kv[0]] = kv[1] + } + } + + // Parse timestamp + var ts int64 + _, err := fmt.Sscanf(parts[2], "%d", &ts) + if err != nil { + return nil, fmt.Errorf("invalid timestamp in line: %s", line) + } + + parsed = append(parsed, struct { + Name string + Tags map[string]string + Fields map[string]string + Timestamp int64 + }{ + Name: name, + Tags: tags, + Fields: fields, + Timestamp: ts, + }) + } + + return parsed, nil +} + +func MapsEqual[K comparable, V comparable](a, b map[K]V) bool { + if len(a) != len(b) { + return false + } + for key, valueA := range a { + if valueB, ok := b[key]; !ok || valueA != valueB { + return false + } + } + return true +} + +func TestMetricsFormat(t *testing.T) { + metrics := []Metric{} + metric1 := Metric{name: "test"} + metric1.timestamp = time.Unix(10, 0) + metric1.AddTag("tag1", "42") + metric1.SetFieldInt64("int64", 1) + metric1.SetFieldUInt64("uint64", 2) + metric1.SetFieldFloat64("float64", 3) + + metrics = append(metrics, metric1) + + metric2 := Metric{name: "test1"} + metric2.timestamp = time.Unix(10, 0) + metric2.AddTag("tag1", "43") + metric2.SetFieldInt64("int64", 2) + metric2.SetFieldUInt64("uint64", 3) + metric2.SetFieldFloat64("float64", 4) + + metrics = append(metrics, metric2) + + buf := bytes.Buffer{} + + Format(&buf, metrics) + + metricsParsed, err := parseMultipleLineProtocol(buf.String()) + if err != nil { + t.Error(err) + } + + for idx, metricParsed := range metricsParsed { + metricOrig := metrics[idx] + + if metricParsed.Name != metricOrig.name { + t.Errorf("failed to compare %v and %v ", metricOrig, metricParsed) + } + // if !MapsEqual(metricParsed.Tags, metricOrig.Tags) { + // t.Errorf("failed to compare %v and %v ", metricOrig.Tags, metricParsed.Tags) + // } + // if !MapsEqual(metricParsed.Fields, metricOrig.Values) { + // t.Errorf("failed to compare %v and %v ", metricOrig.Tags, metricParsed.Tags) + // } + if metricParsed.Timestamp != metricOrig.timestamp.UnixNano() { + t.Errorf("failed to compare %v and %v ", metricOrig.timestamp.UnixNano(), metricParsed.Timestamp) + } + } +} + +// generate unix timestamp with given seconds and random amount of nsecs +func generateTimestamp(seconds int64, r *rand.Rand) time.Time { + return time.Unix(seconds, r.Int63n(999999999)) +} + +func TestMetricsObject(t *testing.T) { + r := rand.New(rand.NewSource(42)) + tags1 := TagsType{Tag{"tag1", "1"}, Tag{"tag2", "2"}} + metrics := NewMetricsAggregate() + for i := 0; i != 1000; i++ { + metrics.AddMetric(&Metric{name: "test", tags: tags1, fields: FieldsType{"val1": int64(1)}, timestamp: generateTimestamp(10, r)}) + metrics.AddMetric(&Metric{name: "test", tags: tags1, fields: FieldsType{"val1": int64(1)}, timestamp: generateTimestamp(11, r)}) + metrics.AddMetric(&Metric{name: "test", tags: tags1, fields: FieldsType{"val2": int64(1)}, timestamp: generateTimestamp(11, r)}) + } + + if len(metrics.metricsBuckets) != 2 { + t.Errorf("there is diffent number of buckets, wanted 2 got %d", len(metrics.metricsBuckets)) + } + + var aggregatedVal1 int64 + var aggregatedVal2 int64 + for key, metric := range metrics.metricsBuckets { + if key.timestamp != time.Unix(10, 0) && key.timestamp != time.Unix(11, 0) { + t.Errorf("expected bucket timestamp as 10 or 11, got %ds, %dns", key.timestamp.Unix(), key.timestamp.UnixNano()) + } + aggregatedVal1 += metric.fields["val1"].(int64) + if _, ok := metric.fields["val2"]; ok { + aggregatedVal2 += metric.fields["val2"].(int64) + } + } + + if aggregatedVal1 != 2000 { + t.Errorf("all values \"val1\" in buckets inside the object should have been aggregated to 2000, but got %d", aggregatedVal1) + } + + if aggregatedVal2 != 1000 { + t.Errorf("all values \"val2\" in buckets inside the object should have been aggregated to 1000, but got %d", aggregatedVal2) + } + + aggregatedMetrics := metrics.GetMetrics() + if len(metrics.metricsBuckets) != 2 { + t.Errorf("there should be only 2 metrics buckets, but the number is %d", len(metrics.metricsBuckets)) + } + + aggregatedVal1 = 0 + aggregatedVal2 = 0 + for _, metric := range aggregatedMetrics { + if metric.timestamp != time.Unix(10, 0) && metric.timestamp != time.Unix(11, 0) { + t.Errorf("expected aggregated metric timestamp as 10 or 11, got %ds, %dns", metric.timestamp.Unix(), metric.timestamp.UnixNano()) + } + aggregatedVal1 += metric.fields["val1"].(int64) + if _, ok := metric.fields["val2"]; ok { + aggregatedVal2 += metric.fields["val2"].(int64) + } + } + if aggregatedVal1 != 2000 { + t.Errorf("all values \"val1\" of aggregated metrics should have been aggregated to 2000, but got %d", aggregatedVal1) + } + + if aggregatedVal2 != 1000 { + t.Errorf("all values \"val2\" of aggregated metrics should have been aggregated to 1000, but got %d", aggregatedVal2) + } + + metrics.Clear() + aggregatedMetrics = metrics.GetMetrics() + if len(aggregatedMetrics) != 0 { + t.Errorf("metrics object should be empty after clearing, but we got some metrics: %+v", aggregatedMetrics) + } +} + +func TestApproximateHistogram(t *testing.T) { + histo := newReservoirSampling("test", 500) + for i := 0; i != 20; i++ { + histo.AddPoint(10) + } + + mean, median, minimum, p10, p30, p70, p90, maximum, count, poolSize := histo.GetStats() + + if count != 20 { + t.Errorf("wrong count before reset, expected 20, got %d", count) + } + + if poolSize != 20 { + t.Errorf("wrong poolSize, expected 20, got %d", poolSize) + } + + if mean != 10 || median != 10 || minimum != 10 || p10 != 10 || p30 != 10 || p70 != 10 || p90 != 10 || maximum != 10 { + t.Errorf("one of the values is not 10 when it should be 10, mean %v, median %v, 10p %v, 30p %v, 70p %v, 90p %v", mean, median, p10, p30, p70, p90) + } + + histo.Reset() + _, _, _, _, _, _, _, _, count, poolSize = histo.GetStats() + if count != 0 { + t.Errorf("wrong count before reset, expected 0, got %d", count) + } + + if poolSize != 0 { + t.Errorf("wrong poolSize, expected 0, got %d", poolSize) + } + + for i := 0; i != 2000; i++ { + histo.AddPoint(10) + } + + mean, median, minimum, p10, p30, p70, p90, maximum, count, poolSize = histo.GetStats() + + if count != 2000 { + t.Errorf("wrong count before reset, expected 2000, got %d", count) + } + + if poolSize != 500 { + t.Errorf("wrong poolSize, expected 500, got %d", poolSize) + } + + if mean != 10 || median != 10 || minimum != 10 || p10 != 10 || p30 != 10 || p70 != 10 || p90 != 10 || maximum != 10 { + t.Errorf("one of the values is not 10 when it should be 10, mean %v, median %v, 10p %v, 30p %v, 70p %v, 90p %v", mean, median, p10, p30, p70, p90) + } + + histo.Reset() + _, _, _, _, _, _, _, _, count, poolSize = histo.GetStats() + if count != 0 { + t.Errorf("wrong count before reset, expected 0, got %d", count) + } + + if poolSize != 0 { + t.Errorf("wrong poolSize, expected 0, got %d", poolSize) + } + + for i := 0; i != 10000; i++ { + histo.AddPoint((float64(rand.Int63n(100)))) + } + + mean, median, minimum, p10, p30, p70, p90, maximum, count, poolSize = histo.GetStats() + + if count != 10000 { + t.Errorf("wrong count before reset, expected 10000, got %d", count) + } + + if poolSize != 500 { + t.Errorf("wrong poolSize, expected 500, got %d", poolSize) + } + + if math.Abs(mean-50) > 10 { + t.Errorf("wrong mean value, expected 50+-10 got %v", mean) + } + + if math.Abs(float64(median-50)) > 10 { + t.Errorf("wrong median value, expected 50+-10 got %v", median) + } + + if float64(minimum) > 10 { + t.Errorf("wrong min value, expected 0+-10 got %v", minimum) + } + + if math.Abs(float64(p10-10)) > 10 { + t.Errorf("wrong 10p value, expected 10+-10 got %v", p10) + } + + if math.Abs(float64(p30-30)) > 10 { + t.Errorf("wrong 30p value, expected 30+-10 got %v", p30) + } + + if math.Abs(float64(p70-70)) > 10 { + t.Errorf("wrong 50p value, expected 70+-10 got %v", p70) + } + + if math.Abs(float64(p90-90)) > 10 { + t.Errorf("wrong 90p value, expected 90+-10 got %v", p90) + } + + if math.Abs(float64(maximum-100)) > 10 { + t.Errorf("wrong max value, expected 100+-10 got %v", maximum) + } +} + +func TestMetricsHistogramObject(t *testing.T) { + metricsHisto := NewMetricsReservoirSampling() + r := rand.New(rand.NewSource(42)) + tags1 := TagsType{Tag{"tag1", "1"}, Tag{"tag2", "2"}} + for i := 0; i != 2000; i++ { + metricsHisto.AddMetric(&Metric{name: "test", tags: tags1, fields: FieldsType{"val1": int64(r.Int31n(100)), "val2": int64(r.Int31n(100))}, timestamp: generateTimestamp(10, r)}) + metricsHisto.AddMetric(&Metric{name: "test", tags: tags1, fields: FieldsType{"val1": int64(r.Int31n(100)), "val2": int64(r.Int31n(100))}, timestamp: generateTimestamp(15, r)}) + } + metrics := metricsHisto.GetMetrics() + if len(metrics) != 4 { + t.Errorf("received wrong number of histogram metrics, expected 4, got %d", len(metrics)) + } + + for _, metric := range metrics { + for valueName, value := range metric.fields { + if strings.Contains(valueName, "mean") { + if math.Abs(value.(float64)-50) > 10 { + t.Errorf("wrong mean value, expected 50+-10 got %v", value.(float64)) + } + } + if strings.Contains(valueName, "median") { + if math.Abs(value.(float64)-50) > 10 { + t.Errorf("wrong median value, expected 50+-10 got %v", value.(float64)) + } + } + if strings.Contains(valueName, "p10") { + if math.Abs(value.(float64)-10) > 10 { + t.Errorf("wrong p10, expected 10+-10 got %v", value.(float64)) + } + } + if strings.Contains(valueName, "p30") { + if math.Abs(value.(float64)-30) > 10 { + t.Errorf("wrong p30, expected 30+-10 got %v", value.(float64)) + } + } + if strings.Contains(valueName, "p70") { + if math.Abs(value.(float64)-70) > 10 { + t.Errorf("wrong p70, expected 70+-10 got %v", value.(float64)) + } + } + if strings.Contains(valueName, "p90") { + if math.Abs(value.(float64)-90) > 10 { + t.Errorf("wrong p90, expected 90+-10 got %v", value.(float64)) + } + } + if strings.Contains(valueName, "count") { + if value.(uint64) != 2000 { + t.Errorf("wrong number of count, expected 2000 got %v", value.(uint64)) + } + } + if strings.Contains(valueName, "poolsize") { + if value.(uint64) != 1000 { + t.Errorf("wrong number of poolsize, expected 1000 got %v", value.(uint64)) + } + } + } + } + + metricsHisto.Clear() + if m := metricsHisto.GetMetrics(); len(m) != 0 { + t.Errorf("histogram metrics should be empty after clearing, but we got histogram metrics: %+v", m) + } +} + +func measureFunc(metric *Metric) { + defer Timer(metric, Millisecond)() + defer Timer(metric, Nanosecond)() + time.Sleep(100 * time.Millisecond) +} + +func TestTimers(t *testing.T) { + metric := NewMetric("test") + measureFunc(&metric) + fields := metric.fields + if fields["execution_time_ms"].(int64) < 100 { + t.Error("wrong milliseconds") + } + if fields["execution_time_ns"].(int64) < 100000000 { + t.Error("wrong nanoseconds") + } +} + +func BenchmarkSimple(b *testing.B) { + cpuProfileFile, err := os.Create("cpu_profile.pprof") + if err != nil { + b.Fatalf("could not create CPU profile: %v", err) + } + defer cpuProfileFile.Close() + + pprof.StartCPUProfile(cpuProfileFile) + defer pprof.StopCPUProfile() + + metrics := NewMetricsAggregate() + + metricToAdd1Tag1Val := Metric{name: "metricToAdd1Tag1Val", tags: TagsType{Tag{"tag1", "tag1"}}, fields: FieldsType{"val1": int64(1)}, timestamp: time.Unix(10, 1111)} + metricToAdd2Tag2Val := Metric{name: "metricToAdd1Tag1Val", tags: TagsType{Tag{"tag1", "tag1"}, Tag{"tag2", "tag2"}}, fields: FieldsType{"val1": int64(1), "val2": int64(2)}, timestamp: time.Unix(10, 1223)} + metricToAdd3Tag3Val := Metric{name: "metricToAdd1Tag1Val", tags: TagsType{Tag{"tag1", "tag1"}, Tag{"tag2", "tag2"}, Tag{"tag3", "tag3"}}, fields: FieldsType{"val1": int64(10), "val2": int64(2), "val3": int64(3)}, timestamp: time.Unix(10, 2531)} + + for b.Loop() { + metrics.AddMetric(&metricToAdd1Tag1Val) + metrics.AddMetric(&metricToAdd2Tag2Val) + metrics.AddMetric(&metricToAdd3Tag3Val) + } + + heapProfileFile, err := os.Create("heap_profile.pprof") + if err != nil { + log.Fatalf("could not create heap profile: %v", err) + } + defer heapProfileFile.Close() + pprof.WriteHeapProfile(heapProfileFile) +} + +func BenchmarkSendingMetrics(b *testing.B) { + go Run(12345, "/metrics") + + defer Stop() + + metric := Metric{name: "testname", timestamp: time.Unix(12345, 0)} + metric.SetFieldInt64("value1", 42) + metric.SetFieldInt64("value2", 42) + metric.AddTag("tag1", "40") + metric.AddTag("tag2", "40") + + for b.Loop() { + Send(&metric) + } +} diff --git a/common/monitoring/reservoirsampling.go b/common/monitoring/reservoirsampling.go new file mode 100644 index 000000000..b954d7316 --- /dev/null +++ b/common/monitoring/reservoirsampling.go @@ -0,0 +1,94 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2025 CERN and copyright holders of ALICE O². + * Author: Michal Tichak + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package monitoring + +import ( + "math/rand" + "sort" +) + +// Documentation how does the calculation of percentiles work +// https://en.wikipedia.org/wiki/Reservoir_sampling + +type reservoirSampling struct { + samples []float64 + samplesLimit uint64 + name string + countSinceReset uint64 +} + +func newReservoirSampling(name string, limit uint64) reservoirSampling { + return reservoirSampling{ + samples: make([]float64, 0, limit), + samplesLimit: limit, + name: name, + countSinceReset: 0, + } +} + +func (this *reservoirSampling) AddPoint(val float64) { + this.countSinceReset += 1 + if len(this.samples) < int(this.samplesLimit) { + this.samples = append(this.samples, val) + } else { + if j := rand.Int63n(int64(this.countSinceReset)); j < int64(len(this.samples)) { + this.samples[j] = val + } + } +} + +func (this *reservoirSampling) indexForPercentile(percentile int) int { + return int(float64(len(this.samples)) * 0.01 * float64(percentile)) +} + +func (this *reservoirSampling) Reset() { + this.samples = this.samples[:0] + this.countSinceReset = 0 +} + +func (this *reservoirSampling) GetStats() (mean float64, median float64, min float64, percentile10 float64, percentile30 float64, percentile70 float64, percentile90 float64, max float64, count uint64, poolSize uint64) { + sort.Slice(this.samples, func(i, j int) bool { return this.samples[i] < this.samples[j] }) + + samplesCount := len(this.samples) + if samplesCount == 0 { + return 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + } + + var sum float64 + for _, val := range this.samples { + sum += float64(val) + } + + return sum / float64(samplesCount), + this.samples[this.indexForPercentile(50)], + this.samples[0], + this.samples[this.indexForPercentile(10)], + this.samples[this.indexForPercentile(30)], + this.samples[this.indexForPercentile(70)], + this.samples[this.indexForPercentile(90)], + this.samples[len(this.samples)-1], + this.countSinceReset, + uint64(len(this.samples)) +} diff --git a/common/monitoring/timer.go b/common/monitoring/timer.go new file mode 100644 index 000000000..f1d26bc31 --- /dev/null +++ b/common/monitoring/timer.go @@ -0,0 +1,59 @@ +package monitoring + +import ( + "time" + + "github.com/AliceO2Group/Control/common/logger/infologger" +) + +type TimeResolution int + +const ( + Millisecond TimeResolution = iota + Nanosecond +) + +// Timer function is meant to be used with defer statement to measure runtime of given function: +// defer Timer(&metric, Milliseconds)() +func Timer(metric *Metric, unit TimeResolution) func() { + return timer(metric, unit, false, false) +} + +// Timer function is meant to be used with defer statement to measure runtime of given function: +// defer Timer(&metric, Milliseconds)() +// sends measured value as Send(metric) +func TimerSendSingle(metric *Metric, unit TimeResolution) func() { + return timer(metric, unit, true, false) +} + +// Timer function is meant to be used with defer statement to measure runtime of given function: +// defer Timer(&metric, Milliseconds)() +// sends measured value as SendHistogrammable(metric) +func TimerSendHist(metric *Metric, unit TimeResolution) func() { + return timer(metric, unit, true, true) +} + +func timer(metric *Metric, unit TimeResolution, send bool, sendHistogrammable bool) func() { + start := time.Now() + + return func() { + dur := time.Since(start) + // we are setting default value as Nanoseconds + switch unit { + case Millisecond: + metric.SetFieldInt64("execution_time_ms", dur.Milliseconds()) + case Nanosecond: + metric.SetFieldInt64("execution_time_ns", dur.Nanoseconds()) + default: + log.WithField("level", infologger.IL_Devel).Warnf("trying to use unknown time resolution in monitoring.timer function [%d], skipping", unit) + } + + if send { + if sendHistogrammable { + SendHistogrammable(metric) + } else { + Send(metric) + } + } + } +} diff --git a/common/product/constants.go b/common/product/constants.go index d75282e4a..c6c981d3a 100644 --- a/common/product/constants.go +++ b/common/product/constants.go @@ -22,6 +22,8 @@ * Intergovernmental Organization or submit itself to any jurisdiction. */ +// Package product provides version information and build metadata for +// O² Control products and components. package product import ( @@ -30,8 +32,8 @@ import ( "path/filepath" "strings" - "gopkg.in/src-d/go-git.v4" - "gopkg.in/src-d/go-git.v4/plumbing" + "github.com/go-git/go-git/v5" + "github.com/go-git/go-git/v5/plumbing" ) func getExecutableDir() string { @@ -85,7 +87,6 @@ func fillBuildFromGit(localRepoPath string) { BUILD = h.String()[:7] } - func init() { // If the core was built with go build directly instead of make. if VERSION_MAJOR == "0" && @@ -103,16 +104,16 @@ func init() { fillBuildFromGit(basePath) } - VERSION = strings.Join([]string{VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH}, ".") - VERSION_SHORT = VERSION - VERSION_BUILD = strings.Join([]string{VERSION, BUILD}, "-") + VERSION = strings.Join([]string{VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH}, ".") + VERSION_SHORT = VERSION + VERSION_BUILD = strings.Join([]string{VERSION, BUILD}, "-") } var ( // Acquired from -ldflags="-X=..." in Makefile - VERSION_MAJOR = "0" - VERSION_MINOR = "0" - VERSION_PATCH = "0" - BUILD = "" + VERSION_MAJOR = "0" + VERSION_MINOR = "0" + VERSION_PATCH = "0" + BUILD = "" ) var ( @@ -122,4 +123,4 @@ var ( VERSION string VERSION_SHORT string VERSION_BUILD string -) \ No newline at end of file +) diff --git a/common/protos/common.pb.go b/common/protos/common.pb.go new file mode 100644 index 000000000..ce7796310 --- /dev/null +++ b/common/protos/common.pb.go @@ -0,0 +1,598 @@ +// +// === This file is part of ALICE O² === +// +// Copyright 2024 CERN and copyright holders of ALICE O². +// Author: Teo Mrnjavac +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// In applying this license CERN does not waive the privileges and +// immunities granted to it by virtue of its status as an +// Intergovernmental Organization or submit itself to any jurisdiction. + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.34.1 +// protoc v5.29.3 +// source: protos/common.proto + +package pb + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// Production and staging BKP recognizes these special user IDs. +// They are used to associate certain actions (e.g. EOR) with certain subsystem +// or AliECS component rather than a human user. +type SpecialUserId int32 + +const ( + SpecialUserId_INVALID SpecialUserId = 0 + SpecialUserId_LHC SpecialUserId = 60 + SpecialUserId_TIMER SpecialUserId = 65 +) + +// Enum value maps for SpecialUserId. +var ( + SpecialUserId_name = map[int32]string{ + 0: "INVALID", + 60: "LHC", + 65: "TIMER", + } + SpecialUserId_value = map[string]int32{ + "INVALID": 0, + "LHC": 60, + "TIMER": 65, + } +) + +func (x SpecialUserId) Enum() *SpecialUserId { + p := new(SpecialUserId) + *p = x + return p +} + +func (x SpecialUserId) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (SpecialUserId) Descriptor() protoreflect.EnumDescriptor { + return file_protos_common_proto_enumTypes[0].Descriptor() +} + +func (SpecialUserId) Type() protoreflect.EnumType { + return &file_protos_common_proto_enumTypes[0] +} + +func (x SpecialUserId) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use SpecialUserId.Descriptor instead. +func (SpecialUserId) EnumDescriptor() ([]byte, []int) { + return file_protos_common_proto_rawDescGZIP(), []int{0} +} + +// * +// Beam modes as defined and sent by LHC DIP client plus: +// * virtual type LOST_BEAMS - that is generated when beam 1 and beam 2 energy values are not equal anymore as per LHC DIP track: dip/acc/LHC/RunControl/SafeBeam +// * virtual type UNKNOWN - that is generated when there is no beam in the machine or value not added by the BKP-LHC Client +// Source of Beam Modes: https://lhc-commissioning.web.cern.ch/systems/data-exchange/doc/LHC-OP-ES-0005-10-00.pdf +type BeamMode int32 + +const ( + BeamMode_UNKNOWN BeamMode = 0 // virtual type + BeamMode_SETUP BeamMode = 1 + BeamMode_ABORT BeamMode = 2 + BeamMode_INJECTION_PROBE_BEAM BeamMode = 3 + BeamMode_INJECTION_SETUP_BEAM BeamMode = 4 + BeamMode_INJECTION_PHYSICS_BEAM BeamMode = 5 + BeamMode_PREPARE_RAMP BeamMode = 6 + BeamMode_RAMP BeamMode = 7 + BeamMode_FLAT_TOP BeamMode = 8 + BeamMode_SQUEEZE BeamMode = 9 + BeamMode_ADJUST BeamMode = 10 + BeamMode_STABLE_BEAMS BeamMode = 11 + BeamMode_LOST_BEAMS BeamMode = 12 // virtual type + BeamMode_UNSTABLE_BEAMS BeamMode = 13 + BeamMode_BEAM_DUMP_WARNING BeamMode = 14 + BeamMode_BEAM_DUMP BeamMode = 15 + BeamMode_RAMP_DOWN BeamMode = 16 + BeamMode_CYCLING BeamMode = 17 + BeamMode_RECOVERY BeamMode = 18 + BeamMode_INJECT_AND_DUMP BeamMode = 19 + BeamMode_CIRCULATE_AND_DUMP BeamMode = 20 + BeamMode_NO_BEAM BeamMode = 21 +) + +// Enum value maps for BeamMode. +var ( + BeamMode_name = map[int32]string{ + 0: "UNKNOWN", + 1: "SETUP", + 2: "ABORT", + 3: "INJECTION_PROBE_BEAM", + 4: "INJECTION_SETUP_BEAM", + 5: "INJECTION_PHYSICS_BEAM", + 6: "PREPARE_RAMP", + 7: "RAMP", + 8: "FLAT_TOP", + 9: "SQUEEZE", + 10: "ADJUST", + 11: "STABLE_BEAMS", + 12: "LOST_BEAMS", + 13: "UNSTABLE_BEAMS", + 14: "BEAM_DUMP_WARNING", + 15: "BEAM_DUMP", + 16: "RAMP_DOWN", + 17: "CYCLING", + 18: "RECOVERY", + 19: "INJECT_AND_DUMP", + 20: "CIRCULATE_AND_DUMP", + 21: "NO_BEAM", + } + BeamMode_value = map[string]int32{ + "UNKNOWN": 0, + "SETUP": 1, + "ABORT": 2, + "INJECTION_PROBE_BEAM": 3, + "INJECTION_SETUP_BEAM": 4, + "INJECTION_PHYSICS_BEAM": 5, + "PREPARE_RAMP": 6, + "RAMP": 7, + "FLAT_TOP": 8, + "SQUEEZE": 9, + "ADJUST": 10, + "STABLE_BEAMS": 11, + "LOST_BEAMS": 12, + "UNSTABLE_BEAMS": 13, + "BEAM_DUMP_WARNING": 14, + "BEAM_DUMP": 15, + "RAMP_DOWN": 16, + "CYCLING": 17, + "RECOVERY": 18, + "INJECT_AND_DUMP": 19, + "CIRCULATE_AND_DUMP": 20, + "NO_BEAM": 21, + } +) + +func (x BeamMode) Enum() *BeamMode { + p := new(BeamMode) + *p = x + return p +} + +func (x BeamMode) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (BeamMode) Descriptor() protoreflect.EnumDescriptor { + return file_protos_common_proto_enumTypes[1].Descriptor() +} + +func (BeamMode) Type() protoreflect.EnumType { + return &file_protos_common_proto_enumTypes[1] +} + +func (x BeamMode) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use BeamMode.Descriptor instead. +func (BeamMode) EnumDescriptor() ([]byte, []int) { + return file_protos_common_proto_rawDescGZIP(), []int{1} +} + +type User struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The unique CERN identifier of this user. + ExternalId *int32 `protobuf:"varint,1,opt,name=externalId,proto3,oneof" json:"externalId,omitempty"` + // The unique identifier of this entity. + Id *int32 `protobuf:"varint,2,opt,name=id,proto3,oneof" json:"id,omitempty"` + // Name of the user. + Name string `protobuf:"bytes,3,opt,name=name,proto3" json:"name,omitempty"` +} + +func (x *User) Reset() { + *x = User{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_common_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *User) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*User) ProtoMessage() {} + +func (x *User) ProtoReflect() protoreflect.Message { + mi := &file_protos_common_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use User.ProtoReflect.Descriptor instead. +func (*User) Descriptor() ([]byte, []int) { + return file_protos_common_proto_rawDescGZIP(), []int{0} +} + +func (x *User) GetExternalId() int32 { + if x != nil && x.ExternalId != nil { + return *x.ExternalId + } + return 0 +} + +func (x *User) GetId() int32 { + if x != nil && x.Id != nil { + return *x.Id + } + return 0 +} + +func (x *User) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +type WorkflowTemplateInfo struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Description string `protobuf:"bytes,2,opt,name=description,proto3" json:"description,omitempty"` + Path string `protobuf:"bytes,3,opt,name=path,proto3" json:"path,omitempty"` + Public bool `protobuf:"varint,4,opt,name=public,proto3" json:"public,omitempty"` // whether the environment is public or not +} + +func (x *WorkflowTemplateInfo) Reset() { + *x = WorkflowTemplateInfo{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_common_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *WorkflowTemplateInfo) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WorkflowTemplateInfo) ProtoMessage() {} + +func (x *WorkflowTemplateInfo) ProtoReflect() protoreflect.Message { + mi := &file_protos_common_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use WorkflowTemplateInfo.ProtoReflect.Descriptor instead. +func (*WorkflowTemplateInfo) Descriptor() ([]byte, []int) { + return file_protos_common_proto_rawDescGZIP(), []int{1} +} + +func (x *WorkflowTemplateInfo) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *WorkflowTemplateInfo) GetDescription() string { + if x != nil { + return x.Description + } + return "" +} + +func (x *WorkflowTemplateInfo) GetPath() string { + if x != nil { + return x.Path + } + return "" +} + +func (x *WorkflowTemplateInfo) GetPublic() bool { + if x != nil { + return x.Public + } + return false +} + +// * +// Beam information at a specific point in time (e.g. start or end of stable beams) +type BeamInfo struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + StableBeamsStart int64 `protobuf:"varint,1,opt,name=stableBeamsStart,proto3" json:"stableBeamsStart,omitempty"` // milliseconds since epoch when stable beams started + StableBeamsEnd int64 `protobuf:"varint,2,opt,name=stableBeamsEnd,proto3" json:"stableBeamsEnd,omitempty"` // milliseconds since epoch when stable beams ended + FillNumber int32 `protobuf:"varint,3,opt,name=fillNumber,proto3" json:"fillNumber,omitempty"` // LHC fill number + FillingSchemeName string `protobuf:"bytes,4,opt,name=fillingSchemeName,proto3" json:"fillingSchemeName,omitempty"` // LHC filling scheme name e.g. 25ns_2460b_2448_2089_2227_144bpi_20inj + BeamType string `protobuf:"bytes,5,opt,name=beamType,proto3" json:"beamType,omitempty"` // e.g. PROTON-PROTON, O8-O8, Pb-Pb, p-Pb, Pb-p + BeamMode BeamMode `protobuf:"varint,6,opt,name=beamMode,proto3,enum=common.BeamMode" json:"beamMode,omitempty"` +} + +func (x *BeamInfo) Reset() { + *x = BeamInfo{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_common_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *BeamInfo) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BeamInfo) ProtoMessage() {} + +func (x *BeamInfo) ProtoReflect() protoreflect.Message { + mi := &file_protos_common_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BeamInfo.ProtoReflect.Descriptor instead. +func (*BeamInfo) Descriptor() ([]byte, []int) { + return file_protos_common_proto_rawDescGZIP(), []int{2} +} + +func (x *BeamInfo) GetStableBeamsStart() int64 { + if x != nil { + return x.StableBeamsStart + } + return 0 +} + +func (x *BeamInfo) GetStableBeamsEnd() int64 { + if x != nil { + return x.StableBeamsEnd + } + return 0 +} + +func (x *BeamInfo) GetFillNumber() int32 { + if x != nil { + return x.FillNumber + } + return 0 +} + +func (x *BeamInfo) GetFillingSchemeName() string { + if x != nil { + return x.FillingSchemeName + } + return "" +} + +func (x *BeamInfo) GetBeamType() string { + if x != nil { + return x.BeamType + } + return "" +} + +func (x *BeamInfo) GetBeamMode() BeamMode { + if x != nil { + return x.BeamMode + } + return BeamMode_UNKNOWN +} + +var File_protos_common_proto protoreflect.FileDescriptor + +var file_protos_common_proto_rawDesc = []byte{ + 0x0a, 0x13, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x06, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x22, 0x6a, 0x0a, + 0x04, 0x55, 0x73, 0x65, 0x72, 0x12, 0x23, 0x0a, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, + 0x6c, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x48, 0x00, 0x52, 0x0a, 0x65, 0x78, 0x74, + 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x49, 0x64, 0x88, 0x01, 0x01, 0x12, 0x13, 0x0a, 0x02, 0x69, 0x64, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x48, 0x01, 0x52, 0x02, 0x69, 0x64, 0x88, 0x01, 0x01, 0x12, + 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, + 0x49, 0x64, 0x42, 0x05, 0x0a, 0x03, 0x5f, 0x69, 0x64, 0x22, 0x78, 0x0a, 0x14, 0x57, 0x6f, 0x72, + 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x49, 0x6e, 0x66, + 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x16, 0x0a, 0x06, 0x70, + 0x75, 0x62, 0x6c, 0x69, 0x63, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x70, 0x75, 0x62, + 0x6c, 0x69, 0x63, 0x22, 0xf6, 0x01, 0x0a, 0x08, 0x42, 0x65, 0x61, 0x6d, 0x49, 0x6e, 0x66, 0x6f, + 0x12, 0x2a, 0x0a, 0x10, 0x73, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x42, 0x65, 0x61, 0x6d, 0x73, 0x53, + 0x74, 0x61, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x10, 0x73, 0x74, 0x61, 0x62, + 0x6c, 0x65, 0x42, 0x65, 0x61, 0x6d, 0x73, 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x26, 0x0a, 0x0e, + 0x73, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x42, 0x65, 0x61, 0x6d, 0x73, 0x45, 0x6e, 0x64, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x03, 0x52, 0x0e, 0x73, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x42, 0x65, 0x61, 0x6d, + 0x73, 0x45, 0x6e, 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x66, 0x69, 0x6c, 0x6c, 0x4e, 0x75, 0x6d, 0x62, + 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x66, 0x69, 0x6c, 0x6c, 0x4e, 0x75, + 0x6d, 0x62, 0x65, 0x72, 0x12, 0x2c, 0x0a, 0x11, 0x66, 0x69, 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x53, + 0x63, 0x68, 0x65, 0x6d, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x11, 0x66, 0x69, 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x65, 0x4e, 0x61, + 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x62, 0x65, 0x61, 0x6d, 0x54, 0x79, 0x70, 0x65, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x62, 0x65, 0x61, 0x6d, 0x54, 0x79, 0x70, 0x65, 0x12, 0x2c, + 0x0a, 0x08, 0x62, 0x65, 0x61, 0x6d, 0x4d, 0x6f, 0x64, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, + 0x32, 0x10, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x42, 0x65, 0x61, 0x6d, 0x4d, 0x6f, + 0x64, 0x65, 0x52, 0x08, 0x62, 0x65, 0x61, 0x6d, 0x4d, 0x6f, 0x64, 0x65, 0x2a, 0x30, 0x0a, 0x0d, + 0x53, 0x70, 0x65, 0x63, 0x69, 0x61, 0x6c, 0x55, 0x73, 0x65, 0x72, 0x49, 0x64, 0x12, 0x0b, 0x0a, + 0x07, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x4c, 0x48, + 0x43, 0x10, 0x3c, 0x12, 0x09, 0x0a, 0x05, 0x54, 0x49, 0x4d, 0x45, 0x52, 0x10, 0x41, 0x2a, 0x80, + 0x03, 0x0a, 0x08, 0x42, 0x65, 0x61, 0x6d, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x55, + 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x53, 0x45, 0x54, 0x55, + 0x50, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x41, 0x42, 0x4f, 0x52, 0x54, 0x10, 0x02, 0x12, 0x18, + 0x0a, 0x14, 0x49, 0x4e, 0x4a, 0x45, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x50, 0x52, 0x4f, 0x42, + 0x45, 0x5f, 0x42, 0x45, 0x41, 0x4d, 0x10, 0x03, 0x12, 0x18, 0x0a, 0x14, 0x49, 0x4e, 0x4a, 0x45, + 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x45, 0x54, 0x55, 0x50, 0x5f, 0x42, 0x45, 0x41, 0x4d, + 0x10, 0x04, 0x12, 0x1a, 0x0a, 0x16, 0x49, 0x4e, 0x4a, 0x45, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, + 0x50, 0x48, 0x59, 0x53, 0x49, 0x43, 0x53, 0x5f, 0x42, 0x45, 0x41, 0x4d, 0x10, 0x05, 0x12, 0x10, + 0x0a, 0x0c, 0x50, 0x52, 0x45, 0x50, 0x41, 0x52, 0x45, 0x5f, 0x52, 0x41, 0x4d, 0x50, 0x10, 0x06, + 0x12, 0x08, 0x0a, 0x04, 0x52, 0x41, 0x4d, 0x50, 0x10, 0x07, 0x12, 0x0c, 0x0a, 0x08, 0x46, 0x4c, + 0x41, 0x54, 0x5f, 0x54, 0x4f, 0x50, 0x10, 0x08, 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x51, 0x55, 0x45, + 0x45, 0x5a, 0x45, 0x10, 0x09, 0x12, 0x0a, 0x0a, 0x06, 0x41, 0x44, 0x4a, 0x55, 0x53, 0x54, 0x10, + 0x0a, 0x12, 0x10, 0x0a, 0x0c, 0x53, 0x54, 0x41, 0x42, 0x4c, 0x45, 0x5f, 0x42, 0x45, 0x41, 0x4d, + 0x53, 0x10, 0x0b, 0x12, 0x0e, 0x0a, 0x0a, 0x4c, 0x4f, 0x53, 0x54, 0x5f, 0x42, 0x45, 0x41, 0x4d, + 0x53, 0x10, 0x0c, 0x12, 0x12, 0x0a, 0x0e, 0x55, 0x4e, 0x53, 0x54, 0x41, 0x42, 0x4c, 0x45, 0x5f, + 0x42, 0x45, 0x41, 0x4d, 0x53, 0x10, 0x0d, 0x12, 0x15, 0x0a, 0x11, 0x42, 0x45, 0x41, 0x4d, 0x5f, + 0x44, 0x55, 0x4d, 0x50, 0x5f, 0x57, 0x41, 0x52, 0x4e, 0x49, 0x4e, 0x47, 0x10, 0x0e, 0x12, 0x0d, + 0x0a, 0x09, 0x42, 0x45, 0x41, 0x4d, 0x5f, 0x44, 0x55, 0x4d, 0x50, 0x10, 0x0f, 0x12, 0x0d, 0x0a, + 0x09, 0x52, 0x41, 0x4d, 0x50, 0x5f, 0x44, 0x4f, 0x57, 0x4e, 0x10, 0x10, 0x12, 0x0b, 0x0a, 0x07, + 0x43, 0x59, 0x43, 0x4c, 0x49, 0x4e, 0x47, 0x10, 0x11, 0x12, 0x0c, 0x0a, 0x08, 0x52, 0x45, 0x43, + 0x4f, 0x56, 0x45, 0x52, 0x59, 0x10, 0x12, 0x12, 0x13, 0x0a, 0x0f, 0x49, 0x4e, 0x4a, 0x45, 0x43, + 0x54, 0x5f, 0x41, 0x4e, 0x44, 0x5f, 0x44, 0x55, 0x4d, 0x50, 0x10, 0x13, 0x12, 0x16, 0x0a, 0x12, + 0x43, 0x49, 0x52, 0x43, 0x55, 0x4c, 0x41, 0x54, 0x45, 0x5f, 0x41, 0x4e, 0x44, 0x5f, 0x44, 0x55, + 0x4d, 0x50, 0x10, 0x14, 0x12, 0x0b, 0x0a, 0x07, 0x4e, 0x4f, 0x5f, 0x42, 0x45, 0x41, 0x4d, 0x10, + 0x15, 0x42, 0x53, 0x0a, 0x1f, 0x63, 0x68, 0x2e, 0x63, 0x65, 0x72, 0x6e, 0x2e, 0x61, 0x6c, 0x69, + 0x63, 0x65, 0x2e, 0x6f, 0x32, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x63, 0x6f, + 0x6d, 0x6d, 0x6f, 0x6e, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, + 0x2f, 0x41, 0x6c, 0x69, 0x63, 0x65, 0x4f, 0x32, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x2f, 0x43, 0x6f, + 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x73, 0x3b, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_protos_common_proto_rawDescOnce sync.Once + file_protos_common_proto_rawDescData = file_protos_common_proto_rawDesc +) + +func file_protos_common_proto_rawDescGZIP() []byte { + file_protos_common_proto_rawDescOnce.Do(func() { + file_protos_common_proto_rawDescData = protoimpl.X.CompressGZIP(file_protos_common_proto_rawDescData) + }) + return file_protos_common_proto_rawDescData +} + +var file_protos_common_proto_enumTypes = make([]protoimpl.EnumInfo, 2) +var file_protos_common_proto_msgTypes = make([]protoimpl.MessageInfo, 3) +var file_protos_common_proto_goTypes = []interface{}{ + (SpecialUserId)(0), // 0: common.SpecialUserId + (BeamMode)(0), // 1: common.BeamMode + (*User)(nil), // 2: common.User + (*WorkflowTemplateInfo)(nil), // 3: common.WorkflowTemplateInfo + (*BeamInfo)(nil), // 4: common.BeamInfo +} +var file_protos_common_proto_depIdxs = []int32{ + 1, // 0: common.BeamInfo.beamMode:type_name -> common.BeamMode + 1, // [1:1] is the sub-list for method output_type + 1, // [1:1] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name +} + +func init() { file_protos_common_proto_init() } +func file_protos_common_proto_init() { + if File_protos_common_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_protos_common_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*User); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_common_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*WorkflowTemplateInfo); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_common_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*BeamInfo); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + file_protos_common_proto_msgTypes[0].OneofWrappers = []interface{}{} + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_protos_common_proto_rawDesc, + NumEnums: 2, + NumMessages: 3, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_protos_common_proto_goTypes, + DependencyIndexes: file_protos_common_proto_depIdxs, + EnumInfos: file_protos_common_proto_enumTypes, + MessageInfos: file_protos_common_proto_msgTypes, + }.Build() + File_protos_common_proto = out.File + file_protos_common_proto_rawDesc = nil + file_protos_common_proto_goTypes = nil + file_protos_common_proto_depIdxs = nil +} diff --git a/common/protos/common.proto b/common/protos/common.proto new file mode 100644 index 000000000..1942c36e2 --- /dev/null +++ b/common/protos/common.proto @@ -0,0 +1,100 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2024 CERN and copyright holders of ALICE O². + * Author: Teo Mrnjavac + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + + +syntax = "proto3"; + +package common; +option java_package = "ch.cern.alice.o2.control.common"; +option go_package = "github.com/AliceO2Group/Control/common/protos;pb"; + +//////////////// Common types /////////////// + +message User { + // The unique CERN identifier of this user. + optional int32 externalId = 1; + // The unique identifier of this entity. + optional int32 id = 2; + // Name of the user. + string name = 3; +} + +// Production and staging BKP recognizes these special user IDs. +// They are used to associate certain actions (e.g. EOR) with certain subsystem +// or AliECS component rather than a human user. +enum SpecialUserId { + INVALID = 0; + LHC = 60; + TIMER = 65; +} + +message WorkflowTemplateInfo { + string name = 1; + string description = 2; + string path = 3; + bool public = 4; // whether the environment is public or not +} + +/** + * Beam information at a specific point in time (e.g. start or end of stable beams) + */ +message BeamInfo { + int64 stableBeamsStart = 1; // milliseconds since epoch when stable beams started + int64 stableBeamsEnd = 2; // milliseconds since epoch when stable beams ended + int32 fillNumber = 3; // LHC fill number + string fillingSchemeName = 4; // LHC filling scheme name e.g. 25ns_2460b_2448_2089_2227_144bpi_20inj + string beamType = 5; // e.g. PROTON-PROTON, O8-O8, Pb-Pb, p-Pb, Pb-p + BeamMode beamMode = 6; +} + +/** + * Beam modes as defined and sent by LHC DIP client plus: + * * virtual type LOST_BEAMS - that is generated when beam 1 and beam 2 energy values are not equal anymore as per LHC DIP track: dip/acc/LHC/RunControl/SafeBeam + * * virtual type UNKNOWN - that is generated when there is no beam in the machine or value not added by the BKP-LHC Client + * Source of Beam Modes: https://lhc-commissioning.web.cern.ch/systems/data-exchange/doc/LHC-OP-ES-0005-10-00.pdf + */ +enum BeamMode { + UNKNOWN = 0; // virtual type + SETUP = 1; + ABORT = 2; + INJECTION_PROBE_BEAM = 3; + INJECTION_SETUP_BEAM = 4; + INJECTION_PHYSICS_BEAM = 5; + PREPARE_RAMP = 6; + RAMP = 7; + FLAT_TOP = 8; + SQUEEZE = 9; + ADJUST = 10; + STABLE_BEAMS = 11; + LOST_BEAMS = 12; // virtual type + UNSTABLE_BEAMS = 13; + BEAM_DUMP_WARNING = 14; + BEAM_DUMP = 15; + RAMP_DOWN = 16; + CYCLING = 17; + RECOVERY = 18; + INJECT_AND_DUMP = 19; + CIRCULATE_AND_DUMP = 20; + NO_BEAM = 21; +} diff --git a/common/protos/events.pb.go b/common/protos/events.pb.go new file mode 100644 index 000000000..65d690048 --- /dev/null +++ b/common/protos/events.pb.go @@ -0,0 +1,1659 @@ +// +// === This file is part of ALICE O² === +// +// Copyright 2024 CERN and copyright holders of ALICE O². +// Author: Teo Mrnjavac +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// In applying this license CERN does not waive the privileges and +// immunities granted to it by virtue of its status as an +// Intergovernmental Organization or submit itself to any jurisdiction. + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.34.1 +// protoc v5.29.3 +// source: protos/events.proto + +package pb + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type OpStatus int32 + +const ( + OpStatus_NULL OpStatus = 0 + OpStatus_STARTED OpStatus = 1 + OpStatus_ONGOING OpStatus = 2 + OpStatus_DONE_OK OpStatus = 3 + OpStatus_DONE_ERROR OpStatus = 4 + OpStatus_DONE_TIMEOUT OpStatus = 5 +) + +// Enum value maps for OpStatus. +var ( + OpStatus_name = map[int32]string{ + 0: "NULL", + 1: "STARTED", + 2: "ONGOING", + 3: "DONE_OK", + 4: "DONE_ERROR", + 5: "DONE_TIMEOUT", + } + OpStatus_value = map[string]int32{ + "NULL": 0, + "STARTED": 1, + "ONGOING": 2, + "DONE_OK": 3, + "DONE_ERROR": 4, + "DONE_TIMEOUT": 5, + } +) + +func (x OpStatus) Enum() *OpStatus { + p := new(OpStatus) + *p = x + return p +} + +func (x OpStatus) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (OpStatus) Descriptor() protoreflect.EnumDescriptor { + return file_protos_events_proto_enumTypes[0].Descriptor() +} + +func (OpStatus) Type() protoreflect.EnumType { + return &file_protos_events_proto_enumTypes[0] +} + +func (x OpStatus) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use OpStatus.Descriptor instead. +func (OpStatus) EnumDescriptor() ([]byte, []int) { + return file_protos_events_proto_rawDescGZIP(), []int{0} +} + +type Ev_MetaEvent_MesosHeartbeat struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *Ev_MetaEvent_MesosHeartbeat) Reset() { + *x = Ev_MetaEvent_MesosHeartbeat{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_events_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Ev_MetaEvent_MesosHeartbeat) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Ev_MetaEvent_MesosHeartbeat) ProtoMessage() {} + +func (x *Ev_MetaEvent_MesosHeartbeat) ProtoReflect() protoreflect.Message { + mi := &file_protos_events_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Ev_MetaEvent_MesosHeartbeat.ProtoReflect.Descriptor instead. +func (*Ev_MetaEvent_MesosHeartbeat) Descriptor() ([]byte, []int) { + return file_protos_events_proto_rawDescGZIP(), []int{0} +} + +type Ev_MetaEvent_CoreStart struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + FrameworkId string `protobuf:"bytes,1,opt,name=frameworkId,proto3" json:"frameworkId,omitempty"` +} + +func (x *Ev_MetaEvent_CoreStart) Reset() { + *x = Ev_MetaEvent_CoreStart{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_events_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Ev_MetaEvent_CoreStart) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Ev_MetaEvent_CoreStart) ProtoMessage() {} + +func (x *Ev_MetaEvent_CoreStart) ProtoReflect() protoreflect.Message { + mi := &file_protos_events_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Ev_MetaEvent_CoreStart.ProtoReflect.Descriptor instead. +func (*Ev_MetaEvent_CoreStart) Descriptor() ([]byte, []int) { + return file_protos_events_proto_rawDescGZIP(), []int{1} +} + +func (x *Ev_MetaEvent_CoreStart) GetFrameworkId() string { + if x != nil { + return x.FrameworkId + } + return "" +} + +type Ev_MetaEvent_FrameworkEvent struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + FrameworkId string `protobuf:"bytes,1,opt,name=frameworkId,proto3" json:"frameworkId,omitempty"` + Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"` +} + +func (x *Ev_MetaEvent_FrameworkEvent) Reset() { + *x = Ev_MetaEvent_FrameworkEvent{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_events_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Ev_MetaEvent_FrameworkEvent) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Ev_MetaEvent_FrameworkEvent) ProtoMessage() {} + +func (x *Ev_MetaEvent_FrameworkEvent) ProtoReflect() protoreflect.Message { + mi := &file_protos_events_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Ev_MetaEvent_FrameworkEvent.ProtoReflect.Descriptor instead. +func (*Ev_MetaEvent_FrameworkEvent) Descriptor() ([]byte, []int) { + return file_protos_events_proto_rawDescGZIP(), []int{2} +} + +func (x *Ev_MetaEvent_FrameworkEvent) GetFrameworkId() string { + if x != nil { + return x.FrameworkId + } + return "" +} + +func (x *Ev_MetaEvent_FrameworkEvent) GetMessage() string { + if x != nil { + return x.Message + } + return "" +} + +type Ev_EnvironmentEvent struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + EnvironmentId string `protobuf:"bytes,1,opt,name=environmentId,proto3" json:"environmentId,omitempty"` + State string `protobuf:"bytes,2,opt,name=state,proto3" json:"state,omitempty"` + RunNumber uint32 `protobuf:"varint,3,opt,name=runNumber,proto3" json:"runNumber,omitempty"` // only when the environment is in the running state + Error string `protobuf:"bytes,4,opt,name=error,proto3" json:"error,omitempty"` + Message string `protobuf:"bytes,5,opt,name=message,proto3" json:"message,omitempty"` // any additional message concerning the current state or transition + Transition string `protobuf:"bytes,6,opt,name=transition,proto3" json:"transition,omitempty"` + TransitionStep string `protobuf:"bytes,7,opt,name=transitionStep,proto3" json:"transitionStep,omitempty"` + TransitionStatus OpStatus `protobuf:"varint,8,opt,name=transitionStatus,proto3,enum=events.OpStatus" json:"transitionStatus,omitempty"` + Vars map[string]string `protobuf:"bytes,9,rep,name=vars,proto3" json:"vars,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` // consolidated environment variables at the root role of the environment + LastRequestUser *User `protobuf:"bytes,10,opt,name=lastRequestUser,proto3" json:"lastRequestUser,omitempty"` + WorkflowTemplateInfo *WorkflowTemplateInfo `protobuf:"bytes,11,opt,name=workflowTemplateInfo,proto3" json:"workflowTemplateInfo,omitempty"` +} + +func (x *Ev_EnvironmentEvent) Reset() { + *x = Ev_EnvironmentEvent{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_events_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Ev_EnvironmentEvent) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Ev_EnvironmentEvent) ProtoMessage() {} + +func (x *Ev_EnvironmentEvent) ProtoReflect() protoreflect.Message { + mi := &file_protos_events_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Ev_EnvironmentEvent.ProtoReflect.Descriptor instead. +func (*Ev_EnvironmentEvent) Descriptor() ([]byte, []int) { + return file_protos_events_proto_rawDescGZIP(), []int{3} +} + +func (x *Ev_EnvironmentEvent) GetEnvironmentId() string { + if x != nil { + return x.EnvironmentId + } + return "" +} + +func (x *Ev_EnvironmentEvent) GetState() string { + if x != nil { + return x.State + } + return "" +} + +func (x *Ev_EnvironmentEvent) GetRunNumber() uint32 { + if x != nil { + return x.RunNumber + } + return 0 +} + +func (x *Ev_EnvironmentEvent) GetError() string { + if x != nil { + return x.Error + } + return "" +} + +func (x *Ev_EnvironmentEvent) GetMessage() string { + if x != nil { + return x.Message + } + return "" +} + +func (x *Ev_EnvironmentEvent) GetTransition() string { + if x != nil { + return x.Transition + } + return "" +} + +func (x *Ev_EnvironmentEvent) GetTransitionStep() string { + if x != nil { + return x.TransitionStep + } + return "" +} + +func (x *Ev_EnvironmentEvent) GetTransitionStatus() OpStatus { + if x != nil { + return x.TransitionStatus + } + return OpStatus_NULL +} + +func (x *Ev_EnvironmentEvent) GetVars() map[string]string { + if x != nil { + return x.Vars + } + return nil +} + +func (x *Ev_EnvironmentEvent) GetLastRequestUser() *User { + if x != nil { + return x.LastRequestUser + } + return nil +} + +func (x *Ev_EnvironmentEvent) GetWorkflowTemplateInfo() *WorkflowTemplateInfo { + if x != nil { + return x.WorkflowTemplateInfo + } + return nil +} + +type Traits struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Trigger string `protobuf:"bytes,1,opt,name=trigger,proto3" json:"trigger,omitempty"` + Await string `protobuf:"bytes,2,opt,name=await,proto3" json:"await,omitempty"` + Timeout string `protobuf:"bytes,3,opt,name=timeout,proto3" json:"timeout,omitempty"` + Critical bool `protobuf:"varint,4,opt,name=critical,proto3" json:"critical,omitempty"` +} + +func (x *Traits) Reset() { + *x = Traits{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_events_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Traits) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Traits) ProtoMessage() {} + +func (x *Traits) ProtoReflect() protoreflect.Message { + mi := &file_protos_events_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Traits.ProtoReflect.Descriptor instead. +func (*Traits) Descriptor() ([]byte, []int) { + return file_protos_events_proto_rawDescGZIP(), []int{4} +} + +func (x *Traits) GetTrigger() string { + if x != nil { + return x.Trigger + } + return "" +} + +func (x *Traits) GetAwait() string { + if x != nil { + return x.Await + } + return "" +} + +func (x *Traits) GetTimeout() string { + if x != nil { + return x.Timeout + } + return "" +} + +func (x *Traits) GetCritical() bool { + if x != nil { + return x.Critical + } + return false +} + +type Ev_TaskEvent struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` // task name, based on the name of the task class + Taskid string `protobuf:"bytes,2,opt,name=taskid,proto3" json:"taskid,omitempty"` // task id, unique + State string `protobuf:"bytes,3,opt,name=state,proto3" json:"state,omitempty"` // state machine state for this task + Status string `protobuf:"bytes,4,opt,name=status,proto3" json:"status,omitempty"` // posible values: ACTIVE/INACTIVE/PARTIAL/UNDEFINED/UNDEPLOYABLE as defined in status.go. + Hostname string `protobuf:"bytes,5,opt,name=hostname,proto3" json:"hostname,omitempty"` + ClassName string `protobuf:"bytes,6,opt,name=className,proto3" json:"className,omitempty"` // name of the task class from which this task was spawned + Traits *Traits `protobuf:"bytes,7,opt,name=traits,proto3" json:"traits,omitempty"` + EnvironmentId string `protobuf:"bytes,8,opt,name=environmentId,proto3" json:"environmentId,omitempty"` + Path string `protobuf:"bytes,9,opt,name=path,proto3" json:"path,omitempty"` // path to the parent taskRole of this task within the environment +} + +func (x *Ev_TaskEvent) Reset() { + *x = Ev_TaskEvent{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_events_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Ev_TaskEvent) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Ev_TaskEvent) ProtoMessage() {} + +func (x *Ev_TaskEvent) ProtoReflect() protoreflect.Message { + mi := &file_protos_events_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Ev_TaskEvent.ProtoReflect.Descriptor instead. +func (*Ev_TaskEvent) Descriptor() ([]byte, []int) { + return file_protos_events_proto_rawDescGZIP(), []int{5} +} + +func (x *Ev_TaskEvent) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Ev_TaskEvent) GetTaskid() string { + if x != nil { + return x.Taskid + } + return "" +} + +func (x *Ev_TaskEvent) GetState() string { + if x != nil { + return x.State + } + return "" +} + +func (x *Ev_TaskEvent) GetStatus() string { + if x != nil { + return x.Status + } + return "" +} + +func (x *Ev_TaskEvent) GetHostname() string { + if x != nil { + return x.Hostname + } + return "" +} + +func (x *Ev_TaskEvent) GetClassName() string { + if x != nil { + return x.ClassName + } + return "" +} + +func (x *Ev_TaskEvent) GetTraits() *Traits { + if x != nil { + return x.Traits + } + return nil +} + +func (x *Ev_TaskEvent) GetEnvironmentId() string { + if x != nil { + return x.EnvironmentId + } + return "" +} + +func (x *Ev_TaskEvent) GetPath() string { + if x != nil { + return x.Path + } + return "" +} + +type Ev_CallEvent struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Func string `protobuf:"bytes,1,opt,name=func,proto3" json:"func,omitempty"` // name of the function being called, within the workflow template context + CallStatus OpStatus `protobuf:"varint,2,opt,name=callStatus,proto3,enum=events.OpStatus" json:"callStatus,omitempty"` // progress or success/failure state of the call + Return string `protobuf:"bytes,3,opt,name=return,proto3" json:"return,omitempty"` // return value of the function + Traits *Traits `protobuf:"bytes,4,opt,name=traits,proto3" json:"traits,omitempty"` + Output string `protobuf:"bytes,5,opt,name=output,proto3" json:"output,omitempty"` // any additional output of the function + Error string `protobuf:"bytes,6,opt,name=error,proto3" json:"error,omitempty"` // error value, if returned + EnvironmentId string `protobuf:"bytes,7,opt,name=environmentId,proto3" json:"environmentId,omitempty"` + Path string `protobuf:"bytes,8,opt,name=path,proto3" json:"path,omitempty"` // path to the parent callRole of this call within the environment +} + +func (x *Ev_CallEvent) Reset() { + *x = Ev_CallEvent{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_events_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Ev_CallEvent) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Ev_CallEvent) ProtoMessage() {} + +func (x *Ev_CallEvent) ProtoReflect() protoreflect.Message { + mi := &file_protos_events_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Ev_CallEvent.ProtoReflect.Descriptor instead. +func (*Ev_CallEvent) Descriptor() ([]byte, []int) { + return file_protos_events_proto_rawDescGZIP(), []int{6} +} + +func (x *Ev_CallEvent) GetFunc() string { + if x != nil { + return x.Func + } + return "" +} + +func (x *Ev_CallEvent) GetCallStatus() OpStatus { + if x != nil { + return x.CallStatus + } + return OpStatus_NULL +} + +func (x *Ev_CallEvent) GetReturn() string { + if x != nil { + return x.Return + } + return "" +} + +func (x *Ev_CallEvent) GetTraits() *Traits { + if x != nil { + return x.Traits + } + return nil +} + +func (x *Ev_CallEvent) GetOutput() string { + if x != nil { + return x.Output + } + return "" +} + +func (x *Ev_CallEvent) GetError() string { + if x != nil { + return x.Error + } + return "" +} + +func (x *Ev_CallEvent) GetEnvironmentId() string { + if x != nil { + return x.EnvironmentId + } + return "" +} + +func (x *Ev_CallEvent) GetPath() string { + if x != nil { + return x.Path + } + return "" +} + +type Ev_RoleEvent struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` // role name + Status string `protobuf:"bytes,2,opt,name=status,proto3" json:"status,omitempty"` // posible values: ACTIVE/INACTIVE/PARTIAL/UNDEFINED/UNDEPLOYABLE as defined in status.go. Derived from the state of child tasks, calls or other roles + State string `protobuf:"bytes,3,opt,name=state,proto3" json:"state,omitempty"` // state machine state for this role + RolePath string `protobuf:"bytes,4,opt,name=rolePath,proto3" json:"rolePath,omitempty"` // path to this role within the environment + EnvironmentId string `protobuf:"bytes,5,opt,name=environmentId,proto3" json:"environmentId,omitempty"` +} + +func (x *Ev_RoleEvent) Reset() { + *x = Ev_RoleEvent{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_events_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Ev_RoleEvent) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Ev_RoleEvent) ProtoMessage() {} + +func (x *Ev_RoleEvent) ProtoReflect() protoreflect.Message { + mi := &file_protos_events_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Ev_RoleEvent.ProtoReflect.Descriptor instead. +func (*Ev_RoleEvent) Descriptor() ([]byte, []int) { + return file_protos_events_proto_rawDescGZIP(), []int{7} +} + +func (x *Ev_RoleEvent) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Ev_RoleEvent) GetStatus() string { + if x != nil { + return x.Status + } + return "" +} + +func (x *Ev_RoleEvent) GetState() string { + if x != nil { + return x.State + } + return "" +} + +func (x *Ev_RoleEvent) GetRolePath() string { + if x != nil { + return x.RolePath + } + return "" +} + +func (x *Ev_RoleEvent) GetEnvironmentId() string { + if x != nil { + return x.EnvironmentId + } + return "" +} + +type Ev_IntegratedServiceEvent struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` // name of the context, usually the path of the callRole that calls a given integrated service function e.g. readout-dataflow.dd-scheduler.terminate + Error string `protobuf:"bytes,2,opt,name=error,proto3" json:"error,omitempty"` // error message, if any + OperationName string `protobuf:"bytes,3,opt,name=operationName,proto3" json:"operationName,omitempty"` // name of the operation, usually the name of the integrated service function being called e.g. ddsched.PartitionTerminate()" + OperationStatus OpStatus `protobuf:"varint,4,opt,name=operationStatus,proto3,enum=events.OpStatus" json:"operationStatus,omitempty"` // progress or success/failure state of the operation + OperationStep string `protobuf:"bytes,5,opt,name=operationStep,proto3" json:"operationStep,omitempty"` // if the operation has substeps, this is the name of the current substep, like an API call or polling phase + OperationStepStatus OpStatus `protobuf:"varint,6,opt,name=operationStepStatus,proto3,enum=events.OpStatus" json:"operationStepStatus,omitempty"` // progress or success/failure state of the current substep + EnvironmentId string `protobuf:"bytes,7,opt,name=environmentId,proto3" json:"environmentId,omitempty"` + Payload string `protobuf:"bytes,8,opt,name=payload,proto3" json:"payload,omitempty"` // any additional payload, depending on the integrated service; there is no schema, it can even be the raw return structure of a remote API call +} + +func (x *Ev_IntegratedServiceEvent) Reset() { + *x = Ev_IntegratedServiceEvent{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_events_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Ev_IntegratedServiceEvent) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Ev_IntegratedServiceEvent) ProtoMessage() {} + +func (x *Ev_IntegratedServiceEvent) ProtoReflect() protoreflect.Message { + mi := &file_protos_events_proto_msgTypes[8] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Ev_IntegratedServiceEvent.ProtoReflect.Descriptor instead. +func (*Ev_IntegratedServiceEvent) Descriptor() ([]byte, []int) { + return file_protos_events_proto_rawDescGZIP(), []int{8} +} + +func (x *Ev_IntegratedServiceEvent) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Ev_IntegratedServiceEvent) GetError() string { + if x != nil { + return x.Error + } + return "" +} + +func (x *Ev_IntegratedServiceEvent) GetOperationName() string { + if x != nil { + return x.OperationName + } + return "" +} + +func (x *Ev_IntegratedServiceEvent) GetOperationStatus() OpStatus { + if x != nil { + return x.OperationStatus + } + return OpStatus_NULL +} + +func (x *Ev_IntegratedServiceEvent) GetOperationStep() string { + if x != nil { + return x.OperationStep + } + return "" +} + +func (x *Ev_IntegratedServiceEvent) GetOperationStepStatus() OpStatus { + if x != nil { + return x.OperationStepStatus + } + return OpStatus_NULL +} + +func (x *Ev_IntegratedServiceEvent) GetEnvironmentId() string { + if x != nil { + return x.EnvironmentId + } + return "" +} + +func (x *Ev_IntegratedServiceEvent) GetPayload() string { + if x != nil { + return x.Payload + } + return "" +} + +type Ev_RunEvent struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + EnvironmentId string `protobuf:"bytes,1,opt,name=environmentId,proto3" json:"environmentId,omitempty"` + RunNumber uint32 `protobuf:"varint,2,opt,name=runNumber,proto3" json:"runNumber,omitempty"` + State string `protobuf:"bytes,3,opt,name=state,proto3" json:"state,omitempty"` + Error string `protobuf:"bytes,4,opt,name=error,proto3" json:"error,omitempty"` + Transition string `protobuf:"bytes,5,opt,name=transition,proto3" json:"transition,omitempty"` + TransitionStatus OpStatus `protobuf:"varint,6,opt,name=transitionStatus,proto3,enum=events.OpStatus" json:"transitionStatus,omitempty"` + LastRequestUser *User `protobuf:"bytes,8,opt,name=lastRequestUser,proto3" json:"lastRequestUser,omitempty"` +} + +func (x *Ev_RunEvent) Reset() { + *x = Ev_RunEvent{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_events_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Ev_RunEvent) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Ev_RunEvent) ProtoMessage() {} + +func (x *Ev_RunEvent) ProtoReflect() protoreflect.Message { + mi := &file_protos_events_proto_msgTypes[9] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Ev_RunEvent.ProtoReflect.Descriptor instead. +func (*Ev_RunEvent) Descriptor() ([]byte, []int) { + return file_protos_events_proto_rawDescGZIP(), []int{9} +} + +func (x *Ev_RunEvent) GetEnvironmentId() string { + if x != nil { + return x.EnvironmentId + } + return "" +} + +func (x *Ev_RunEvent) GetRunNumber() uint32 { + if x != nil { + return x.RunNumber + } + return 0 +} + +func (x *Ev_RunEvent) GetState() string { + if x != nil { + return x.State + } + return "" +} + +func (x *Ev_RunEvent) GetError() string { + if x != nil { + return x.Error + } + return "" +} + +func (x *Ev_RunEvent) GetTransition() string { + if x != nil { + return x.Transition + } + return "" +} + +func (x *Ev_RunEvent) GetTransitionStatus() OpStatus { + if x != nil { + return x.TransitionStatus + } + return OpStatus_NULL +} + +func (x *Ev_RunEvent) GetLastRequestUser() *User { + if x != nil { + return x.LastRequestUser + } + return nil +} + +// * +// Beam mode changes are propagated as Kafka events and to be sent by the BKP-LHC-Client on a dedicated topic +// e.g. dip.lhc.beam_mode +type Ev_BeamModeEvent struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Timestamp int64 `protobuf:"varint,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"` // milliseconds since epoch when the beam mode change happened + BeamInfo *BeamInfo `protobuf:"bytes,2,opt,name=beamInfo,proto3" json:"beamInfo,omitempty"` +} + +func (x *Ev_BeamModeEvent) Reset() { + *x = Ev_BeamModeEvent{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_events_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Ev_BeamModeEvent) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Ev_BeamModeEvent) ProtoMessage() {} + +func (x *Ev_BeamModeEvent) ProtoReflect() protoreflect.Message { + mi := &file_protos_events_proto_msgTypes[10] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Ev_BeamModeEvent.ProtoReflect.Descriptor instead. +func (*Ev_BeamModeEvent) Descriptor() ([]byte, []int) { + return file_protos_events_proto_rawDescGZIP(), []int{10} +} + +func (x *Ev_BeamModeEvent) GetTimestamp() int64 { + if x != nil { + return x.Timestamp + } + return 0 +} + +func (x *Ev_BeamModeEvent) GetBeamInfo() *BeamInfo { + if x != nil { + return x.BeamInfo + } + return nil +} + +type Event struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Timestamp int64 `protobuf:"varint,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + TimestampNano int64 `protobuf:"varint,2,opt,name=timestampNano,proto3" json:"timestampNano,omitempty"` + // Types that are assignable to Payload: + // + // *Event_EnvironmentEvent + // *Event_TaskEvent + // *Event_RoleEvent + // *Event_CallEvent + // *Event_IntegratedServiceEvent + // *Event_RunEvent + // *Event_FrameworkEvent + // *Event_MesosHeartbeatEvent + // *Event_CoreStartEvent + // *Event_BeamModeEvent + Payload isEvent_Payload `protobuf_oneof:"Payload"` +} + +func (x *Event) Reset() { + *x = Event{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_events_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Event) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Event) ProtoMessage() {} + +func (x *Event) ProtoReflect() protoreflect.Message { + mi := &file_protos_events_proto_msgTypes[11] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Event.ProtoReflect.Descriptor instead. +func (*Event) Descriptor() ([]byte, []int) { + return file_protos_events_proto_rawDescGZIP(), []int{11} +} + +func (x *Event) GetTimestamp() int64 { + if x != nil { + return x.Timestamp + } + return 0 +} + +func (x *Event) GetTimestampNano() int64 { + if x != nil { + return x.TimestampNano + } + return 0 +} + +func (m *Event) GetPayload() isEvent_Payload { + if m != nil { + return m.Payload + } + return nil +} + +func (x *Event) GetEnvironmentEvent() *Ev_EnvironmentEvent { + if x, ok := x.GetPayload().(*Event_EnvironmentEvent); ok { + return x.EnvironmentEvent + } + return nil +} + +func (x *Event) GetTaskEvent() *Ev_TaskEvent { + if x, ok := x.GetPayload().(*Event_TaskEvent); ok { + return x.TaskEvent + } + return nil +} + +func (x *Event) GetRoleEvent() *Ev_RoleEvent { + if x, ok := x.GetPayload().(*Event_RoleEvent); ok { + return x.RoleEvent + } + return nil +} + +func (x *Event) GetCallEvent() *Ev_CallEvent { + if x, ok := x.GetPayload().(*Event_CallEvent); ok { + return x.CallEvent + } + return nil +} + +func (x *Event) GetIntegratedServiceEvent() *Ev_IntegratedServiceEvent { + if x, ok := x.GetPayload().(*Event_IntegratedServiceEvent); ok { + return x.IntegratedServiceEvent + } + return nil +} + +func (x *Event) GetRunEvent() *Ev_RunEvent { + if x, ok := x.GetPayload().(*Event_RunEvent); ok { + return x.RunEvent + } + return nil +} + +func (x *Event) GetFrameworkEvent() *Ev_MetaEvent_FrameworkEvent { + if x, ok := x.GetPayload().(*Event_FrameworkEvent); ok { + return x.FrameworkEvent + } + return nil +} + +func (x *Event) GetMesosHeartbeatEvent() *Ev_MetaEvent_MesosHeartbeat { + if x, ok := x.GetPayload().(*Event_MesosHeartbeatEvent); ok { + return x.MesosHeartbeatEvent + } + return nil +} + +func (x *Event) GetCoreStartEvent() *Ev_MetaEvent_CoreStart { + if x, ok := x.GetPayload().(*Event_CoreStartEvent); ok { + return x.CoreStartEvent + } + return nil +} + +func (x *Event) GetBeamModeEvent() *Ev_BeamModeEvent { + if x, ok := x.GetPayload().(*Event_BeamModeEvent); ok { + return x.BeamModeEvent + } + return nil +} + +type isEvent_Payload interface { + isEvent_Payload() +} + +type Event_EnvironmentEvent struct { + // Events produced by AliECS + EnvironmentEvent *Ev_EnvironmentEvent `protobuf:"bytes,11,opt,name=environmentEvent,proto3,oneof"` +} + +type Event_TaskEvent struct { + TaskEvent *Ev_TaskEvent `protobuf:"bytes,12,opt,name=taskEvent,proto3,oneof"` +} + +type Event_RoleEvent struct { + RoleEvent *Ev_RoleEvent `protobuf:"bytes,13,opt,name=roleEvent,proto3,oneof"` +} + +type Event_CallEvent struct { + CallEvent *Ev_CallEvent `protobuf:"bytes,14,opt,name=callEvent,proto3,oneof"` +} + +type Event_IntegratedServiceEvent struct { + IntegratedServiceEvent *Ev_IntegratedServiceEvent `protobuf:"bytes,15,opt,name=integratedServiceEvent,proto3,oneof"` +} + +type Event_RunEvent struct { + RunEvent *Ev_RunEvent `protobuf:"bytes,16,opt,name=runEvent,proto3,oneof"` +} + +type Event_FrameworkEvent struct { + // Meta events produced by AliECS or its components + FrameworkEvent *Ev_MetaEvent_FrameworkEvent `protobuf:"bytes,101,opt,name=frameworkEvent,proto3,oneof"` +} + +type Event_MesosHeartbeatEvent struct { + MesosHeartbeatEvent *Ev_MetaEvent_MesosHeartbeat `protobuf:"bytes,102,opt,name=mesosHeartbeatEvent,proto3,oneof"` +} + +type Event_CoreStartEvent struct { + CoreStartEvent *Ev_MetaEvent_CoreStart `protobuf:"bytes,103,opt,name=coreStartEvent,proto3,oneof"` +} + +type Event_BeamModeEvent struct { + // Events produced by other systems, but natively supported and defined by AliECS + BeamModeEvent *Ev_BeamModeEvent `protobuf:"bytes,200,opt,name=beamModeEvent,proto3,oneof"` +} + +func (*Event_EnvironmentEvent) isEvent_Payload() {} + +func (*Event_TaskEvent) isEvent_Payload() {} + +func (*Event_RoleEvent) isEvent_Payload() {} + +func (*Event_CallEvent) isEvent_Payload() {} + +func (*Event_IntegratedServiceEvent) isEvent_Payload() {} + +func (*Event_RunEvent) isEvent_Payload() {} + +func (*Event_FrameworkEvent) isEvent_Payload() {} + +func (*Event_MesosHeartbeatEvent) isEvent_Payload() {} + +func (*Event_CoreStartEvent) isEvent_Payload() {} + +func (*Event_BeamModeEvent) isEvent_Payload() {} + +var File_protos_events_proto protoreflect.FileDescriptor + +var file_protos_events_proto_rawDesc = []byte{ + 0x0a, 0x13, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x1a, 0x13, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x22, 0x1d, 0x0a, 0x1b, 0x45, 0x76, 0x5f, 0x4d, 0x65, 0x74, 0x61, 0x45, 0x76, 0x65, + 0x6e, 0x74, 0x5f, 0x4d, 0x65, 0x73, 0x6f, 0x73, 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, + 0x74, 0x22, 0x3a, 0x0a, 0x16, 0x45, 0x76, 0x5f, 0x4d, 0x65, 0x74, 0x61, 0x45, 0x76, 0x65, 0x6e, + 0x74, 0x5f, 0x43, 0x6f, 0x72, 0x65, 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x66, + 0x72, 0x61, 0x6d, 0x65, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0b, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x64, 0x22, 0x59, 0x0a, + 0x1b, 0x45, 0x76, 0x5f, 0x4d, 0x65, 0x74, 0x61, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x46, 0x72, + 0x61, 0x6d, 0x65, 0x77, 0x6f, 0x72, 0x6b, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x20, 0x0a, 0x0b, + 0x66, 0x72, 0x61, 0x6d, 0x65, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0b, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x64, 0x12, 0x18, + 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0xa3, 0x04, 0x0a, 0x13, 0x45, 0x76, 0x5f, + 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, + 0x12, 0x24, 0x0a, 0x0d, 0x65, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x49, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x65, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, + 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1c, 0x0a, 0x09, + 0x72, 0x75, 0x6e, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x09, 0x72, 0x75, 0x6e, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, + 0x72, 0x6f, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, + 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x74, 0x72, + 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, + 0x74, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x26, 0x0a, 0x0e, 0x74, 0x72, + 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x65, 0x70, 0x18, 0x07, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, + 0x65, 0x70, 0x12, 0x3c, 0x0a, 0x10, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, + 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x10, 0x2e, 0x65, + 0x76, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x4f, 0x70, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x10, + 0x74, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x12, 0x39, 0x0a, 0x04, 0x76, 0x61, 0x72, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x25, + 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x45, 0x76, 0x5f, 0x45, 0x6e, 0x76, 0x69, 0x72, + 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x56, 0x61, 0x72, 0x73, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x04, 0x76, 0x61, 0x72, 0x73, 0x12, 0x36, 0x0a, 0x0f, 0x6c, + 0x61, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x55, 0x73, 0x65, 0x72, 0x18, 0x0a, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x55, 0x73, + 0x65, 0x72, 0x52, 0x0f, 0x6c, 0x61, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x55, + 0x73, 0x65, 0x72, 0x12, 0x50, 0x0a, 0x14, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x54, + 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x18, 0x0b, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1c, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x66, + 0x6c, 0x6f, 0x77, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, + 0x14, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, + 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x1a, 0x37, 0x0a, 0x09, 0x56, 0x61, 0x72, 0x73, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x6e, + 0x0a, 0x06, 0x54, 0x72, 0x61, 0x69, 0x74, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x74, 0x72, 0x69, 0x67, + 0x67, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x74, 0x72, 0x69, 0x67, 0x67, + 0x65, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x77, 0x61, 0x69, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x05, 0x61, 0x77, 0x61, 0x69, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x65, + 0x6f, 0x75, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, + 0x75, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x63, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x22, 0x84, + 0x02, 0x0a, 0x0c, 0x45, 0x76, 0x5f, 0x54, 0x61, 0x73, 0x6b, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, + 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x74, 0x61, 0x73, 0x6b, 0x69, 0x64, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x61, 0x73, 0x6b, 0x69, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x73, + 0x74, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, + 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x68, 0x6f, 0x73, + 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x68, 0x6f, 0x73, + 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x4e, 0x61, + 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x4e, + 0x61, 0x6d, 0x65, 0x12, 0x26, 0x0a, 0x06, 0x74, 0x72, 0x61, 0x69, 0x74, 0x73, 0x18, 0x07, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x54, 0x72, 0x61, + 0x69, 0x74, 0x73, 0x52, 0x06, 0x74, 0x72, 0x61, 0x69, 0x74, 0x73, 0x12, 0x24, 0x0a, 0x0d, 0x65, + 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x18, 0x08, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0d, 0x65, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x49, + 0x64, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x04, 0x70, 0x61, 0x74, 0x68, 0x22, 0xfc, 0x01, 0x0a, 0x0c, 0x45, 0x76, 0x5f, 0x43, 0x61, 0x6c, + 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x75, 0x6e, 0x63, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, 0x75, 0x6e, 0x63, 0x12, 0x30, 0x0a, 0x0a, 0x63, 0x61, + 0x6c, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x10, + 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x4f, 0x70, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x52, 0x0a, 0x63, 0x61, 0x6c, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x16, 0x0a, 0x06, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, + 0x74, 0x75, 0x72, 0x6e, 0x12, 0x26, 0x0a, 0x06, 0x74, 0x72, 0x61, 0x69, 0x74, 0x73, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x54, 0x72, + 0x61, 0x69, 0x74, 0x73, 0x52, 0x06, 0x74, 0x72, 0x61, 0x69, 0x74, 0x73, 0x12, 0x16, 0x0a, 0x06, + 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6f, 0x75, + 0x74, 0x70, 0x75, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x06, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x24, 0x0a, 0x0d, 0x65, 0x6e, + 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0d, 0x65, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x64, + 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x70, 0x61, 0x74, 0x68, 0x22, 0x92, 0x01, 0x0a, 0x0c, 0x45, 0x76, 0x5f, 0x52, 0x6f, 0x6c, 0x65, + 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x6f, 0x6c, 0x65, 0x50, + 0x61, 0x74, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x6f, 0x6c, 0x65, 0x50, + 0x61, 0x74, 0x68, 0x12, 0x24, 0x0a, 0x0d, 0x65, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, + 0x6e, 0x74, 0x49, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x65, 0x6e, 0x76, 0x69, + 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x22, 0xd1, 0x02, 0x0a, 0x19, 0x45, 0x76, + 0x5f, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x65, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x65, + 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, + 0x72, 0x12, 0x24, 0x0a, 0x0d, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x61, + 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x3a, 0x0a, 0x0f, 0x6f, 0x70, 0x65, 0x72, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, + 0x32, 0x10, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x4f, 0x70, 0x53, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x52, 0x0f, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x12, 0x24, 0x0a, 0x0d, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x53, 0x74, 0x65, 0x70, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6f, 0x70, 0x65, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x65, 0x70, 0x12, 0x42, 0x0a, 0x13, 0x6f, 0x70, 0x65, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x65, 0x70, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x10, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x2e, + 0x4f, 0x70, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x13, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x65, 0x70, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x24, 0x0a, + 0x0d, 0x65, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x18, 0x07, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x65, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, + 0x74, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x08, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x22, 0x99, 0x02, + 0x0a, 0x0b, 0x45, 0x76, 0x5f, 0x52, 0x75, 0x6e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x24, 0x0a, + 0x0d, 0x65, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x65, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, + 0x74, 0x49, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x72, 0x75, 0x6e, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x72, 0x75, 0x6e, 0x4e, 0x75, 0x6d, 0x62, 0x65, + 0x72, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x1e, 0x0a, + 0x0a, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0a, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3c, 0x0a, + 0x10, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x10, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, + 0x2e, 0x4f, 0x70, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x10, 0x74, 0x72, 0x61, 0x6e, 0x73, + 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x36, 0x0a, 0x0f, 0x6c, + 0x61, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x55, 0x73, 0x65, 0x72, 0x18, 0x08, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x55, 0x73, + 0x65, 0x72, 0x52, 0x0f, 0x6c, 0x61, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x55, + 0x73, 0x65, 0x72, 0x4a, 0x04, 0x08, 0x07, 0x10, 0x08, 0x22, 0x5e, 0x0a, 0x10, 0x45, 0x76, 0x5f, + 0x42, 0x65, 0x61, 0x6d, 0x4d, 0x6f, 0x64, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x1c, 0x0a, + 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x2c, 0x0a, 0x08, 0x62, + 0x65, 0x61, 0x6d, 0x49, 0x6e, 0x66, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, + 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x42, 0x65, 0x61, 0x6d, 0x49, 0x6e, 0x66, 0x6f, 0x52, + 0x08, 0x62, 0x65, 0x61, 0x6d, 0x49, 0x6e, 0x66, 0x6f, 0x22, 0x9b, 0x06, 0x0a, 0x05, 0x45, 0x76, + 0x65, 0x6e, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, + 0x70, 0x12, 0x24, 0x0a, 0x0d, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x4e, 0x61, + 0x6e, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x4e, 0x61, 0x6e, 0x6f, 0x12, 0x49, 0x0a, 0x10, 0x65, 0x6e, 0x76, 0x69, 0x72, + 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x18, 0x0b, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1b, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x45, 0x76, 0x5f, 0x45, 0x6e, + 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, + 0x52, 0x10, 0x65, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x45, 0x76, 0x65, + 0x6e, 0x74, 0x12, 0x34, 0x0a, 0x09, 0x74, 0x61, 0x73, 0x6b, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x18, + 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x45, + 0x76, 0x5f, 0x54, 0x61, 0x73, 0x6b, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x09, 0x74, + 0x61, 0x73, 0x6b, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x34, 0x0a, 0x09, 0x72, 0x6f, 0x6c, 0x65, + 0x45, 0x76, 0x65, 0x6e, 0x74, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x65, 0x76, + 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x45, 0x76, 0x5f, 0x52, 0x6f, 0x6c, 0x65, 0x45, 0x76, 0x65, 0x6e, + 0x74, 0x48, 0x00, 0x52, 0x09, 0x72, 0x6f, 0x6c, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x34, + 0x0a, 0x09, 0x63, 0x61, 0x6c, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x18, 0x0e, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x14, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x45, 0x76, 0x5f, 0x43, 0x61, + 0x6c, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x09, 0x63, 0x61, 0x6c, 0x6c, 0x45, + 0x76, 0x65, 0x6e, 0x74, 0x12, 0x5b, 0x0a, 0x16, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, + 0x65, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x18, 0x0f, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x45, 0x76, + 0x5f, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x65, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x16, 0x69, 0x6e, 0x74, 0x65, 0x67, + 0x72, 0x61, 0x74, 0x65, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x45, 0x76, 0x65, 0x6e, + 0x74, 0x12, 0x31, 0x0a, 0x08, 0x72, 0x75, 0x6e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x18, 0x10, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x45, 0x76, 0x5f, + 0x52, 0x75, 0x6e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x08, 0x72, 0x75, 0x6e, 0x45, + 0x76, 0x65, 0x6e, 0x74, 0x12, 0x4d, 0x0a, 0x0e, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x77, 0x6f, 0x72, + 0x6b, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x18, 0x65, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x65, + 0x76, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x45, 0x76, 0x5f, 0x4d, 0x65, 0x74, 0x61, 0x45, 0x76, 0x65, + 0x6e, 0x74, 0x5f, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x77, 0x6f, 0x72, 0x6b, 0x45, 0x76, 0x65, 0x6e, + 0x74, 0x48, 0x00, 0x52, 0x0e, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x77, 0x6f, 0x72, 0x6b, 0x45, 0x76, + 0x65, 0x6e, 0x74, 0x12, 0x57, 0x0a, 0x13, 0x6d, 0x65, 0x73, 0x6f, 0x73, 0x48, 0x65, 0x61, 0x72, + 0x74, 0x62, 0x65, 0x61, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x18, 0x66, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x23, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x45, 0x76, 0x5f, 0x4d, 0x65, 0x74, + 0x61, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x4d, 0x65, 0x73, 0x6f, 0x73, 0x48, 0x65, 0x61, 0x72, + 0x74, 0x62, 0x65, 0x61, 0x74, 0x48, 0x00, 0x52, 0x13, 0x6d, 0x65, 0x73, 0x6f, 0x73, 0x48, 0x65, + 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x48, 0x0a, 0x0e, + 0x63, 0x6f, 0x72, 0x65, 0x53, 0x74, 0x61, 0x72, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x18, 0x67, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x45, 0x76, + 0x5f, 0x4d, 0x65, 0x74, 0x61, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x43, 0x6f, 0x72, 0x65, 0x53, + 0x74, 0x61, 0x72, 0x74, 0x48, 0x00, 0x52, 0x0e, 0x63, 0x6f, 0x72, 0x65, 0x53, 0x74, 0x61, 0x72, + 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x41, 0x0a, 0x0d, 0x62, 0x65, 0x61, 0x6d, 0x4d, 0x6f, + 0x64, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x18, 0xc8, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, + 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x45, 0x76, 0x5f, 0x42, 0x65, 0x61, 0x6d, 0x4d, + 0x6f, 0x64, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x0d, 0x62, 0x65, 0x61, 0x6d, + 0x4d, 0x6f, 0x64, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x42, 0x09, 0x0a, 0x07, 0x50, 0x61, 0x79, + 0x6c, 0x6f, 0x61, 0x64, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x0b, 0x4a, 0x04, 0x08, 0x11, 0x10, 0x65, + 0x4a, 0x05, 0x08, 0x68, 0x10, 0xc8, 0x01, 0x2a, 0x5d, 0x0a, 0x08, 0x4f, 0x70, 0x53, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x12, 0x08, 0x0a, 0x04, 0x4e, 0x55, 0x4c, 0x4c, 0x10, 0x00, 0x12, 0x0b, 0x0a, + 0x07, 0x53, 0x54, 0x41, 0x52, 0x54, 0x45, 0x44, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x4f, 0x4e, + 0x47, 0x4f, 0x49, 0x4e, 0x47, 0x10, 0x02, 0x12, 0x0b, 0x0a, 0x07, 0x44, 0x4f, 0x4e, 0x45, 0x5f, + 0x4f, 0x4b, 0x10, 0x03, 0x12, 0x0e, 0x0a, 0x0a, 0x44, 0x4f, 0x4e, 0x45, 0x5f, 0x45, 0x52, 0x52, + 0x4f, 0x52, 0x10, 0x04, 0x12, 0x10, 0x0a, 0x0c, 0x44, 0x4f, 0x4e, 0x45, 0x5f, 0x54, 0x49, 0x4d, + 0x45, 0x4f, 0x55, 0x54, 0x10, 0x05, 0x42, 0x53, 0x0a, 0x1f, 0x63, 0x68, 0x2e, 0x63, 0x65, 0x72, + 0x6e, 0x2e, 0x61, 0x6c, 0x69, 0x63, 0x65, 0x2e, 0x6f, 0x32, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, + 0x6f, 0x6c, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, + 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x41, 0x6c, 0x69, 0x63, 0x65, 0x4f, 0x32, 0x47, 0x72, 0x6f, + 0x75, 0x70, 0x2f, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, + 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x3b, 0x70, 0x62, 0x50, 0x00, 0x62, 0x06, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_protos_events_proto_rawDescOnce sync.Once + file_protos_events_proto_rawDescData = file_protos_events_proto_rawDesc +) + +func file_protos_events_proto_rawDescGZIP() []byte { + file_protos_events_proto_rawDescOnce.Do(func() { + file_protos_events_proto_rawDescData = protoimpl.X.CompressGZIP(file_protos_events_proto_rawDescData) + }) + return file_protos_events_proto_rawDescData +} + +var file_protos_events_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_protos_events_proto_msgTypes = make([]protoimpl.MessageInfo, 13) +var file_protos_events_proto_goTypes = []interface{}{ + (OpStatus)(0), // 0: events.OpStatus + (*Ev_MetaEvent_MesosHeartbeat)(nil), // 1: events.Ev_MetaEvent_MesosHeartbeat + (*Ev_MetaEvent_CoreStart)(nil), // 2: events.Ev_MetaEvent_CoreStart + (*Ev_MetaEvent_FrameworkEvent)(nil), // 3: events.Ev_MetaEvent_FrameworkEvent + (*Ev_EnvironmentEvent)(nil), // 4: events.Ev_EnvironmentEvent + (*Traits)(nil), // 5: events.Traits + (*Ev_TaskEvent)(nil), // 6: events.Ev_TaskEvent + (*Ev_CallEvent)(nil), // 7: events.Ev_CallEvent + (*Ev_RoleEvent)(nil), // 8: events.Ev_RoleEvent + (*Ev_IntegratedServiceEvent)(nil), // 9: events.Ev_IntegratedServiceEvent + (*Ev_RunEvent)(nil), // 10: events.Ev_RunEvent + (*Ev_BeamModeEvent)(nil), // 11: events.Ev_BeamModeEvent + (*Event)(nil), // 12: events.Event + nil, // 13: events.Ev_EnvironmentEvent.VarsEntry + (*User)(nil), // 14: common.User + (*WorkflowTemplateInfo)(nil), // 15: common.WorkflowTemplateInfo + (*BeamInfo)(nil), // 16: common.BeamInfo +} +var file_protos_events_proto_depIdxs = []int32{ + 0, // 0: events.Ev_EnvironmentEvent.transitionStatus:type_name -> events.OpStatus + 13, // 1: events.Ev_EnvironmentEvent.vars:type_name -> events.Ev_EnvironmentEvent.VarsEntry + 14, // 2: events.Ev_EnvironmentEvent.lastRequestUser:type_name -> common.User + 15, // 3: events.Ev_EnvironmentEvent.workflowTemplateInfo:type_name -> common.WorkflowTemplateInfo + 5, // 4: events.Ev_TaskEvent.traits:type_name -> events.Traits + 0, // 5: events.Ev_CallEvent.callStatus:type_name -> events.OpStatus + 5, // 6: events.Ev_CallEvent.traits:type_name -> events.Traits + 0, // 7: events.Ev_IntegratedServiceEvent.operationStatus:type_name -> events.OpStatus + 0, // 8: events.Ev_IntegratedServiceEvent.operationStepStatus:type_name -> events.OpStatus + 0, // 9: events.Ev_RunEvent.transitionStatus:type_name -> events.OpStatus + 14, // 10: events.Ev_RunEvent.lastRequestUser:type_name -> common.User + 16, // 11: events.Ev_BeamModeEvent.beamInfo:type_name -> common.BeamInfo + 4, // 12: events.Event.environmentEvent:type_name -> events.Ev_EnvironmentEvent + 6, // 13: events.Event.taskEvent:type_name -> events.Ev_TaskEvent + 8, // 14: events.Event.roleEvent:type_name -> events.Ev_RoleEvent + 7, // 15: events.Event.callEvent:type_name -> events.Ev_CallEvent + 9, // 16: events.Event.integratedServiceEvent:type_name -> events.Ev_IntegratedServiceEvent + 10, // 17: events.Event.runEvent:type_name -> events.Ev_RunEvent + 3, // 18: events.Event.frameworkEvent:type_name -> events.Ev_MetaEvent_FrameworkEvent + 1, // 19: events.Event.mesosHeartbeatEvent:type_name -> events.Ev_MetaEvent_MesosHeartbeat + 2, // 20: events.Event.coreStartEvent:type_name -> events.Ev_MetaEvent_CoreStart + 11, // 21: events.Event.beamModeEvent:type_name -> events.Ev_BeamModeEvent + 22, // [22:22] is the sub-list for method output_type + 22, // [22:22] is the sub-list for method input_type + 22, // [22:22] is the sub-list for extension type_name + 22, // [22:22] is the sub-list for extension extendee + 0, // [0:22] is the sub-list for field type_name +} + +func init() { file_protos_events_proto_init() } +func file_protos_events_proto_init() { + if File_protos_events_proto != nil { + return + } + file_protos_common_proto_init() + if !protoimpl.UnsafeEnabled { + file_protos_events_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Ev_MetaEvent_MesosHeartbeat); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_events_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Ev_MetaEvent_CoreStart); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_events_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Ev_MetaEvent_FrameworkEvent); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_events_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Ev_EnvironmentEvent); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_events_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Traits); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_events_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Ev_TaskEvent); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_events_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Ev_CallEvent); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_events_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Ev_RoleEvent); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_events_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Ev_IntegratedServiceEvent); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_events_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Ev_RunEvent); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_events_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Ev_BeamModeEvent); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_events_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Event); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + file_protos_events_proto_msgTypes[11].OneofWrappers = []interface{}{ + (*Event_EnvironmentEvent)(nil), + (*Event_TaskEvent)(nil), + (*Event_RoleEvent)(nil), + (*Event_CallEvent)(nil), + (*Event_IntegratedServiceEvent)(nil), + (*Event_RunEvent)(nil), + (*Event_FrameworkEvent)(nil), + (*Event_MesosHeartbeatEvent)(nil), + (*Event_CoreStartEvent)(nil), + (*Event_BeamModeEvent)(nil), + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_protos_events_proto_rawDesc, + NumEnums: 1, + NumMessages: 13, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_protos_events_proto_goTypes, + DependencyIndexes: file_protos_events_proto_depIdxs, + EnumInfos: file_protos_events_proto_enumTypes, + MessageInfos: file_protos_events_proto_msgTypes, + }.Build() + File_protos_events_proto = out.File + file_protos_events_proto_rawDesc = nil + file_protos_events_proto_goTypes = nil + file_protos_events_proto_depIdxs = nil +} diff --git a/common/protos/events.proto b/common/protos/events.proto new file mode 100644 index 000000000..870524ff1 --- /dev/null +++ b/common/protos/events.proto @@ -0,0 +1,163 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2024 CERN and copyright holders of ALICE O². + * Author: Teo Mrnjavac + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +syntax = "proto3"; + +package events; +option java_package = "ch.cern.alice.o2.control.events"; +option go_package = "github.com/AliceO2Group/Control/common/protos;pb"; + +import public "protos/common.proto"; + +//////////////// Common event messages /////////////// + +enum OpStatus { + NULL = 0; + STARTED = 1; + ONGOING = 2; + DONE_OK = 3; + DONE_ERROR = 4; + DONE_TIMEOUT = 5; +} + +message Ev_MetaEvent_MesosHeartbeat { +} + +message Ev_MetaEvent_CoreStart { + string frameworkId = 1; +} + +message Ev_MetaEvent_FrameworkEvent { + string frameworkId = 1; + string message = 2; +} + +message Ev_EnvironmentEvent { + string environmentId = 1; + string state = 2; + uint32 runNumber = 3; // only when the environment is in the running state + string error = 4; + string message = 5; // any additional message concerning the current state or transition + string transition = 6; + string transitionStep = 7; + OpStatus transitionStatus = 8; + map vars = 9; // consolidated environment variables at the root role of the environment + common.User lastRequestUser = 10; + common.WorkflowTemplateInfo workflowTemplateInfo = 11; +} + +message Traits { + string trigger = 1; + string await = 2; + string timeout = 3; + bool critical = 4; +} + +message Ev_TaskEvent { + string name = 1; // task name, based on the name of the task class + string taskid = 2; // task id, unique + string state = 3; // state machine state for this task + string status = 4; // posible values: ACTIVE/INACTIVE/PARTIAL/UNDEFINED/UNDEPLOYABLE as defined in status.go. + string hostname = 5; + string className = 6; // name of the task class from which this task was spawned + Traits traits = 7; + string environmentId = 8; + string path = 9; // path to the parent taskRole of this task within the environment +} + +message Ev_CallEvent { + string func = 1; // name of the function being called, within the workflow template context + OpStatus callStatus = 2; // progress or success/failure state of the call + string return = 3; // return value of the function + Traits traits = 4; + string output = 5; // any additional output of the function + string error = 6; // error value, if returned + string environmentId = 7; + string path = 8; // path to the parent callRole of this call within the environment +} + +message Ev_RoleEvent { + string name = 1; // role name + string status = 2; // posible values: ACTIVE/INACTIVE/PARTIAL/UNDEFINED/UNDEPLOYABLE as defined in status.go. Derived from the state of child tasks, calls or other roles + string state = 3; // state machine state for this role + string rolePath = 4; // path to this role within the environment + string environmentId = 5; +} + +message Ev_IntegratedServiceEvent { + string name = 1; // name of the context, usually the path of the callRole that calls a given integrated service function e.g. readout-dataflow.dd-scheduler.terminate + string error = 2; // error message, if any + string operationName = 3; // name of the operation, usually the name of the integrated service function being called e.g. ddsched.PartitionTerminate()" + OpStatus operationStatus = 4; // progress or success/failure state of the operation + string operationStep = 5; // if the operation has substeps, this is the name of the current substep, like an API call or polling phase + OpStatus operationStepStatus = 6; // progress or success/failure state of the current substep + string environmentId = 7; + string payload = 8; // any additional payload, depending on the integrated service; there is no schema, it can even be the raw return structure of a remote API call +} + +message Ev_RunEvent { + string environmentId = 1; + uint32 runNumber = 2; + string state = 3; + string error = 4; + string transition = 5; + OpStatus transitionStatus = 6; + reserved 7; // 7 was used for `vars` field that was removed + common.User lastRequestUser = 8; +} + +/** + * Beam mode changes are propagated as Kafka events and to be sent by the BKP-LHC-Client on a dedicated topic + * e.g. dip.lhc.beam_mode + */ +message Ev_BeamModeEvent { + int64 timestamp = 1; // milliseconds since epoch when the beam mode change happened + common.BeamInfo beamInfo = 2; +} + +message Event { + int64 timestamp = 1; + int64 timestampNano = 2; + reserved 3 to 10; + reserved 17 to 100; + reserved 104 to 199; + + oneof Payload { + // Events produced by AliECS + Ev_EnvironmentEvent environmentEvent = 11; + Ev_TaskEvent taskEvent = 12; + Ev_RoleEvent roleEvent = 13; + Ev_CallEvent callEvent = 14; + Ev_IntegratedServiceEvent integratedServiceEvent = 15; + Ev_RunEvent runEvent = 16; + + // Meta events produced by AliECS or its components + Ev_MetaEvent_FrameworkEvent frameworkEvent = 101; + Ev_MetaEvent_MesosHeartbeat mesosHeartbeatEvent = 102; + Ev_MetaEvent_CoreStart coreStartEvent = 103; + + // Events produced by other systems, but natively supported and defined by AliECS + Ev_BeamModeEvent beamModeEvent = 200; + } +} diff --git a/common/protos/protoutils.go b/common/protos/protoutils.go new file mode 100644 index 000000000..5d3a43d82 --- /dev/null +++ b/common/protos/protoutils.go @@ -0,0 +1,50 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2020 CERN and copyright holders of ALICE O². + * Author: Miltiadis Alexis + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package pb + +import ( + "time" +) + +func WrapEvent(ce isEvent_Payload) *Event { + return &Event{ + Timestamp: time.Now().UnixMilli(), + Payload: ce, + } +} + +// SpecialUser returns a User populated from a SpecialUserId enum: +// - externalId = enum numeric value +// - id = enum numeric value +// - name = enum string name +func SpecialUser(enumId SpecialUserId) *User { + externalId := int32(enumId) + name := enumId.String() + return &User{ + ExternalId: &externalId, + Id: nil, + Name: name, + } +} diff --git a/common/runtype/runtype.go b/common/runtype/runtype.go new file mode 100644 index 000000000..419684faa --- /dev/null +++ b/common/runtype/runtype.go @@ -0,0 +1,57 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2021 CERN and copyright holders of ALICE O². + * Author: Teo Mrnjavac + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +//go:generate go run github.com/dmarkham/enumer -type=RunType -yaml -json -text -transform=upper -output=runtype_strings.go + +package runtype + +// NOTE: make sure the enum values include and match those in RunType in dcs.pb.go and apricot.proto +// NOTE: this run type list is replicated in AliceO2 repo in +// https://github.com/AliceO2Group/AliceO2/blob/dev/DataFormats/Parameters/include/DataFormatsParameters/ECSDataAdapters.h +// Inform Ruben when the list is updated. +// NOTE: this run type list is replicated in Bookkeeping upstream, common.proto +// Inform George/Martin when the list is updated. +type RunType int + +const ( + NONE RunType = iota + PHYSICS + TECHNICAL + PEDESTAL + PULSER + LASER + CALIBRATION_ITHR_TUNING + CALIBRATION_VCASN_TUNING + CALIBRATION_THR_SCAN + CALIBRATION_DIGITAL_SCAN + CALIBRATION_ANALOG_SCAN + CALIBRATION_FHR + CALIBRATION_ALPIDE_SCAN + CALIBRATION // no correspondence with DCS + COSMICS // no correspondence with DCS + SYNTHETIC // no correspondence with DCS + NOISE + CALIBRATION_PULSE_LENGTH + CALIBRATION_VRESETD +) diff --git a/common/runtype/runtype_strings.go b/common/runtype/runtype_strings.go new file mode 100644 index 000000000..63acb7274 --- /dev/null +++ b/common/runtype/runtype_strings.go @@ -0,0 +1,193 @@ +// Code generated by "enumer -type=RunType -yaml -json -text -transform=upper -output=runtype_strings.go"; DO NOT EDIT. + +package runtype + +import ( + "encoding/json" + "fmt" + "strings" +) + +const _RunTypeName = "NONEPHYSICSTECHNICALPEDESTALPULSERLASERCALIBRATION_ITHR_TUNINGCALIBRATION_VCASN_TUNINGCALIBRATION_THR_SCANCALIBRATION_DIGITAL_SCANCALIBRATION_ANALOG_SCANCALIBRATION_FHRCALIBRATION_ALPIDE_SCANCALIBRATIONCOSMICSSYNTHETICNOISECALIBRATION_PULSE_LENGTHCALIBRATION_VRESETD" + +var _RunTypeIndex = [...]uint16{0, 4, 11, 20, 28, 34, 39, 62, 86, 106, 130, 153, 168, 191, 202, 209, 218, 223, 247, 266} + +const _RunTypeLowerName = "nonephysicstechnicalpedestalpulserlasercalibration_ithr_tuningcalibration_vcasn_tuningcalibration_thr_scancalibration_digital_scancalibration_analog_scancalibration_fhrcalibration_alpide_scancalibrationcosmicssyntheticnoisecalibration_pulse_lengthcalibration_vresetd" + +func (i RunType) String() string { + if i < 0 || i >= RunType(len(_RunTypeIndex)-1) { + return fmt.Sprintf("RunType(%d)", i) + } + return _RunTypeName[_RunTypeIndex[i]:_RunTypeIndex[i+1]] +} + +// An "invalid array index" compiler error signifies that the constant values have changed. +// Re-run the stringer command to generate them again. +func _RunTypeNoOp() { + var x [1]struct{} + _ = x[NONE-(0)] + _ = x[PHYSICS-(1)] + _ = x[TECHNICAL-(2)] + _ = x[PEDESTAL-(3)] + _ = x[PULSER-(4)] + _ = x[LASER-(5)] + _ = x[CALIBRATION_ITHR_TUNING-(6)] + _ = x[CALIBRATION_VCASN_TUNING-(7)] + _ = x[CALIBRATION_THR_SCAN-(8)] + _ = x[CALIBRATION_DIGITAL_SCAN-(9)] + _ = x[CALIBRATION_ANALOG_SCAN-(10)] + _ = x[CALIBRATION_FHR-(11)] + _ = x[CALIBRATION_ALPIDE_SCAN-(12)] + _ = x[CALIBRATION-(13)] + _ = x[COSMICS-(14)] + _ = x[SYNTHETIC-(15)] + _ = x[NOISE-(16)] + _ = x[CALIBRATION_PULSE_LENGTH-(17)] + _ = x[CALIBRATION_VRESETD-(18)] +} + +var _RunTypeValues = []RunType{NONE, PHYSICS, TECHNICAL, PEDESTAL, PULSER, LASER, CALIBRATION_ITHR_TUNING, CALIBRATION_VCASN_TUNING, CALIBRATION_THR_SCAN, CALIBRATION_DIGITAL_SCAN, CALIBRATION_ANALOG_SCAN, CALIBRATION_FHR, CALIBRATION_ALPIDE_SCAN, CALIBRATION, COSMICS, SYNTHETIC, NOISE, CALIBRATION_PULSE_LENGTH, CALIBRATION_VRESETD} + +var _RunTypeNameToValueMap = map[string]RunType{ + _RunTypeName[0:4]: NONE, + _RunTypeLowerName[0:4]: NONE, + _RunTypeName[4:11]: PHYSICS, + _RunTypeLowerName[4:11]: PHYSICS, + _RunTypeName[11:20]: TECHNICAL, + _RunTypeLowerName[11:20]: TECHNICAL, + _RunTypeName[20:28]: PEDESTAL, + _RunTypeLowerName[20:28]: PEDESTAL, + _RunTypeName[28:34]: PULSER, + _RunTypeLowerName[28:34]: PULSER, + _RunTypeName[34:39]: LASER, + _RunTypeLowerName[34:39]: LASER, + _RunTypeName[39:62]: CALIBRATION_ITHR_TUNING, + _RunTypeLowerName[39:62]: CALIBRATION_ITHR_TUNING, + _RunTypeName[62:86]: CALIBRATION_VCASN_TUNING, + _RunTypeLowerName[62:86]: CALIBRATION_VCASN_TUNING, + _RunTypeName[86:106]: CALIBRATION_THR_SCAN, + _RunTypeLowerName[86:106]: CALIBRATION_THR_SCAN, + _RunTypeName[106:130]: CALIBRATION_DIGITAL_SCAN, + _RunTypeLowerName[106:130]: CALIBRATION_DIGITAL_SCAN, + _RunTypeName[130:153]: CALIBRATION_ANALOG_SCAN, + _RunTypeLowerName[130:153]: CALIBRATION_ANALOG_SCAN, + _RunTypeName[153:168]: CALIBRATION_FHR, + _RunTypeLowerName[153:168]: CALIBRATION_FHR, + _RunTypeName[168:191]: CALIBRATION_ALPIDE_SCAN, + _RunTypeLowerName[168:191]: CALIBRATION_ALPIDE_SCAN, + _RunTypeName[191:202]: CALIBRATION, + _RunTypeLowerName[191:202]: CALIBRATION, + _RunTypeName[202:209]: COSMICS, + _RunTypeLowerName[202:209]: COSMICS, + _RunTypeName[209:218]: SYNTHETIC, + _RunTypeLowerName[209:218]: SYNTHETIC, + _RunTypeName[218:223]: NOISE, + _RunTypeLowerName[218:223]: NOISE, + _RunTypeName[223:247]: CALIBRATION_PULSE_LENGTH, + _RunTypeLowerName[223:247]: CALIBRATION_PULSE_LENGTH, + _RunTypeName[247:266]: CALIBRATION_VRESETD, + _RunTypeLowerName[247:266]: CALIBRATION_VRESETD, +} + +var _RunTypeNames = []string{ + _RunTypeName[0:4], + _RunTypeName[4:11], + _RunTypeName[11:20], + _RunTypeName[20:28], + _RunTypeName[28:34], + _RunTypeName[34:39], + _RunTypeName[39:62], + _RunTypeName[62:86], + _RunTypeName[86:106], + _RunTypeName[106:130], + _RunTypeName[130:153], + _RunTypeName[153:168], + _RunTypeName[168:191], + _RunTypeName[191:202], + _RunTypeName[202:209], + _RunTypeName[209:218], + _RunTypeName[218:223], + _RunTypeName[223:247], + _RunTypeName[247:266], +} + +// RunTypeString retrieves an enum value from the enum constants string name. +// Throws an error if the param is not part of the enum. +func RunTypeString(s string) (RunType, error) { + if val, ok := _RunTypeNameToValueMap[s]; ok { + return val, nil + } + + if val, ok := _RunTypeNameToValueMap[strings.ToLower(s)]; ok { + return val, nil + } + return 0, fmt.Errorf("%s does not belong to RunType values", s) +} + +// RunTypeValues returns all values of the enum +func RunTypeValues() []RunType { + return _RunTypeValues +} + +// RunTypeStrings returns a slice of all String values of the enum +func RunTypeStrings() []string { + strs := make([]string, len(_RunTypeNames)) + copy(strs, _RunTypeNames) + return strs +} + +// IsARunType returns "true" if the value is listed in the enum definition. "false" otherwise +func (i RunType) IsARunType() bool { + for _, v := range _RunTypeValues { + if i == v { + return true + } + } + return false +} + +// MarshalJSON implements the json.Marshaler interface for RunType +func (i RunType) MarshalJSON() ([]byte, error) { + return json.Marshal(i.String()) +} + +// UnmarshalJSON implements the json.Unmarshaler interface for RunType +func (i *RunType) UnmarshalJSON(data []byte) error { + var s string + if err := json.Unmarshal(data, &s); err != nil { + return fmt.Errorf("RunType should be a string, got %s", data) + } + + var err error + *i, err = RunTypeString(s) + return err +} + +// MarshalText implements the encoding.TextMarshaler interface for RunType +func (i RunType) MarshalText() ([]byte, error) { + return []byte(i.String()), nil +} + +// UnmarshalText implements the encoding.TextUnmarshaler interface for RunType +func (i *RunType) UnmarshalText(text []byte) error { + var err error + *i, err = RunTypeString(string(text)) + return err +} + +// MarshalYAML implements a YAML Marshaler for RunType +func (i RunType) MarshalYAML() (interface{}, error) { + return i.String(), nil +} + +// UnmarshalYAML implements a YAML Unmarshaler for RunType +func (i *RunType) UnmarshalYAML(unmarshal func(interface{}) error) error { + var s string + if err := unmarshal(&s); err != nil { + return err + } + + var err error + *i, err = RunTypeString(s) + return err +} diff --git a/common/system/systemid.go b/common/system/systemid.go new file mode 100644 index 000000000..c25c3b927 --- /dev/null +++ b/common/system/systemid.go @@ -0,0 +1,85 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2021 CERN and copyright holders of ALICE O². + * Author: Teo Mrnjavac + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +//go:generate go run github.com/dmarkham/enumer -type=ID -yaml -json -text -transform=upper -output=systemid_strings.go + +package system + +import "sort" + +// Source: https://alice-notes.web.cern.ch/system/files/notes/public/1052/2020-07-15-2020-07-07-O2_Report_Identification_of_sources_for_ALICE_data.pdf +// System ID mapping on page 3 + +type IDMap map[ID]struct{} + +func (m IDMap) StringList() []string { + list := make([]string, len(m)) + i := 0 + for k := range m { + list[i] = k.String() + i++ + } + sort.Strings(list) + return list +} + +type ID int + +const ( + // 1 + // 2 + TPC ID = 3 + TRD ID = 4 + TOF ID = 5 + HMP ID = 6 + PHS ID = 7 + CPV ID = 8 + // 9 + MCH ID = 10 + // 11-14 + ZDC ID = 15 + // 16 + TRG ID = 17 + EMC ID = 18 + TST ID = 19 + // 20-31 + ITS ID = 32 + FDD ID = 33 + FT0 ID = 34 + FV0 ID = 35 + MFT ID = 36 + MID ID = 37 + DCS ID = 38 + FOC ID = 39 + + FIT ID = 254 // non-standard mapping: FT0 + FV0 = FIT + NIL ID = 255 +) + +// Additional non-standard system IDs +const ( + FLP ID = -1 + EPN ID = -2 + PDP ID = -3 +) diff --git a/common/system/systemid_strings.go b/common/system/systemid_strings.go new file mode 100644 index 000000000..7ad087f90 --- /dev/null +++ b/common/system/systemid_strings.go @@ -0,0 +1,253 @@ +// Code generated by "enumer -type=ID -yaml -json -text -transform=upper -output=systemid_strings.go"; DO NOT EDIT. + +package system + +import ( + "encoding/json" + "fmt" + "strings" +) + +const ( + _IDName_0 = "PDPEPNFLP" + _IDLowerName_0 = "pdpepnflp" + _IDName_1 = "TPCTRDTOFHMPPHSCPV" + _IDLowerName_1 = "tpctrdtofhmpphscpv" + _IDName_2 = "MCH" + _IDLowerName_2 = "mch" + _IDName_3 = "ZDC" + _IDLowerName_3 = "zdc" + _IDName_4 = "TRGEMCTST" + _IDLowerName_4 = "trgemctst" + _IDName_5 = "ITSFDDFT0FV0MFTMIDDCSFOC" + _IDLowerName_5 = "itsfddft0fv0mftmiddcsfoc" + _IDName_6 = "FITNIL" + _IDLowerName_6 = "fitnil" +) + +var ( + _IDIndex_0 = [...]uint8{0, 3, 6, 9} + _IDIndex_1 = [...]uint8{0, 3, 6, 9, 12, 15, 18} + _IDIndex_2 = [...]uint8{0, 3} + _IDIndex_3 = [...]uint8{0, 3} + _IDIndex_4 = [...]uint8{0, 3, 6, 9} + _IDIndex_5 = [...]uint8{0, 3, 6, 9, 12, 15, 18, 21, 24} + _IDIndex_6 = [...]uint8{0, 3, 6} +) + +func (i ID) String() string { + switch { + case -3 <= i && i <= -1: + i -= -3 + return _IDName_0[_IDIndex_0[i]:_IDIndex_0[i+1]] + case 3 <= i && i <= 8: + i -= 3 + return _IDName_1[_IDIndex_1[i]:_IDIndex_1[i+1]] + case i == 10: + return _IDName_2 + case i == 15: + return _IDName_3 + case 17 <= i && i <= 19: + i -= 17 + return _IDName_4[_IDIndex_4[i]:_IDIndex_4[i+1]] + case 32 <= i && i <= 39: + i -= 32 + return _IDName_5[_IDIndex_5[i]:_IDIndex_5[i+1]] + case 254 <= i && i <= 255: + i -= 254 + return _IDName_6[_IDIndex_6[i]:_IDIndex_6[i+1]] + default: + return fmt.Sprintf("ID(%d)", i) + } +} + +// An "invalid array index" compiler error signifies that the constant values have changed. +// Re-run the stringer command to generate them again. +func _IDNoOp() { + var x [1]struct{} + _ = x[PDP-(-3)] + _ = x[EPN-(-2)] + _ = x[FLP-(-1)] + _ = x[TPC-(3)] + _ = x[TRD-(4)] + _ = x[TOF-(5)] + _ = x[HMP-(6)] + _ = x[PHS-(7)] + _ = x[CPV-(8)] + _ = x[MCH-(10)] + _ = x[ZDC-(15)] + _ = x[TRG-(17)] + _ = x[EMC-(18)] + _ = x[TST-(19)] + _ = x[ITS-(32)] + _ = x[FDD-(33)] + _ = x[FT0-(34)] + _ = x[FV0-(35)] + _ = x[MFT-(36)] + _ = x[MID-(37)] + _ = x[DCS-(38)] + _ = x[FOC-(39)] + _ = x[FIT-(254)] + _ = x[NIL-(255)] +} + +var _IDValues = []ID{PDP, EPN, FLP, TPC, TRD, TOF, HMP, PHS, CPV, MCH, ZDC, TRG, EMC, TST, ITS, FDD, FT0, FV0, MFT, MID, DCS, FOC, FIT, NIL} + +var _IDNameToValueMap = map[string]ID{ + _IDName_0[0:3]: PDP, + _IDLowerName_0[0:3]: PDP, + _IDName_0[3:6]: EPN, + _IDLowerName_0[3:6]: EPN, + _IDName_0[6:9]: FLP, + _IDLowerName_0[6:9]: FLP, + _IDName_1[0:3]: TPC, + _IDLowerName_1[0:3]: TPC, + _IDName_1[3:6]: TRD, + _IDLowerName_1[3:6]: TRD, + _IDName_1[6:9]: TOF, + _IDLowerName_1[6:9]: TOF, + _IDName_1[9:12]: HMP, + _IDLowerName_1[9:12]: HMP, + _IDName_1[12:15]: PHS, + _IDLowerName_1[12:15]: PHS, + _IDName_1[15:18]: CPV, + _IDLowerName_1[15:18]: CPV, + _IDName_2[0:3]: MCH, + _IDLowerName_2[0:3]: MCH, + _IDName_3[0:3]: ZDC, + _IDLowerName_3[0:3]: ZDC, + _IDName_4[0:3]: TRG, + _IDLowerName_4[0:3]: TRG, + _IDName_4[3:6]: EMC, + _IDLowerName_4[3:6]: EMC, + _IDName_4[6:9]: TST, + _IDLowerName_4[6:9]: TST, + _IDName_5[0:3]: ITS, + _IDLowerName_5[0:3]: ITS, + _IDName_5[3:6]: FDD, + _IDLowerName_5[3:6]: FDD, + _IDName_5[6:9]: FT0, + _IDLowerName_5[6:9]: FT0, + _IDName_5[9:12]: FV0, + _IDLowerName_5[9:12]: FV0, + _IDName_5[12:15]: MFT, + _IDLowerName_5[12:15]: MFT, + _IDName_5[15:18]: MID, + _IDLowerName_5[15:18]: MID, + _IDName_5[18:21]: DCS, + _IDLowerName_5[18:21]: DCS, + _IDName_5[21:24]: FOC, + _IDLowerName_5[21:24]: FOC, + _IDName_6[0:3]: FIT, + _IDLowerName_6[0:3]: FIT, + _IDName_6[3:6]: NIL, + _IDLowerName_6[3:6]: NIL, +} + +var _IDNames = []string{ + _IDName_0[0:3], + _IDName_0[3:6], + _IDName_0[6:9], + _IDName_1[0:3], + _IDName_1[3:6], + _IDName_1[6:9], + _IDName_1[9:12], + _IDName_1[12:15], + _IDName_1[15:18], + _IDName_2[0:3], + _IDName_3[0:3], + _IDName_4[0:3], + _IDName_4[3:6], + _IDName_4[6:9], + _IDName_5[0:3], + _IDName_5[3:6], + _IDName_5[6:9], + _IDName_5[9:12], + _IDName_5[12:15], + _IDName_5[15:18], + _IDName_5[18:21], + _IDName_5[21:24], + _IDName_6[0:3], + _IDName_6[3:6], +} + +// IDString retrieves an enum value from the enum constants string name. +// Throws an error if the param is not part of the enum. +func IDString(s string) (ID, error) { + if val, ok := _IDNameToValueMap[s]; ok { + return val, nil + } + + if val, ok := _IDNameToValueMap[strings.ToLower(s)]; ok { + return val, nil + } + return 0, fmt.Errorf("%s does not belong to ID values", s) +} + +// IDValues returns all values of the enum +func IDValues() []ID { + return _IDValues +} + +// IDStrings returns a slice of all String values of the enum +func IDStrings() []string { + strs := make([]string, len(_IDNames)) + copy(strs, _IDNames) + return strs +} + +// IsAID returns "true" if the value is listed in the enum definition. "false" otherwise +func (i ID) IsAID() bool { + for _, v := range _IDValues { + if i == v { + return true + } + } + return false +} + +// MarshalJSON implements the json.Marshaler interface for ID +func (i ID) MarshalJSON() ([]byte, error) { + return json.Marshal(i.String()) +} + +// UnmarshalJSON implements the json.Unmarshaler interface for ID +func (i *ID) UnmarshalJSON(data []byte) error { + var s string + if err := json.Unmarshal(data, &s); err != nil { + return fmt.Errorf("ID should be a string, got %s", data) + } + + var err error + *i, err = IDString(s) + return err +} + +// MarshalText implements the encoding.TextMarshaler interface for ID +func (i ID) MarshalText() ([]byte, error) { + return []byte(i.String()), nil +} + +// UnmarshalText implements the encoding.TextUnmarshaler interface for ID +func (i *ID) UnmarshalText(text []byte) error { + var err error + *i, err = IDString(string(text)) + return err +} + +// MarshalYAML implements a YAML Marshaler for ID +func (i ID) MarshalYAML() (interface{}, error) { + return i.String(), nil +} + +// UnmarshalYAML implements a YAML Unmarshaler for ID +func (i *ID) UnmarshalYAML(unmarshal func(interface{}) error) error { + var s string + if err := unmarshal(&s); err != nil { + return err + } + + var err error + *i, err = IDString(s) + return err +} diff --git a/common/taskcmdinfo.go b/common/taskcmdinfo.go index d2f2ea0a7..af2e269a1 100644 --- a/common/taskcmdinfo.go +++ b/common/taskcmdinfo.go @@ -24,10 +24,17 @@ package common -import "github.com/AliceO2Group/Control/common/controlmode" +import ( + "time" + + "github.com/AliceO2Group/Control/common/controlmode" +) type TaskCommandInfo struct { CommandInfo ControlPort uint64 `json:"controlPort"` ControlMode controlmode.ControlMode `json:"controlMode"` + + // Only applies to hooks and basic tasks + Timeout time.Duration `json:"timeout,omitempty"` } diff --git a/common/tools/tools.go b/common/tools/tools.go new file mode 100644 index 000000000..c2319f088 --- /dev/null +++ b/common/tools/tools.go @@ -0,0 +1,42 @@ +//go:build tools +// +build tools + +/* + * === This file is part of ALICE O² === + * + * Copyright 2021 CERN and copyright holders of ALICE O². + * Author: Teo Mrnjavac + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package tools + +import ( + _ "github.com/pseudomuto/protoc-gen-doc/cmd/protoc-gen-doc" + _ "google.golang.org/grpc/cmd/protoc-gen-go-grpc" + + // protocol buffer compiler plugins + _ "google.golang.org/protobuf/cmd/protoc-gen-go" + + // enumer + _ "github.com/dmarkham/enumer" + + // swaggo + _ "github.com/swaggo/swag/cmd/swag" +) diff --git a/common/utils/safeacks/safeacks.go b/common/utils/safeacks/safeacks.go new file mode 100644 index 000000000..b3ff7f116 --- /dev/null +++ b/common/utils/safeacks/safeacks.go @@ -0,0 +1,126 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2020 CERN and copyright holders of ALICE O². + * Author: Miltiadis Alexis + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +// Package safeacks provides thread-safe acknowledgment handling for managing +// communication between multiple senders and a single receiver. +package safeacks + +import ( + "fmt" + "sync" +) + +// SafeAcks is a thread safe structure which allows to handle acknowledgment exchanges +// with N senders and one receiver. The first sender succeeds, then an error is returned for the subsequent ones. +// This way, subsequent senders are not stuck sending an acknowledgment when nothing expects it anymore. +// The signaling design is inspired by point 2 in https://go101.org/article/channel-closing.html +// SafeAcks can be used to acknowledge that an action happened to the task such as task KILLED. +// At the moment we utilize SafeAcks to acknowledge that all the requested tasks were killed by mesos (task/manager.go). +type SafeAcks struct { + mu sync.RWMutex + acks map[string]ackChannels +} + +type ackChannels struct { + // the channel to send the ack to + ack chan struct{} + // the channel to close when acks are no longer expected + stop chan struct{} +} + +func (a *SafeAcks) deleteKey(key string) { + a.mu.Lock() + defer a.mu.Unlock() + + delete(a.acks, key) +} + +func (a *SafeAcks) ExpectsAck(key string) bool { + a.mu.RLock() + defer a.mu.RUnlock() + + _, ok := a.acks[key] + + return ok +} + +func (a *SafeAcks) RegisterAck(key string) error { + a.mu.Lock() + defer a.mu.Unlock() + + if _, hasKey := a.acks[key]; hasKey { + return fmt.Errorf("an acknowledgment was already registered for key '%s'", key) + } + + a.acks[key] = ackChannels{make(chan struct{}), make(chan struct{})} + return nil +} + +func (a *SafeAcks) getValue(key string) (ackChannels ackChannels, ok bool) { + a.mu.Lock() + defer a.mu.Unlock() + + ackChannels, ok = a.acks[key] + return +} + +// TrySendAck checks if an acknowledgment is expected and if it is, it blocks until it is received. +// If an acknowledgment is not expected at the moment of the call (or already was received), nil is returned. +// If more than one goroutine attempts to send an acknowledgment before it is received, all but one goroutines will +// receive an error. +func (a *SafeAcks) TrySendAck(key string) error { + channels, ok := a.getValue(key) + if !ok { + // fixme: perhaps we should return an error also here, but returning nil preserves the original behaviour + // of safeAcks before the refactoring. Perhaps the rest of the code assumes it's ok to blindly try sending + // an ack "just in case", so I would not change it lightly. + return nil + } + + select { + case <-channels.stop: + return fmt.Errorf("an acknowledgment has been already received for key '%s'", key) + case channels.ack <- struct{}{}: + return nil + } +} + +// TryReceiveAck blocks until an acknowledgment is received and then returns true. +// It will return false if an acknowledgment for a given key is not expected. +func (a *SafeAcks) TryReceiveAck(key string) bool { + channels, ok := a.getValue(key) + if !ok { + return false + } + <-channels.ack + close(channels.stop) + a.deleteKey(key) + return true +} + +func NewAcks() *SafeAcks { + return &SafeAcks{ + acks: make(map[string]ackChannels), + } +} diff --git a/common/utils/safeacks/safeacks_test.go b/common/utils/safeacks/safeacks_test.go new file mode 100644 index 000000000..6a5103c2d --- /dev/null +++ b/common/utils/safeacks/safeacks_test.go @@ -0,0 +1,118 @@ +package safeacks + +import ( + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "sync" + "testing" + "time" +) + +var _ = Describe("SafeAcks", func() { + var sa *SafeAcks + + BeforeEach(func() { + sa = NewAcks() + }) + + Describe("RegisterAck", func() { + It("should register a new ack", func(ctx SpecContext) { + err := sa.RegisterAck("test") + Expect(err).NotTo(HaveOccurred()) + Expect(sa.ExpectsAck("test")).To(BeTrue()) + }, SpecTimeout(5*time.Second)) + + It("should return error when an ack is already registered", func(ctx SpecContext) { + err := sa.RegisterAck("test") + Expect(err).NotTo(HaveOccurred()) + Expect(sa.ExpectsAck("test")).To(BeTrue()) + + err = sa.RegisterAck("test") + Expect(err).To(HaveOccurred()) + + Expect(sa.ExpectsAck("test")).To(BeTrue()) + }, SpecTimeout(5*time.Second)) + }) + // TODO add timeout for this test + Describe("TrySendAck and TryReceiveAck", func() { + It("should return nil for non-existent key", func(ctx SpecContext) { + err := sa.TrySendAck("nonexistent") + Expect(err).To(BeNil()) + }, SpecTimeout(5*time.Second)) + + It("should send ack successfully", func(ctx SpecContext) { + err := sa.RegisterAck("test") + Expect(err).NotTo(HaveOccurred()) + + var wg sync.WaitGroup + wg.Add(1) + + go func() { + defer wg.Done() + err := sa.TrySendAck("test") + Expect(err).To(BeNil()) + }() + Expect(sa.TryReceiveAck("test")).To(BeTrue()) + + wg.Wait() + }, SpecTimeout(5*time.Second)) + + It("should return error when ack was already sent once", func(ctx SpecContext) { + err := sa.RegisterAck("test") + Expect(err).NotTo(HaveOccurred()) + + result1 := make(chan error) + result2 := make(chan error) + go func() { + result1 <- sa.TrySendAck("test") + }() + + go func() { + result2 <- sa.TrySendAck("test") + }() + + // I really don't like relying on a sleep call to test this, but I see no other way... + // The goal is to have both `TrySendAck` blocked at channel send before invoking TryReceiveAck. + // Hopefully 1 second is enough to avoid having a shaky test. + time.Sleep(1000 * time.Millisecond) + + ok := sa.TryReceiveAck("test") + Expect(ok).To(BeTrue()) + + oneErrorHaveOccured := (<-result1 != nil) != (<-result2 != nil) + Expect(oneErrorHaveOccured).To(BeTrue()) + }, SpecTimeout(5*time.Second)) + }) + + Describe("ExpectsAck", func() { + It("should return false for non-existent key", func(ctx SpecContext) { + Expect(sa.ExpectsAck("nonexistent")).To(BeFalse()) + }, SpecTimeout(5*time.Second)) + + It("should return true for registered key", func(ctx SpecContext) { + err := sa.RegisterAck("test") + Expect(err).NotTo(HaveOccurred()) + Expect(sa.ExpectsAck("test")).To(BeTrue()) + }, SpecTimeout(5*time.Second)) + + It("should not be permanently blocked by another call", func(ctx SpecContext) { + err := sa.RegisterAck("test") + Expect(err).NotTo(HaveOccurred()) + go func() { + sa.TryReceiveAck("test") + }() + + // I really don't like relying on a sleep call to test this, but I see no other way... + // The goal is to have `TryReceiveAck` blocked at channel receive before invoking ExpectsAck. + // Hopefully 1 second is enough to avoid having a shaky test. + time.Sleep(1000 * time.Millisecond) + + Expect(sa.ExpectsAck("test")).To(BeTrue()) + }, SpecTimeout(5*time.Second)) + }) +}) + +func TestSafeAcks(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Component SafeAcks Test Suite") +} diff --git a/common/utils/uid/uid.go b/common/utils/uid/uid.go new file mode 100644 index 000000000..1b417a638 --- /dev/null +++ b/common/utils/uid/uid.go @@ -0,0 +1,116 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2020 CERN and copyright holders of ALICE O². + * Author: Teo Mrnjavac + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +// Package uid provides unique identifier generation functionality. +package uid + +import ( + "encoding/binary" + "encoding/json" + "time" + + "github.com/AliceO2Group/Control/common/logger" + "github.com/denisbrodbeck/machineid" + "github.com/osamingo/indigo" + "github.com/pborman/uuid" + "github.com/rs/xid" + "github.com/sirupsen/logrus" +) + +type ID string + +var ( + log = logger.New(logrus.StandardLogger(), "utils") + uidGen *indigo.Generator +) + +func init() { + // In order to correctly seed ID generation and ensure that all generated IDs + // are reasonably unique to a given machine, we need to provide a uint16 + // that represents the current machine. + // By default, Sonyflake/Indigo attempt to acquire such an ID from the machine's + // private IP address, but this doesn't always work (e.g. on some Docker + // instances). + // So we use denisbrodbeck/machineid to read in the standard machine-id in a + // cross-platform way. On CentOS and similar Linuxes, this means that we read + // the file /etc/machine-id. + // This file contains a standard UUID as string, so we need to parse it into + // a []byte, and fetch 2 of these bytes to generate the uint16 ID. If this + // fails, the uint16 ID defaults to 42. + var machineId uint16 = 42 + + id, err := machineid.ID() + if err == nil { + parsed := uuid.Parse(id) + if parsed != nil { + // The NodeID consists of the last 6 bytes of the full UUID. + // We use the first 2 bytes of this 6-byte block instead of the full + // uuid.Array because the first 10 bits are clock-dependent. + array := parsed.NodeID() + machineId = binary.BigEndian.Uint16(array[0:2]) + } + } else { + id = "" + } + //log.Infof("machine UUID: %s generator machine ID: %d", id, machineId) + + uidGen = indigo.New( + nil, + indigo.StartTime(time.Unix(1257894000, 0)), // Go epoch + indigo.MachineID(func() (uint16, error) { return machineId, nil }), + ) +} + +func (u ID) String() string { + return string(u) +} + +func (u ID) IsNil() bool { + return len(u) == 0 +} + +func FromString(s string) (ID, error) { + _, err := uidGen.Decompose(s) + if err != nil { + return "", err + } + return ID(s), nil +} + +func NilID() ID { + return "" +} + +func New() ID { + id, err := uidGen.NextID() + if err != nil { + //log.Warnf("indigo.NextID() failed with %s, reverting to XID", err) + return ID(xid.New().String()) + } + return ID(id) +} + +func (u ID) MarshalJSON() ([]byte, error) { + return json.Marshal(u.String()) +} diff --git a/common/utils/utils.go b/common/utils/utils.go index f18017910..01b7d7288 100644 --- a/common/utils/utils.go +++ b/common/utils/utils.go @@ -1,11 +1,81 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2018-2019 CERN and copyright holders of ALICE O². + * Author: Teo Mrnjavac + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +// Package utils provides common utility functions for string manipulation, +// data conversion, and various helper operations used across O² Control components. package utils import ( + "encoding/csv" + "encoding/json" + "fmt" "io" "os" + "regexp" + "runtime" "strings" + "time" + "unicode/utf8" + + "github.com/AliceO2Group/Control/common/logger" + "github.com/sirupsen/logrus" + "github.com/spf13/viper" + "gopkg.in/yaml.v3" ) +func TimeTrack(start time.Time, name string, log *logrus.Entry) { + if !viper.GetBool("verbose") { + return + } + + if log == nil { + log = logger.New(logrus.StandardLogger(), "debug").WithPrefix("debug") + } + elapsed := time.Since(start) + log.WithField("level", 11 /*devel*/).Debugf("%s took %s", name, elapsed) +} + +func TimeTrackFunction(start time.Time, log *logrus.Entry) { + // Skip this function, and fetch the PC and file for its parent. + pc, _, _, _ := runtime.Caller(1) + + // Retrieve a function object this functions parent. + funcObj := runtime.FuncForPC(pc) + + // Regex to extract just the function name (and not the module path). + runtimeFunc := regexp.MustCompile(`^.*\.(.*)$`) + name := runtimeFunc.ReplaceAllString(funcObj.Name(), "$1") + log = log.WithField("method", funcObj.Name()) + + TimeTrack(start, name, log) +} + +func NewUnixTimestamp() string { + // User for IL direct hook and scheduler.go + return fmt.Sprintf("%f", float64(time.Now().UnixNano())/1e9) +} + func IsDirEmpty(path string) (bool, error) { dir, err := os.Open(path) if err != nil { @@ -25,4 +95,125 @@ func EnsureTrailingSlash(path *string) { if !strings.HasSuffix(*path, "/") { //Add trailing '/' *path += "/" } -} \ No newline at end of file +} + +// helper func to package strings up nicely for protobuf +func ProtoString(s string) *string { return &s } + +func StringSliceContains(s []string, str string) bool { + for _, a := range s { + if a == str { + return true + } + } + return false +} + +func readAsCSV(val string) ([]string, error) { + if val == "" { + return []string{}, nil + } + stringReader := strings.NewReader(val) + csvReader := csv.NewReader(stringReader) + return csvReader.Read() +} + +func isJson(str string) bool { + var js json.RawMessage + return json.Unmarshal([]byte(str), &js) == nil +} + +func ParseExtraVars(extraVars string) (extraVarsMap map[string]string, err error) { + extraVarsMap = make(map[string]string) + if isJson(extraVars) { + extraVarsMapI := make(map[string]interface{}) + err = yaml.Unmarshal([]byte(extraVars), &extraVarsMapI) + if err != nil { + err = fmt.Errorf("cannot parse extra-vars as JSON: %w", err) + return + } + for k, v := range extraVarsMapI { + if strVal, ok := v.(string); ok { + extraVarsMap[k] = strVal + continue + } + marshaledValue, marshalErr := json.Marshal(v) + if marshalErr != nil { + continue + } + extraVarsMap[k] = string(marshaledValue) + } + } else { + extraVarsSlice := make([]string, 0) + extraVarsSlice, err = readAsCSV(extraVars) + if err != nil { + err = fmt.Errorf("cannot parse extra-vars as CSV: %w", err) + return + } + + for _, entry := range extraVarsSlice { + if len(entry) < 3 { // can't be shorter than a=b + err = fmt.Errorf("invalid variable assignment %s", entry) + return + } + if strings.Count(entry, "=") != 1 { + err = fmt.Errorf("invalid variable assignment %s", entry) + return + } + + sanitized := strings.Trim(strings.TrimSpace(entry), "\"'") + + entryKV := strings.Split(sanitized, "=") + extraVarsMap[entryKV[0]] = entryKV[1] + } + } + return +} + +func TruncateString(str string, length int) string { + if length <= 0 { + return "" + } + + if utf8.RuneCountInString(str) < length { + return str + } + + return string([]rune(str)[:length]) +} + +var taskClassNameRgx = regexp.MustCompile(`(?:.*/)?([^/@]+)@`) // Captures the segment between the last '/' and '@' + +// ExtractTaskClassName extracts the task class name from the provided task name string +// For example, we extract "readout" from the following string: +// "alio2-cr1-hv-gw01.cern.ch:/opt/git/ControlWorkflows/tasks/readout@12b11ac4bb652e1835e3e94806a688c951691d5f#2sP21PjpfCQ" +func ExtractTaskClassName(taskName string) (string, error) { + + matches := taskClassNameRgx.FindStringSubmatch(taskName) + if len(matches) < 2 { + return "", fmt.Errorf("failed to extract task class name from '%s'", taskName) + } + + return matches[1], nil +} + +// JitK8sPrefix is the name prefix of JIT workflows and task classes generated to run on Kubernetes. +const JitK8sPrefix = "jit-k8s-" + +// TrimJitPrefix removes the JIT prefix from task class names. +// For example, "jit-ad6f2b64b7502198430d7d7f93f15bf94c088cab-qc-pp-TPC-CalibQC_long" becomes "qc-pp-TPC-CalibQC_long", +// as does "jit-k8s-ad6f2b64b7502198430d7d7f93f15bf94c088cab-qc-pp-TPC-CalibQC_long". +// If input does not contain a JIT prefix, it is returned as it is. +func TrimJitPrefix(taskClassName string) string { + nParts := 3 + if strings.HasPrefix(taskClassName, JitK8sPrefix) { + nParts = 4 + } + if strings.HasPrefix(taskClassName, "jit-") { + parts := strings.SplitN(taskClassName, "-", nParts) + if len(parts) == nParts { + return parts[nParts-1] + } + } + return taskClassName +} diff --git a/common/utils/utils_test.go b/common/utils/utils_test.go new file mode 100644 index 000000000..1033070e4 --- /dev/null +++ b/common/utils/utils_test.go @@ -0,0 +1,51 @@ +package utils + +import ( + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "testing" +) + +var _ = Describe("Utils", func() { + Describe("ExtractTaskClassNameFromTaskName", func() { + It("should extract the correct task class name", func() { + result, err := ExtractTaskClassName("alio2-cr1-hv-gw01.cern.ch:/opt/git/ControlWorkflows/tasks/readout@12b11ac4bb652e1835e3e94806a688c951691d5f#2sP21PjpfCQ") + Expect(err).ToNot(HaveOccurred()) + Expect(result).To(Equal("readout")) + }) + + It("should extract the correct JIT task class name", func() { + result, err := ExtractTaskClassName("alio2-cr1-hv-gw01.cern.ch:/opt/git/ControlWorkflows/tasks/jit-ad6f2b64b7502198430d7d7f93f15bf94c088cab-qc-pp-TPC-CalibQC_long@12b11ac4bb652e1835e3e94806a688c951691d5f#2sP21PjpfCQ") + Expect(err).ToNot(HaveOccurred()) + Expect(result).To(Equal("jit-ad6f2b64b7502198430d7d7f93f15bf94c088cab-qc-pp-TPC-CalibQC_long")) + }) + + It("should return an error for invalid input", func() { + result, err := ExtractTaskClassName("invalid-string") + Expect(err).To(HaveOccurred()) + Expect(result).To(Equal("")) + }) + }) + + Describe("TrimJitPrefix", func() { + It("should remove the JIT prefix correctly", func() { + result := TrimJitPrefix("jit-ad6f2b64b7502198430d7d7f93f15bf94c088cab-qc-pp-TPC-CalibQC_long") + Expect(result).To(Equal("qc-pp-TPC-CalibQC_long")) + }) + + It("should remove the Kubernetes JIT prefix correctly", func() { + result := TrimJitPrefix("jit-k8s-ad6f2b64b7502198430d7d7f93f15bf94c088cab-qc-pp-TPC-CalibQC_long") + Expect(result).To(Equal("qc-pp-TPC-CalibQC_long")) + }) + + It("should return the original string if no JIT prefix is present", func() { + result := TrimJitPrefix("qc-pp-TPC-CalibQC_long") + Expect(result).To(Equal("qc-pp-TPC-CalibQC_long")) + }) + }) +}) + +func TestUtils(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Component Utils Test Suite") +} diff --git a/configuration/configuration_suite_test.go b/configuration/cfgbackend/configuration_suite_test.go similarity index 61% rename from configuration/configuration_suite_test.go rename to configuration/cfgbackend/configuration_suite_test.go index bc4f5d411..0c7e61fa3 100644 --- a/configuration/configuration_suite_test.go +++ b/configuration/cfgbackend/configuration_suite_test.go @@ -1,4 +1,4 @@ -package configuration_test +package cfgbackend_test import ( "io" @@ -6,11 +6,12 @@ import ( "os" "testing" - . "github.com/onsi/ginkgo" + . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var tmpDir *string + const configFile = "configuration_test.yaml" func TestConfiguration(t *testing.T) { @@ -27,16 +28,27 @@ var _ = BeforeSuite(func() { // copy config file from, err := os.Open("./" + configFile) Expect(err).NotTo(HaveOccurred()) - defer from.Close() - - to, err := os.OpenFile(*tmpDir + "/" + configFile, os.O_RDWR|os.O_CREATE, 0666) + defer func() { + err := from.Close() + if err != nil { + Expect(err).NotTo(HaveOccurred()) + } + }() + + to, err := os.OpenFile(*tmpDir+"/"+configFile, os.O_RDWR|os.O_CREATE, 0666) Expect(err).NotTo(HaveOccurred()) - defer to.Close() + defer func() { + err := to.Close() + if err != nil { + Expect(err).NotTo(HaveOccurred()) + } + }() _, err = io.Copy(to, from) Expect(err).NotTo(HaveOccurred()) }) var _ = AfterSuite(func() { - os.RemoveAll(*tmpDir) + err := os.RemoveAll(*tmpDir) + Expect(err).NotTo(HaveOccurred()) }) diff --git a/configuration/configuration_test.yaml b/configuration/cfgbackend/configuration_test.yaml similarity index 100% rename from configuration/configuration_test.yaml rename to configuration/cfgbackend/configuration_test.yaml diff --git a/configuration/configurationmap.go b/configuration/cfgbackend/configurationmap.go similarity index 97% rename from configuration/configurationmap.go rename to configuration/cfgbackend/configurationmap.go index b2171b8e3..304312a52 100644 --- a/configuration/configurationmap.go +++ b/configuration/cfgbackend/configurationmap.go @@ -22,7 +22,7 @@ * Intergovernmental Organization or submit itself to any jurisdiction. */ -package configuration +package cfgbackend import ( "errors" @@ -32,13 +32,13 @@ import ( ) type ItemType int + const ( IT_Value ItemType = iota IT_Map IT_Array ) - type Item interface { Type() ItemType Value() string @@ -46,9 +46,9 @@ type Item interface { Array() Array DeepCopy() Item } -type Map map[string]Item -type Array []Item -type String string +type Map map[string]Item +type Array []Item +type String string func (m Map) Type() ItemType { return IT_Map diff --git a/configuration/consulsource.go b/configuration/cfgbackend/consulsource.go similarity index 71% rename from configuration/consulsource.go rename to configuration/cfgbackend/consulsource.go index debda789e..5cf8fad03 100644 --- a/configuration/consulsource.go +++ b/configuration/cfgbackend/consulsource.go @@ -22,14 +22,16 @@ * Intergovernmental Organization or submit itself to any jurisdiction. */ -package configuration +package cfgbackend import ( - "github.com/hashicorp/consul/api" "errors" + "fmt" "strconv" "strings" - "gopkg.in/yaml.v2" + + "github.com/hashicorp/consul/api" + "gopkg.in/yaml.v3" ) type ConsulSource struct { @@ -37,7 +39,7 @@ type ConsulSource struct { kv *api.KV } -func newConsulSource(uri string) (cc *ConsulSource, err error) { +func NewConsulSource(uri string) (cc *ConsulSource, err error) { cfg := api.DefaultConfig() cfg.Address = uri cli, err := api.NewClient(cfg) @@ -46,16 +48,22 @@ func newConsulSource(uri string) (cc *ConsulSource, err error) { } cc = &ConsulSource{ uri: uri, - kv: cli.KV(), + kv: cli.KV(), } return } func (cc *ConsulSource) GetNextUInt32(key string) (value uint32, err error) { - kvp, _, err := cc.kv.Get(formatKey(key), &api.QueryOptions{RequireConsistent: true}) + var kvp *api.KVPair + kvp, _, err = cc.kv.Get(formatKey(key), &api.QueryOptions{RequireConsistent: true}) if err != nil { return } + if kvp == nil { + // Key doesn't exist and we should create kvp with value 0. We use cas=0, + // in order Check-And-Set to put the key if it does not already exist. + kvp = &api.KVPair{Key: formatKey(key), Value: []byte("0"), ModifyIndex: uint64(0)} + } var value64 uint64 value64, err = strconv.ParseUint(string(kvp.Value[:]), 10, 32) if err != nil { @@ -76,11 +84,49 @@ func (cc *ConsulSource) GetNextUInt32(key string) (value uint32, err error) { } func (cc *ConsulSource) Get(key string) (value string, err error) { - kvp, _, err := cc.kv.Get(formatKey(key), nil) + var kvp *api.KVPair + kvp, _, err = cc.kv.Get(formatKey(key), nil) if err != nil { return } - value = string(kvp.Value[:]) + if kvp != nil { + value = string(kvp.Value[:]) + } else { + return "", fmt.Errorf("nil response for key %s", key) + } + return +} + +func (cc *ConsulSource) GetWithLastIndex(key string) (value string, lastIndex uint64, err error) { + var kvp *api.KVPair + var qm *api.QueryMeta + kvp, qm, err = cc.kv.Get(formatKey(key), nil) + if err != nil { + return + } + if kvp != nil { + value = string(kvp.Value[:]) + } else { + err = fmt.Errorf("nil value response for key %s", key) + } + if qm != nil { + lastIndex = qm.LastIndex + } else { + err = fmt.Errorf("nil metadata response for key %s", key) + } + return +} + +func (cc *ConsulSource) GetKeysByPrefix(keyPrefix string) (keys []string, err error) { + // An empty keyPrefix is ok by definition. + // If it's non-empty, we must ensure its sanity. + if len(keyPrefix) > 0 { + keyPrefix = formatKey(keyPrefix) + // We must ensure that the path ends with a separator, otherwise we also + // get keys in keyPrefix/.. that start with the name of the key. + keyPrefix = strings.TrimSuffix(keyPrefix, "/") + "/" + } + keys, _, err = cc.kv.Keys(keyPrefix, "", nil) return } @@ -131,6 +177,18 @@ func (cc *ConsulSource) Exists(key string) (exists bool, err error) { return } +func (cc *ConsulSource) IsDir(key string) (isDir bool) { + kvp, _, err := cc.kv.Get(strings.TrimSuffix(formatKey(key), "/")+"/", nil) + if err != nil { + return false + } + isDir = kvp != nil + if isDir { + isDir = strings.HasSuffix(kvp.Key, "/") + } + return +} + func formatKey(key string) (consulKey string) { // Trim leading slashes consulKey = strings.TrimLeft(key, "/") @@ -188,4 +246,4 @@ func mapify(kvps api.KVPairs) Map { m[prefix] = mapify(kvpairslist) } return m -} \ No newline at end of file +} diff --git a/configuration/cfgbackend/mocksource.go b/configuration/cfgbackend/mocksource.go new file mode 100644 index 000000000..1e6ea648e --- /dev/null +++ b/configuration/cfgbackend/mocksource.go @@ -0,0 +1,68 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2018 CERN and copyright holders of ALICE O². + * Author: Teo Mrnjavac + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package cfgbackend + +// MockSource is a minimal mock implementation of the Source interface +type MockSource struct{} + +func NewMockSource() (*MockSource, error) { + return &MockSource{}, nil +} + +func (m *MockSource) Get(key string) (string, error) { + return "", nil +} + +func (m *MockSource) GetKeysByPrefix(prefix string) ([]string, error) { + return []string{}, nil +} + +func (m *MockSource) GetRecursive(key string) (Item, error) { + return make(Map), nil +} + +func (m *MockSource) GetRecursiveYaml(key string) ([]byte, error) { + return []byte{}, nil +} + +func (m *MockSource) Exists(key string) (bool, error) { + return false, nil +} + +func (m *MockSource) IsDir(key string) bool { + return false +} + +func (m *MockSource) Put(key, value string) error { + return nil +} + +func (m *MockSource) PutRecursive(key string, item Item) error { + return nil +} + +func (m *MockSource) PutRecursiveYaml(key string, data []byte) error { + return nil +} diff --git a/configuration/source.go b/configuration/cfgbackend/source.go similarity index 87% rename from configuration/source.go rename to configuration/cfgbackend/source.go index 0251307bb..d53cd4584 100644 --- a/configuration/source.go +++ b/configuration/cfgbackend/source.go @@ -25,18 +25,21 @@ // Package configuration defines the Source interface as the // main access point to O² Configuration backends. // Consul and YAML backends are also provided. -package configuration +package cfgbackend import ( - "strings" "errors" + "strings" ) +// Read-Only Source type ROSource interface { Get(string) (string, error) + GetKeysByPrefix(string) ([]string, error) GetRecursive(string) (Item, error) GetRecursiveYaml(string) ([]byte, error) Exists(string) (bool, error) + IsDir(string) bool } type Source interface { @@ -48,12 +51,15 @@ type Source interface { func NewSource(uri string) (configuration Source, err error) { if strings.HasPrefix(uri, "consul://") { - configuration, err = newConsulSource(strings.TrimPrefix(uri, "consul://")) + configuration, err = NewConsulSource(strings.TrimPrefix(uri, "consul://")) return } else if strings.HasPrefix(uri, "file://") && (strings.HasSuffix(uri, ".yaml") || strings.HasSuffix(uri, ".json")) { configuration, err = newYamlSource(uri) return + } else if strings.HasPrefix(uri, "mock://") { + configuration, err = NewMockSource() + return } err = errors.New("bad URI for configuration source") diff --git a/configuration/source_test.go b/configuration/cfgbackend/source_test.go similarity index 66% rename from configuration/source_test.go rename to configuration/cfgbackend/source_test.go index e0e09f245..cd40ae75f 100644 --- a/configuration/source_test.go +++ b/configuration/cfgbackend/source_test.go @@ -1,88 +1,88 @@ -package configuration_test +package cfgbackend_test import ( - . "github.com/AliceO2Group/Control/configuration" - . "github.com/onsi/ginkgo" + "github.com/AliceO2Group/Control/configuration/cfgbackend" + . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" - "gopkg.in/yaml.v2" + "gopkg.in/yaml.v3" ) var _ = Describe("Source", func() { var ( - c Source + c cfgbackend.Source err error ) DoConfigurationTests := func() { var ( - o2_control_tasks_1_map = Map{ - "name": String("fairmq-ex-1-n-1-sampler"), - "control": Map{ - "mode": String("fairmq"), + o2_control_tasks_1_map = cfgbackend.Map{ + "name": cfgbackend.String("fairmq-ex-1-n-1-sampler"), + "control": cfgbackend.Map{ + "mode": cfgbackend.String("fairmq"), }, - "wants": Map{ - "cpu": String("1"), - "memory": String("256"), - "ports": String("1"), + "wants": cfgbackend.Map{ + "cpu": cfgbackend.String("1"), + "memory": cfgbackend.String("256"), + "ports": cfgbackend.String("1"), }, - "bind": Array{ - Map{ - "name": String("data1"), - "type": String("push"), - "sndBufSize": String("1000"), - "rcvBufSize": String("1000"), - "rateLogging": String("0"), + "bind": cfgbackend.Array{ + cfgbackend.Map{ + "name": cfgbackend.String("data1"), + "type": cfgbackend.String("push"), + "sndBufSize": cfgbackend.String("1000"), + "rcvBufSize": cfgbackend.String("1000"), + "rateLogging": cfgbackend.String("0"), }, }, - "properties": Map{ - "severity": String("trace"), - "color": String("false"), + "properties": cfgbackend.Map{ + "severity": cfgbackend.String("trace"), + "color": cfgbackend.String("false"), }, - "command": Map{ - "env": Array{}, - "shell": String("true"), - "arguments": Array{}, - "value": String("fairmq-ex-1-n-1-sampler"), + "command": cfgbackend.Map{ + "env": cfgbackend.Array{}, + "shell": cfgbackend.String("true"), + "arguments": cfgbackend.Array{}, + "value": cfgbackend.String("fairmq-ex-1-n-1-sampler"), }, } - recursivePutMap = Map{ - "firstKey": String("one"), - "secondKey": Array{ - Map{ - "name": String("first"), - "type": String("an array item"), + recursivePutMap = cfgbackend.Map{ + "firstKey": cfgbackend.String("one"), + "secondKey": cfgbackend.Array{ + cfgbackend.Map{ + "name": cfgbackend.String("first"), + "type": cfgbackend.String("an array item"), }, - Map{ - "name": String("second"), - "type": String("an array item"), + cfgbackend.Map{ + "name": cfgbackend.String("second"), + "type": cfgbackend.String("an array item"), }, - Map{ - "name": String("third"), - "type": String("and yet another array item"), + cfgbackend.Map{ + "name": cfgbackend.String("third"), + "type": cfgbackend.String("and yet another array item"), }, }, - "thirdKey": Map{ - "just some": String("stuff"), + "thirdKey": cfgbackend.Map{ + "just some": cfgbackend.String("stuff"), }, } - recursivePutArray = Array{ - Map{ - "name": String("first"), - "type": String("an array item with a property map inside"), - "properties": Map{ - "just some": String("stuff"), + recursivePutArray = cfgbackend.Array{ + cfgbackend.Map{ + "name": cfgbackend.String("first"), + "type": cfgbackend.String("an array item with a property map inside"), + "properties": cfgbackend.Map{ + "just some": cfgbackend.String("stuff"), }, }, - Map{ - "name": String("second"), - "type": String("an array item"), + cfgbackend.Map{ + "name": cfgbackend.String("second"), + "type": cfgbackend.String("an array item"), }, - Map{ - "name": String("third"), - "type": String("and yet another array item"), + cfgbackend.Map{ + "name": cfgbackend.String("third"), + "type": cfgbackend.String("and yet another array item"), }, } - recursivePutString = String("this is a bit underwhelming compared to the other two...") + recursivePutString = cfgbackend.String("this is a bit underwhelming compared to the other two...") ) It("should return no error when creating an instance", func() { @@ -105,6 +105,22 @@ var _ = Describe("Source", func() { Expect(marshErr).NotTo(HaveOccurred()) Expect(c.GetRecursiveYaml("o2/control/tasks[1]")).To(Equal(marshalled)) }) + + It("should correctly get all keys with the provided prefix", func() { + Expect(c.GetKeysByPrefix("o2/control/globals")).To( + SatisfyAll( + HaveLen(9), + ContainElements( + "o2/control/globals/o2_install_path", + "o2/control/globals/config_basedir", + "o2/control/globals/control/", + "o2/control/globals/control/fairmq/", + "o2/control/globals/control/fairmq/plugin_args", + "o2/control/globals/control/fairmq/control_port_args", + "o2/control/globals/control/direct/", + "o2/control/globals/control/direct/control_port_args", + "o2/control/globals/modulepath"))) + }) }) Context("to check the existence of a key", func() { @@ -128,7 +144,7 @@ var _ = Describe("Source", func() { }) }) - Context("to add a new subtree or value", func(){ + Context("to add a new subtree or value", func() { It("should correctly push a single value", func() { Expect(c.Exists("o2/control/tasks[0]/bind[1]/newSingleValue")).To(BeFalse()) putErr := c.Put("o2/control/tasks[0]/bind[1]/newSingleValue", "foobar") @@ -210,11 +226,11 @@ var _ = Describe("Source", func() { Describe("when interacting with an instance", func() { Context("with Consul backend", func() { BeforeEach(func() { - c, err = NewSource("consul://dummy") + c, err = cfgbackend.NewSource("consul://dummy") }) It("should be of type *ConsulSource", func() { - _, ok := c.(*ConsulSource) + _, ok := c.(*cfgbackend.ConsulSource) Expect(ok).To(Equal(true)) }) @@ -223,11 +239,11 @@ var _ = Describe("Source", func() { Context("with YAML file backend", func() { BeforeEach(func() { - c, err = NewSource("file://" + *tmpDir + "/" + configFile) + c, err = cfgbackend.NewSource("file://" + *tmpDir + "/" + configFile) }) It("should be of type *YamlSource", func() { - _, ok := c.(*YamlSource) + _, ok := c.(*cfgbackend.YamlSource) Expect(ok).To(Equal(true)) }) diff --git a/configuration/yamlsource.go b/configuration/cfgbackend/yamlsource.go similarity index 87% rename from configuration/yamlsource.go rename to configuration/cfgbackend/yamlsource.go index c75a071bd..a4d9b11eb 100644 --- a/configuration/yamlsource.go +++ b/configuration/cfgbackend/yamlsource.go @@ -22,7 +22,7 @@ * Intergovernmental Organization or submit itself to any jurisdiction. */ -package configuration +package cfgbackend import ( "errors" @@ -32,7 +32,7 @@ import ( "strconv" "strings" - "gopkg.in/yaml.v2" + "gopkg.in/yaml.v3" ) type YamlSource struct { @@ -42,7 +42,7 @@ type YamlSource struct { func newYamlSource(uri string) (yc *YamlSource, err error) { yc = &YamlSource{ - uri: uri, + uri: uri, data: nil, } err = yc.refresh() @@ -150,12 +150,59 @@ func (yc *YamlSource) Get(key string) (value string, err error) { return } +func (yc *YamlSource) GetKeysByPrefix(keyPrefix string) (keys []string, err error) { + var recursive Item + recursive, err = yc.GetRecursive(keyPrefix) + if err != nil { + return + } + + keys = []string{} + if recursive.Type() != IT_Map { + err = errors.New("cannot get keys of non-map type") + return + } + + // we collect all the keys recursively and mark nodes with "/" at the end, just as the consul backend does + var collectKeys func(Item, string) + collectKeys = func(item Item, path string) { + if item.Type() == IT_Map { + for k, v := range item.Map() { + newPath := yamlFormatKey(path) + "/" + k + // i'm not sure what to do with arrays, but the consul backend does not support them anyway + if v.Type() == IT_Map { + keys = append(keys, newPath+"/") + } else { + keys = append(keys, newPath) + } + collectKeys(v, newPath) + } + } + } + collectKeys(recursive, keyPrefix) + + return +} + +func (yc *YamlSource) IsDir(key string) (isDir bool) { + recursive, err := yc.GetRecursive(key) + if err != nil { + return false + } + return recursive.Type() == IT_Map +} + func (yc *YamlSource) GetRecursive(key string) (value Item, err error) { err = yc.refresh() if err != nil { return } requestKey := yamlFormatKey(key) + if len(requestKey) == 0 { + // we request the root + value = yc.data + return + } keysPath := strings.Split(requestKey, "/") currentMap := yc.data for i, k := range keysPath { @@ -172,7 +219,7 @@ func (yc *YamlSource) GetRecursive(key string) (value Item, err error) { } } if currentMap[currentKey] == nil { - err = errors.New(fmt.Sprintf("no value for key %s", key)) + err = errors.New(fmt.Sprintf("no value for key '%s'", key)) return } else { it := currentMap[currentKey] @@ -251,7 +298,7 @@ func (yc *YamlSource) Put(key string, value string) (err error) { } } - if i == len(keysPath) - 1 { + if i == len(keysPath)-1 { if arrayIndex > -1 { if currentMap[currentKey] == nil || currentMap[currentKey].Type() != IT_Array { err = errors.New(fmt.Sprintf("found nil at key %s but array was expected", key)) @@ -321,7 +368,7 @@ func (yc *YamlSource) PutRecursive(key string, value Item) (err error) { } } - if i == len(keysPath) - 1 { + if i == len(keysPath)-1 { if arrayIndex > -1 { if currentMap[currentKey] == nil || currentMap[currentKey].Type() != IT_Array { err = errors.New(fmt.Sprintf("found nil at key %s but array was expected", key)) @@ -371,7 +418,7 @@ func (yc *YamlSource) PutRecursive(key string, value Item) (err error) { func (yc *YamlSource) PutRecursiveYaml(key string, value []byte) (err error) { var ( - raw interface{} + raw interface{} cooked Item ) err = yaml.Unmarshal(value, &raw) @@ -414,7 +461,7 @@ func (yc *YamlSource) Exists(key string) (exists bool, err error) { return } else { it := currentMap[currentKey] - if i == len(keysPath) - 1 { // last iteration, so we should have a string for this key + if i == len(keysPath)-1 { // last iteration, so we should have a string for this key if arrayIndex > -1 { exists = it.Array()[arrayIndex] != nil } @@ -441,8 +488,8 @@ func (yc *YamlSource) Exists(key string) (exists bool, err error) { } func yamlFormatKey(key string) (consulKey string) { - // Trim leading slashes - consulKey = strings.TrimLeft(key, "/") + // Trim leading and trailing slashes + consulKey = strings.Trim(key, "/") return } diff --git a/configuration/componentcfg/componentcfg.go b/configuration/componentcfg/componentcfg.go new file mode 100644 index 000000000..082464d21 --- /dev/null +++ b/configuration/componentcfg/componentcfg.go @@ -0,0 +1,89 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2020-2021 CERN and copyright holders of ALICE O². + * Author: Teo Mrnjavac + * Author: George Raduta + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +// Package componentcfg provides component configuration management functionality, +// including query handling and template processing for O² components. +package componentcfg + +import ( + apricotpb "github.com/AliceO2Group/Control/apricot/protos" + "strings" +) + +const ( + ConfigComponentsPath = "o2/components/" +) + +const ( + SEPARATOR = "/" + SEPARATOR_RUNE = '/' +) + +// Checks whether the input string is a valid component name +func IsInputValidComponentName(input string) bool { + return !strings.Contains(input, "/") +} + +// Checks whether the input string is a valid entry name +func IsInputValidEntryName(input string) bool { + return !strings.HasSuffix(input, "resolve") +} + +// keys must be a slice containing all the full keys in /o2/components +func GetComponentsMapFromKeysList(keys []string) map[string]bool { + var componentsMap = make(map[string]bool) + for _, value := range keys { + if !strings.HasPrefix(value, ConfigComponentsPath) { + continue + } + value := strings.TrimPrefix(value, ConfigComponentsPath) + component := strings.Split(value, "/")[0] + if len(component) > 0 { + componentsMap[component] = true + } + } + return componentsMap +} + +func GetEntriesMapOfComponentFromKeysList(component string, runtype apricotpb.RunType, rolename string, keys []string) map[string]bool { + var entriesMap = make(map[string]bool) + runtypeString, ok := apricotpb.RunType_name[int32(runtype)] + if !ok { + runtypeString = "ANY" // this should never happen + } + for _, value := range keys { + value := strings.TrimPrefix(value, ConfigComponentsPath) + parts := strings.Split(value, "/") + if len(parts) < 4 { + continue + } + if parts[0] == component && + parts[1] == runtypeString && + parts[2] == rolename { + entriesMap[parts[3]] = true + } + } + return entriesMap +} diff --git a/configuration/componentcfg/componentcfg_test.go b/configuration/componentcfg/componentcfg_test.go new file mode 100644 index 000000000..28a400aac --- /dev/null +++ b/configuration/componentcfg/componentcfg_test.go @@ -0,0 +1,152 @@ +package componentcfg + +import ( + apricotpb "github.com/AliceO2Group/Control/apricot/protos" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "testing" +) + +var _ = Describe("Component configuration helpers", func() { + Describe("IsInputValidComponentName", func() { + When("a component does not have '/' characters", func() { + It("should be considered as a valid component name", func() { + Expect(IsInputValidComponentName("aZ0+@")).To(BeTrue()) + }) + }) + When("a component has a '/' character", func() { + It("should be considered as an invalid component name", func() { + Expect(IsInputValidComponentName("foo/bar")).To(BeFalse()) + }) + }) + }) + + Describe("IsInputValidEntryName", func() { + When("an entry does not have '/resolve' string at the end", func() { + It("should be considered as a valid component name", func() { + Expect(IsInputValidEntryName("aZ0+@")).To(BeTrue()) + }) + }) + When("an entry has a '/resolve' string at the end", func() { + It("should be considered as an invalid entry name", func() { + Expect(IsInputValidComponentName("foo/resolve")).To(BeFalse()) + }) + }) + When("an entry consists of directories, i.e. it has '/' characters", func() { + It("should be considered as a valid entry name", func() { + Expect(IsInputValidComponentName("foo/bar")).To(BeFalse()) + }) + }) + }) + + Describe("GetComponentsMapFromKeysList", func() { + When("given a list of keys with valid component names", func() { + It("should return a map with the component names as keys", func() { + keys := []string{ + "o2/components/component1/key1", + "o2/components/component1/key2", + "o2/components/component2/key1", + } + expected := map[string]bool{ + "component1": true, + "component2": true, + } + Expect(GetComponentsMapFromKeysList(keys)).To(Equal(expected)) + }) + }) + + When("given a list of keys with empty component names", func() { + It("should return an empty map", func() { + keys := []string{ + "o2/components//key1", + "o2/components/", + } + expected := map[string]bool{} + Expect(GetComponentsMapFromKeysList(keys)).To(Equal(expected)) + }) + }) + + When("given an empty list of keys", func() { + It("should return an empty map", func() { + keys := []string{} + expected := map[string]bool{} + Expect(GetComponentsMapFromKeysList(keys)).To(Equal(expected)) + }) + }) + + When("given a list of keys which are not in o2/components", func() { + It("should return an empty map", func() { + keys := []string{ + "o2/hardware/key", + } + expected := map[string]bool{} + Expect(GetComponentsMapFromKeysList(keys)).To(Equal(expected)) + }) + }) + }) + + Describe("GetEntriesMapOfComponentFromKeysList", func() { + var ( + component = "qc" + runtype = apricotpb.RunType_PHYSICS // Assume 1 is a valid RunType + rolename = "testRole" + keys = []string{ + "o2/components/qc/PHYSICS/testRole/entry1", + "o2/components/qc/PHYSICS/testRole/entry2", + "o2/components/qc/PHYSICS/testRole/entry3", + } + ) + // fixme: it seems that this function does not support the "any" role any "ANY" run type. should this be fixed? + + When("keys list contains valid entries for the given component, runtype, and rolename", func() { + It("should return a map with the entries matching the provided component", func() { + expectedMap := map[string]bool{ + "entry1": true, + "entry2": true, + "entry3": true, + } + result := GetEntriesMapOfComponentFromKeysList(component, runtype, rolename, keys) + Expect(result).To(Equal(expectedMap)) + }) + }) + + When("keys list contains entries for a different component", func() { + It("should not include those entries in the map", func() { + keysWithDifferentComponent := append(keys, "o2/components/readout/PHYSICS/testRole/entry5") + result := GetEntriesMapOfComponentFromKeysList(component, runtype, rolename, keysWithDifferentComponent) + Expect(result).ToNot(HaveKey("entry5")) + }) + }) + + When("keys list contains entries for a different runtype", func() { + It("should not include those entries in the map", func() { + keysWithDifferentRuntype := append(keys, "o2/components/qc/TECHNICAL/testRole/entry6") + result := GetEntriesMapOfComponentFromKeysList(component, runtype, rolename, keysWithDifferentRuntype) + Expect(result).ToNot(HaveKey("entry6")) + }) + }) + + When("keys list contains entries for a different rolename", func() { + It("should not include those entries in the map", func() { + keysWithDifferentRolename := append(keys, "o2/components/qc/PHYSICS/differentRole/entry7") + result := GetEntriesMapOfComponentFromKeysList(component, runtype, rolename, keysWithDifferentRolename) + Expect(result).ToNot(HaveKey("entry7")) + }) + }) + + When("keys list contains an entry with an unexpected format", func() { + It("should gracefully ignore malformed entries", func() { + malformedKeys := append(keys, "o2/components/qc/PHYSICS/testRole") + result := GetEntriesMapOfComponentFromKeysList(component, runtype, rolename, malformedKeys) + Expect(result).ToNot(HaveKey("")) + }) + // fixme: undefined behaviour: + // "o2/components/qc/PHYSICS/testRole//" will return a key "", is this correct? + }) + }) +}) + +func TestComponentCfg(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Component Configuration Test Suite") +} diff --git a/configuration/componentcfg/query.go b/configuration/componentcfg/query.go new file mode 100644 index 000000000..a6d8e2333 --- /dev/null +++ b/configuration/componentcfg/query.go @@ -0,0 +1,208 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2020-2021 CERN and copyright holders of ALICE O². + * Author: Teo Mrnjavac + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package componentcfg + +import ( + "errors" + "net/url" + "regexp" + "strconv" + "strings" + + apricotpb "github.com/AliceO2Group/Control/apricot/protos" +) + +const ( + FALLBACK_RUNTYPE = apricotpb.RunType_ANY + + FALLBACK_ROLENAME = "any" +) + +var ( + // component /RUNTYPE /rolename /entry + inputFullRegex = regexp.MustCompile(`^([a-zA-Z0-9-_]+)(\/[A-Z0-9-_]+){1}(\/[a-z-A-Z0-9-_]+){1}(\/[a-z-A-Z0-9-_/]+){1}$`) + // component /RUNTYPE /rolename + inputEntriesRegex = regexp.MustCompile(`^([a-zA-Z0-9-_]+)(\/[A-Z0-9-_]+){1}(\/[a-z-A-Z0-9-_]+){1}$`) + inputParametersRegex = regexp.MustCompile(`^([a-zA-Z0-9-_]+=[a-zA-Z0-9-_,"\[\]]+)(&[a-zA-Z0-9-_]+=[a-zA-Z0-9-_,"\[\]]+)*$`) + E_BAD_KEY = errors.New("bad component configuration key format") +) + +func IsStringValidQueryPath(input string) bool { + return inputFullRegex.MatchString(input) +} +func IsStringValidEntriesQueryPath(input string) bool { + return inputEntriesRegex.MatchString(input) +} +func IsStringValidQueryParameters(input string) bool { + return inputParametersRegex.MatchString(input) +} + +type EntriesQuery struct { + Component string + RunType apricotpb.RunType + RoleName string +} + +func NewEntriesQuery(path string) (p *EntriesQuery, err error) { + p = &EntriesQuery{ + Component: "", + RunType: apricotpb.RunType_NULL, + RoleName: "", + } + path = strings.TrimSpace(path) + if IsStringValidEntriesQueryPath(path) { + // coconut conf list component/FLAVOR/rolename + params := strings.Split(path, SEPARATOR) + p.Component = params[0] + // Convert FLAVOR to pb-provided enum + typedRunType, ok := apricotpb.RunType_value[params[1]] + if !ok { + err = E_BAD_KEY + return + } + p.RunType = apricotpb.RunType(typedRunType) + p.RoleName = params[2] + } else { + err = E_BAD_KEY + return + } + + return p, nil +} + +type Query struct { + Component string + RunType apricotpb.RunType + RoleName string + EntryKey string +} + +func NewQuery(path string) (p *Query, err error) { + p = &Query{ + Component: "", + RunType: apricotpb.RunType_NULL, + RoleName: "", + EntryKey: "", + } + path = strings.TrimSpace(path) + if IsStringValidQueryPath(path) { + + matches := inputFullRegex.FindAllStringSubmatch(path, -1) + + if len(matches) != 1 { + err = E_BAD_KEY + return + } + captureGroups := matches[0][1:] // the first element is the full query, we don't need it + if len(captureGroups) != 4 { + err = E_BAD_KEY + return + } + p.Component = captureGroups[0] + // Convert FLAVOR to pb-provided enum + typedFlavor, ok := apricotpb.RunType_value[strings.TrimPrefix(captureGroups[1], "/")] + if !ok { + err = E_BAD_KEY + return + } + p.RunType = apricotpb.RunType(typedFlavor) + p.RoleName = strings.TrimPrefix(captureGroups[2], "/") + p.EntryKey = strings.TrimPrefix(captureGroups[3], "/") + } else { + err = E_BAD_KEY + return + } + + return p, nil +} + +func (p *Query) WithFallbackRunType() *Query { + return &Query{ + Component: p.Component, + RunType: FALLBACK_RUNTYPE, + RoleName: p.RoleName, + EntryKey: p.EntryKey, + } +} + +func (p *Query) WithFallbackRoleName() *Query { + return &Query{ + Component: p.Component, + RunType: p.RunType, + RoleName: FALLBACK_ROLENAME, + EntryKey: p.EntryKey, + } +} + +func (p *Query) Path() string { + return p.Component + SEPARATOR + apricotpb.RunType_name[int32(p.RunType)] + SEPARATOR + p.RoleName + SEPARATOR + p.EntryKey +} + +func (p *Query) Raw() string { + return p.Component + SEPARATOR + apricotpb.RunType_name[int32(p.RunType)] + SEPARATOR + p.RoleName + SEPARATOR + p.EntryKey +} + +func (p *Query) AbsoluteRaw() string { + return ConfigComponentsPath + p.Raw() +} + +type QueryParameters struct { + ProcessTemplates bool + VarStack map[string]string +} + +func NewQueryParameters(parameters string) (p *QueryParameters, err error) { + p = &QueryParameters{ + ProcessTemplates: true, + VarStack: make(map[string]string), + } + parameters = strings.TrimSpace(parameters) + + if !IsStringValidQueryParameters(parameters) { + err = E_BAD_KEY + return + } + keyValues, err := url.ParseQuery(parameters) + if err != nil { + return + } + // in our case, we support just one value per key, thus we map the returned keyValues accordingly + for key, values := range keyValues { + if len(values) != 1 { + err = E_BAD_KEY + return + } + if key == "process" { + p.ProcessTemplates, err = strconv.ParseBool(values[0]) + if err != nil { + return + } + } else { + p.VarStack[key] = values[0] + } + } + + return p, nil +} diff --git a/configuration/componentcfg/query_test.go b/configuration/componentcfg/query_test.go new file mode 100644 index 000000000..a83212722 --- /dev/null +++ b/configuration/componentcfg/query_test.go @@ -0,0 +1,252 @@ +package componentcfg + +import ( + apricotpb "github.com/AliceO2Group/Control/apricot/protos" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +var _ = Describe("query", func() { + Describe("Component configuration query", func() { + var ( + q *Query + err error + ) + + When("creating a new query with the full path", func() { + BeforeEach(func() { + q, err = NewQuery("qc/PHYSICS/pp/ctp-raw-qc") + }) + It("should be parsed without reporting errors", func() { + Expect(err).To(BeNil()) + }) + It("should return the path correctly", func() { + Expect(q.Path()).To(Equal("qc/PHYSICS/pp/ctp-raw-qc")) + }) + It("should return the raw query correctly", func() { + Expect(q.Raw()).To(Equal("qc/PHYSICS/pp/ctp-raw-qc")) + }) + It("should return the absolute raw query correctly", func() { + Expect(q.AbsoluteRaw()).To(Equal(ConfigComponentsPath + "qc/PHYSICS/pp/ctp-raw-qc")) + }) + It("should be able to generalize to a query for any run type", func() { + Expect(q.WithFallbackRunType().Path()).To(Equal("qc/ANY/pp/ctp-raw-qc")) + }) + It("should be able to generalize to a query for any role name", func() { + Expect(q.WithFallbackRunType().Path()).To(Equal("qc/ANY/pp/ctp-raw-qc")) + }) + It("should recognize the RunType correctly", func() { + Expect(q.RunType.String()).To(Equal("PHYSICS")) + }) + It("should recognize the RoleName correctly", func() { + Expect(q.RoleName).To(Equal("pp")) + }) + It("should recognize the Component correctly", func() { + Expect(q.Component).To(Equal("qc")) + }) + It("should recognize the EntryKey correctly", func() { + Expect(q.EntryKey).To(Equal("ctp-raw-qc")) + }) + }) + + When("creating a new query with all the supported types of characters", func() { + BeforeEach(func() { + q, err = NewQuery("aAzZ09-_/ANY/aAzZ09-_/aAzZ09-_") + }) + It("should be parsed without reporting errors", func() { + Expect(err).To(BeNil()) + }) + }) + + When("creating a new query with entry having a subdirectory", func() { + BeforeEach(func() { + q, err = NewQuery("qc/ANY/any/tpc/clusters") + }) + It("should be parsed without reporting errors", func() { + Expect(err).To(BeNil()) + }) + It("the entry should be correctly recognized", func() { + Expect(q.EntryKey).To(Equal("tpc/clusters")) + }) + }) + + Describe("dealing with incorrectly formatted queries", func() { + When("query is empty", func() { + BeforeEach(func() { + q, err = NewQuery("") + }) + It("should return an error", func() { + Expect(err).To(MatchError(E_BAD_KEY)) + }) + }) + When("query has not enough / separators", func() { + BeforeEach(func() { + q, err = NewQuery("qc/ANY/any") + }) + It("should return an error", func() { + Expect(err).To(MatchError(E_BAD_KEY)) + }) + }) + When("query has an empty token", func() { + BeforeEach(func() { + q, err = NewQuery("qc/ANY//ctp-raw-qc") + }) + It("should return an error", func() { + Expect(err).To(MatchError(E_BAD_KEY)) + }) + }) + // I don't know whether we are expected to support it or not, but the current behaviour is that we don't, + // even though one can write a key with a space to consul + When("query has a space in the entry key", func() { + BeforeEach(func() { + q, err = NewQuery("qc/ANY/any/ctp qc") + }) + It("should return an error", func() { + Expect(err).To(MatchError(E_BAD_KEY)) + }) + }) + When("run type uses lower case letters", func() { + BeforeEach(func() { + q, err = NewQuery("qc/physics/any/ctp-raw-qc") + }) + It("should return an error", func() { + Expect(err).To(MatchError(E_BAD_KEY)) + }) + }) + When("run type is unknown", func() { + BeforeEach(func() { + q, err = NewQuery("qc/FOO/any/ctp-raw-qc") + }) + It("should return an error", func() { + Expect(err).To(MatchError(E_BAD_KEY)) + }) + }) + }) + }) + + Describe("Component configuration entries query", func() { + var ( + q *EntriesQuery + err error + ) + + When("creating a new valid query", func() { + BeforeEach(func() { + q, err = NewEntriesQuery("qc/PHYSICS/pp") + }) + It("should be parsed without reporting errors", func() { + Expect(err).To(BeNil()) + }) + It("should have parsed the component correctly", func() { + Expect(q.Component).To(Equal("qc")) + }) + It("should have parsed the run type correctly", func() { + Expect(q.RunType).To(Equal(apricotpb.RunType_PHYSICS)) + }) + It("should have parsed the role name correctly", func() { + Expect(q.RoleName).To(Equal("pp")) + }) + }) + + When("creating a new query with all the supported types of characters", func() { + BeforeEach(func() { + q, err = NewEntriesQuery("aAzZ09-_/ANY/aAzZ09-_") + }) + It("should be parsed without reporting errors", func() { + Expect(err).To(BeNil()) + }) + }) + + Describe("dealing with incorrectly formatted queries", func() { + When("query is empty", func() { + BeforeEach(func() { + q, err = NewEntriesQuery("") + }) + It("should return an error", func() { + Expect(err).To(MatchError(E_BAD_KEY)) + }) + }) + When("query has not enough / separators", func() { + BeforeEach(func() { + q, err = NewEntriesQuery("qc/ANY") + }) + It("should return an error", func() { + Expect(err).To(MatchError(E_BAD_KEY)) + }) + }) + When("query has an empty token", func() { + BeforeEach(func() { + q, err = NewEntriesQuery("qc/ANY/") + }) + It("should return an error", func() { + Expect(err).To(MatchError(E_BAD_KEY)) + }) + }) + }) + }) + + Describe("Component configuration query parameters", func() { + var ( + q *QueryParameters + err error + ) + + When("creating new valid query parameters", func() { + BeforeEach(func() { + q, err = NewQueryParameters("process=true&a=aaa&b=123&C_D3=C,C,C&detectors=[\"MCH\",\"MID\"]") + }) + It("should be parsed without reporting errors", func() { + Expect(err).To(BeNil()) + }) + It("should have parsed the process variable correctly", func() { + Expect(q.ProcessTemplates).To(BeTrue()) + }) + It("should have parsed the var stack correctly", func() { + Expect(q.VarStack["a"]).To(Equal("aaa")) + Expect(q.VarStack["b"]).To(Equal("123")) + Expect(q.VarStack["C_D3"]).To(Equal("C,C,C")) + Expect(q.VarStack["detectors"]).To(Equal("[\"MCH\",\"MID\"]")) + }) + }) + When("the query parameters has an escaped first value", func() { + // we test this case because for technical reasons, the regex has one matching group for the first kv + // and another matching group for the rest + BeforeEach(func() { + q, err = NewQueryParameters("detectors=[\"MCH\",\"MID\"]") + }) + It("should be parsed without reporting errors", func() { + Expect(err).To(BeNil()) + }) + It("should have parsed the var stack correctly", func() { + Expect(q.VarStack["detectors"]).To(Equal("[\"MCH\",\"MID\"]")) + }) + }) + + Describe("dealing with incorrectly formatted query parameters", func() { + When("query parameters are empty", func() { + BeforeEach(func() { + q, err = NewQueryParameters("") + }) + It("should return an error", func() { + Expect(err).To(MatchError(E_BAD_KEY)) + }) + }) + When("a key has empty value", func() { + BeforeEach(func() { + q, err = NewQueryParameters("process=") + }) + It("should return an error", func() { + Expect(err).To(MatchError(E_BAD_KEY)) + }) + }) + When("there are two identical keys", func() { + BeforeEach(func() { + q, err = NewQueryParameters("a=33&a=34") + }) + It("should return an error", func() { + Expect(err).To(MatchError(E_BAD_KEY)) + }) + }) + }) + }) +}) diff --git a/configuration/service.go b/configuration/service.go new file mode 100644 index 000000000..534cba725 --- /dev/null +++ b/configuration/service.go @@ -0,0 +1,68 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2021 CERN and copyright holders of ALICE O². + * Author: Teo Mrnjavac + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +// Package configuration provides interfaces and implementations for managing +// runtime configuration of O² Control components and detectors. +package configuration + +import ( + "github.com/AliceO2Group/Control/configuration/componentcfg" +) + +type RuntimeService interface { + GetRuntimeEntry(component string, key string) (string, error) + GetRuntimeEntries(component string) (map[string]string, error) + SetRuntimeEntry(component string, key string, value string) error + ListRuntimeEntries(component string) ([]string, error) +} + +type Service interface { + RuntimeService + NewRunNumber() (runNumber uint32, err error) + GetDefaults() map[string]string + GetVars() map[string]string + InvalidateComponentTemplateCache() + GetComponentConfiguration(query *componentcfg.Query) (payload string, err error) + GetComponentConfigurationWithLastIndex(query *componentcfg.Query) (payload string, lastIndex uint64, err error) + GetAndProcessComponentConfiguration(query *componentcfg.Query, varStack map[string]string) (payload string, err error) + + // getAll == false should skip TRG in the list + ListDetectors(getAll bool) (detectors []string, err error) + GetHostInventory(detector string) (hosts []string, err error) + GetDetectorsInventory() (inventory map[string][]string, err error) + ListComponents() (components []string, err error) + ListComponentEntries(query *componentcfg.EntriesQuery) (entries []string, err error) + ResolveComponentQuery(query *componentcfg.Query) (resolved *componentcfg.Query, err error) + + ImportComponentConfiguration(query *componentcfg.Query, payload string, newComponent bool) (existingComponentUpdated bool, existingEntryUpdated bool, err error) + + GetDetectorForHost(hostname string) (string, error) + GetDetectorsForHosts(hosts []string) ([]string, error) + GetCRUCardsForHost(hostname string) ([]string, error) + GetEndpointsForCRUCard(hostname, cardSerial string) ([]string, error) + GetLinkIDsForCRUEndpoint(host string, cruId string, endpoint string, onlyEnabled bool) (ids []string, err error) + GetAliasedLinkIDsForDetector(detector string, onlyEnabled bool) (aliasedLinkIds []string, err error) + + RawGetRecursive(path string) (string, error) +} diff --git a/configuration/template/dplutil.go b/configuration/template/dplutil.go new file mode 100644 index 000000000..1da68ea9d --- /dev/null +++ b/configuration/template/dplutil.go @@ -0,0 +1,255 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2020-2022 CERN and copyright holders of ALICE O². + * Author: Teo Mrnjavac + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package template + +import ( + "crypto/sha1" + "encoding/hex" + "fmt" + "github.com/AliceO2Group/Control/common/logger/infologger" + "github.com/AliceO2Group/Control/common/utils" + "os" + "os/exec" + "path/filepath" + "regexp" + "strings" + texttemplate "text/template" + + "github.com/AliceO2Group/Control/configuration/componentcfg" + "github.com/AliceO2Group/Control/core/repos" + "github.com/spf13/viper" +) + +// extractConfigURIs looks for configuration URIs in the provided DPL commands and extracts them +// tt supports escaped arguments with quotes and will return trimmed URIs +func extractConfigURIs(dplCommand string) (uris []string) { + const nMaxExpectedQcPayloads = 2 + // Match any consul/apricot URI + // it would be the easiest to use a backreference in the regex, but regexp does not support those: + // (['"]?)((consul-json|apricot)://[^ |\n]*)(\1) + re := regexp.MustCompile(`['"]?(consul-json|apricot)://[^ |\n]+`) + matches := re.FindAllStringSubmatch(dplCommand, nMaxExpectedQcPayloads) + + for _, match := range matches { + uri := match[0] + + if len(uri) < 1 { + // I don't even know how this could happen, but it won't hurt to check... + log.Errorf("seen an empty config URI regex match for DPL command '%s'", dplCommand) + continue + } + + if (uri[0] == '\'' || uri[0] == '"') && uri[0] == uri[len(uri)-1] { + uri = strings.Trim(uri, "\"'") + } + uris = append(uris, uri) + } + + return uris +} + +// jitDplGenerate takes a resolved dplCommand as an argument, +// generates the corresponding tasks and workflow +// and returns the resolved dplWorkflow as a string +func jitDplGenerate(confSvc ConfigurationService, varStack map[string]string, workflowRepo repos.IRepo, dplCommand string, userWfName string) (jitWorkflowName string, err error) { + envId, _ := varStack["environment_id"] + + // When enabled, the JIT workflow gets a separate name (and files), whose tasks are deployed via the K8s path + jitK8sEnabled := varStack["jit_k8s_enabled"] == "true" + if jitK8sEnabled && !viper.GetBool("enableKubernetes") { + return "", fmt.Errorf("jit_k8s_enabled is set, but Kubernetes support is not enabled in core (enableKubernetes)") + } + + var payloads []string + + configURIs := extractConfigURIs(dplCommand) + + // Gather all the processed configuration payloads from apricot + for _, uri := range configURIs { + keyRe := regexp.MustCompile(`components/.*`) + consulKeyMatch := keyRe.FindAllStringSubmatch(uri, 1) + consulKey := strings.SplitAfter(consulKeyMatch[0][0], "components/") + // split between the query and its parameters if there are any + consulKeyTokens := strings.Split(consulKey[1], "?") + + // Query Apricot for the configuration payload + query, err := componentcfg.NewQuery(consulKeyTokens[0]) + if err != nil { + return "", fmt.Errorf("JIT could not create a query out of path '%s'. error: %w", consulKey[1], err) + } + + queryParams := &componentcfg.QueryParameters{ProcessTemplates: false, VarStack: nil} + // Configuration library adds "process=true" if apricot backend is used and "process" is missing, + // thus using apricot backend with no parameters always implies an expectation to process templates. + if strings.HasPrefix(uri, "apricot") { + queryParams.ProcessTemplates = true + } + // parse parameters if they are present + if len(consulKeyTokens) == 2 { + queryParams, err = componentcfg.NewQueryParameters(consulKeyTokens[1]) + if err != nil { + return "", fmt.Errorf("JIT could not parse query parameters of path '%s', error: %w", consulKey[1], err) + } + } + var payload string + if queryParams.ProcessTemplates { + payload, err = confSvc.GetAndProcessComponentConfiguration(query, queryParams.VarStack) + } else { + payload, err = confSvc.GetComponentConfiguration(query) + } + + if err != nil { + return "", fmt.Errorf("JIT failed trying to query QC payload '%s', error: %w", uri, err) + } + payloads = append(payloads, payload) + } + + // Get the O2 & QualityControl version + o2VersionCmd := exec.Command("bash", "-c", "rpm -qa o2-O2 o2-QualityControl") + o2VersionOut, err := o2VersionCmd.Output() + if err != nil { + log.WithField("partition", envId). + WithField("level", infologger.IL_Support). + Warn("JIT couldn't get O2 / QualityControl version: " + err.Error()) + } + + // Get the env vars necessary for JIT + jitEnvVars := varStack["jit_env_vars"] + + // Generate a hash out of + // 1) The full DPL command + // 2) The O2 + QualityControl package versions + // 3) The JIT env vars + // 4) The returned configuration payloads (as separate Write calls to avoid copies of large strings) + hash := sha1.New() + hash.Write([]byte(dplCommand + string(o2VersionOut) + jitEnvVars)) + for _, payload := range payloads { + hash.Write([]byte(payload)) + } + if jitK8sEnabled { + jitWorkflowName = utils.JitK8sPrefix + hex.EncodeToString(hash.Sum(nil)) + } else { + jitWorkflowName = "jit-" + hex.EncodeToString(hash.Sum(nil)) + } + + // We now have a workflow name made out of a hash that should be unique with respect to + // 1) DPL command and + // 2) O2 + QualityControl package versions + // 3) JIT env vars + // 4) Configuration payloads returned by Apricot + // Only generate new tasks & workflows if the files don't exist + // If they exist, hash comparison guarantees validity + if _, err = os.Stat(filepath.Join(workflowRepo.GetCloneDir(), "workflows", jitWorkflowName+".yaml")); err == nil { + log.WithField("partition", envId). + WithField("level", infologger.IL_Support). + WithField("hash", jitWorkflowName). + Infof("Reusing JIT workflow templates for '%s'", userWfName) + return jitWorkflowName, nil + } + log.WithField("partition", envId). + WithField("level", infologger.IL_Support). + WithField("hash", jitWorkflowName). + Infof("Generating JIT workflow templates for '%s'", userWfName) + + // TODO: Before executing we need to check that this is a valid dpl command + // If not, any command may be injected on the aliecs host + // since this will be run as user `aliecs` it might not pose a problem at this point + cmdString := "export " + jitEnvVars + " && " + dplCommand + " --o2-control " + jitWorkflowName + // for some reason the above concatenation may introduce new lines + cmdString = strings.ReplaceAll(cmdString, "\n", " ") + log.Trace("Resolved DPL command: " + cmdString) + dplCmd := exec.Command("bash", "-c", cmdString) + + // execute the DPL command in the repo of the workflow used + dplCmd.Dir = workflowRepo.GetCloneDir() + var dplOut []byte + dplOut, err = dplCmd.CombinedOutput() + if err != nil { + log.WithField("partition", envId). + WithField("level", infologger.IL_Support). + Errorf("failed to run DPL command due to error '%s'", err.Error()) + log.WithField("partition", envId). + WithField("level", infologger.IL_Support). + Errorf("failed DPL command output: %s", string(dplOut)) + return "", fmt.Errorf("failed to run DPL command due to error '%w'. See FLP Infologger for DPL command output", err) + } else { + log.WithField("partition", envId). + WithField("level", infologger.IL_Support). + Trace("DPL command out: " + string(dplOut)) + } + + return jitWorkflowName, nil +} + +func generateDplSubworkflow(confSvc ConfigurationService, varStack map[string]string, workflowRepo repos.IRepo, dplCommand string, userWfName string) (jitWorkflowName string, err error) { + if dplCommand == "none" { + return "", fmt.Errorf("dplCommand is 'none'") + } + + // Resolve any templates as part of the DPL command + fields := Fields{WrapPointer(&dplCommand)} + err = fields.Execute(confSvc, dplCommand, varStack, nil, nil, make(map[string]texttemplate.Template), workflowRepo) + if err != nil { + return "", fmt.Errorf("JIT failed in template resolution of the dpl_command: %w", err) + } + + return jitDplGenerate(confSvc, varStack, workflowRepo, "source /etc/profile.d/o2.sh &&"+dplCommand, userWfName) +} + +func generateDplSubworkflowFromUri(confSvc ConfigurationService, varStack map[string]string, workflowRepo repos.IRepo, dplCommandUri string, fallbackToTemplate bool) (jitWorkflowName string, err error) { + if dplCommandUri == "none" { + return "", fmt.Errorf("dplCommand is 'none'") + } + + dplCommand, err := workflowRepo.GetDplCommand(dplCommandUri) + if err != nil { + if fallbackToTemplate { + // if a file in JIT is missing, it will try to fallback to a standard workflow template in 'workflows/'. + // effectively, this allows us to have an intermediate switch workflow to select different JIT commands + // for different nodes. + envId, _ := varStack["environment_id"] + log.WithField("partition", envId). + WithField("level", infologger.IL_Support). + Debugf("JIT: There is no file 'jit/%s' with a DPL command, falling back the template at 'workflows/%s'", dplCommandUri, dplCommandUri) + return dplCommandUri, nil + } else { + return "", fmt.Errorf("Failed to read DPL command from '%s': %w\n", dplCommandUri, err) + } + } + + // Resolve any templates as part of the DPL command + fields := Fields{WrapPointer(&dplCommand)} + err = fields.Execute(confSvc, dplCommand, varStack, nil, nil, make(map[string]texttemplate.Template), workflowRepo) + if err != nil { + return "", fmt.Errorf("JIT failed in template resolution of the dpl_command: %w", err) + } + + jitWorkflowName, err = jitDplGenerate(confSvc, varStack, workflowRepo, "source /etc/profile.d/o2.sh && "+dplCommand, dplCommandUri) + if err != nil { + detector := varStack["detector"] + return "", fmt.Errorf("for JIT workflow '%s' and detector '%s': %w", dplCommandUri, detector, err) + } + return +} diff --git a/configuration/template/dplutil_test.go b/configuration/template/dplutil_test.go new file mode 100644 index 000000000..47511dc59 --- /dev/null +++ b/configuration/template/dplutil_test.go @@ -0,0 +1,103 @@ +package template + +import ( + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +var _ = Describe("DPL utilities", func() { + Describe("extracting config URIs from DPL commands", func() { + var uris []string + When("the command does not have a config URI", func() { + BeforeEach(func() { + uris = extractConfigURIs("myexe --arg val") + }) + It("should return an empty slice", func() { + Expect(uris).To(HaveLen(0)) + }) + }) + When("URI is not complete", func() { + BeforeEach(func() { + uris = extractConfigURIs("myexe --config apricot://") + }) + It("should return an empty slice", func() { + Expect(uris).To(HaveLen(0)) + }) + }) + When("the URI is the last argument", func() { + BeforeEach(func() { + uris = extractConfigURIs("myexe --config apricot://host.cern.ch:12345/components/qc/ANY/any/ctp-raw") + }) + It("should return correctly parsed URIs", func() { + Expect(uris).To(HaveLen(1)) + Expect(uris[0]).To(Equal("apricot://host.cern.ch:12345/components/qc/ANY/any/ctp-raw")) + }) + }) + When("there is a pipe adjacent to the URI", func() { + BeforeEach(func() { + uris = extractConfigURIs("myexe --config apricot://host.cern.ch:12345/components/qc/ANY/any/ctp-raw| another-exe") + }) + It("should return correctly parsed URIs", func() { + Expect(uris).To(HaveLen(1)) + Expect(uris[0]).To(Equal("apricot://host.cern.ch:12345/components/qc/ANY/any/ctp-raw")) + }) + }) + When("there is a space after the URI", func() { + BeforeEach(func() { + uris = extractConfigURIs("myexe --config apricot://host.cern.ch:12345/components/qc/ANY/any/ctp-raw --run-faster") + }) + It("should return correctly parsed URIs", func() { + Expect(uris).To(HaveLen(1)) + Expect(uris[0]).To(Equal("apricot://host.cern.ch:12345/components/qc/ANY/any/ctp-raw")) + }) + }) + When("the URI had a single quote at the end which is used to write a value for a variable", func() { + BeforeEach(func() { + uris = extractConfigURIs("myexe --config apricot://host.cern.ch:12345/components/qc/ANY/any/ctp-raw?a='b' --run-faster") + }) + It("should return correctly parsed URIs", func() { + Expect(uris).To(HaveLen(1)) + Expect(uris[0]).To(Equal("apricot://host.cern.ch:12345/components/qc/ANY/any/ctp-raw?a='b'")) + }) + }) + When("the URI had a double quote at the end which is used to write a value for a variable", func() { + BeforeEach(func() { + uris = extractConfigURIs("myexe --config apricot://host.cern.ch:12345/components/qc/ANY/any/ctp-raw?a=\"b\" --run-faster") + }) + It("should return correctly parsed URIs", func() { + Expect(uris).To(HaveLen(1)) + Expect(uris[0]).To(Equal("apricot://host.cern.ch:12345/components/qc/ANY/any/ctp-raw?a=\"b\"")) + }) + }) + When("the URI is escaped with single quotes", func() { + BeforeEach(func() { + uris = extractConfigURIs("myexe --config 'apricot://host.cern.ch:12345/components/qc/ANY/any/ctp-raw?a=[\"b\"]' --run-faster") + }) + It("should return correctly parsed URIs", func() { + Expect(uris).To(HaveLen(1)) + Expect(uris[0]).To(Equal("apricot://host.cern.ch:12345/components/qc/ANY/any/ctp-raw?a=[\"b\"]")) + }) + }) + When("the URI is escaped and a pipe is just after", func() { + BeforeEach(func() { + uris = extractConfigURIs("myexe --config 'apricot://host.cern.ch:12345/components/qc/ANY/any/ctp-raw?a=[\"b\"]'| another-exe") + }) + It("should return correctly parsed URIs", func() { + Expect(uris).To(HaveLen(1)) + Expect(uris[0]).To(Equal("apricot://host.cern.ch:12345/components/qc/ANY/any/ctp-raw?a=[\"b\"]")) + }) + }) + When("there are two URIs", func() { + BeforeEach(func() { + uris = extractConfigURIs( + "myexe --config apricot://host.cern.ch:12345/components/qc/ANY/any/ctp-raw" + + " | another-exe --config consul-json://host.cern.ch:12345/components/qc/ANY/any/tcp-raw") + }) + It("should return correctly parsed URIs", func() { + Expect(uris).To(HaveLen(2)) + Expect(uris[0]).To(Equal("apricot://host.cern.ch:12345/components/qc/ANY/any/ctp-raw")) + Expect(uris[1]).To(Equal("consul-json://host.cern.ch:12345/components/qc/ANY/any/tcp-raw")) + }) + }) + }) +}) diff --git a/configuration/template/fields.go b/configuration/template/fields.go new file mode 100644 index 000000000..43a0f8441 --- /dev/null +++ b/configuration/template/fields.go @@ -0,0 +1,365 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2018-2020 CERN and copyright holders of ALICE O². + * Author: Teo Mrnjavac + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +// Package template provides template processing functionality for configuration +// files, including custom field handling and template execution. +package template + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "io" + "strings" + "text/template" + + "github.com/AliceO2Group/Control/common/gera" + "github.com/AliceO2Group/Control/common/logger" + "github.com/AliceO2Group/Control/configuration/componentcfg" + "github.com/AliceO2Group/Control/core/repos" + "github.com/expr-lang/expr" + "github.com/expr-lang/expr/vm" + "github.com/sirupsen/logrus" + "github.com/valyala/fasttemplate" +) + +const DEBUG_TEMPLATE_SYSTEM = false + +var log = logger.New(logrus.StandardLogger(), "template") + +type Sequence map[Stage]Fields +type BuildObjectStackFunc func(stage Stage) map[string]interface{} +type StageCallbackFunc func(stage Stage, err error) error // called after each stage template processing, if err != nil the sequence bails + +type RoleDisabledError struct { + RolePath string +} + +func (e *RoleDisabledError) Error() string { + return fmt.Sprintf("role %s disabled", e.RolePath) +} + +func NullCallback(_ Stage, err error) error { + return err +} + +type ConfigurationService interface { + GetComponentConfiguration(query *componentcfg.Query) (payload string, err error) + GetComponentConfigurationWithLastIndex(query *componentcfg.Query) (payload string, lastIndex uint64, err error) + GetAndProcessComponentConfiguration(query *componentcfg.Query, varStack map[string]string) (payload string, err error) + ResolveComponentQuery(query *componentcfg.Query) (resolved *componentcfg.Query, err error) + + GetDetectorForHost(hostname string) (string, error) + GetDetectorsForHosts(hosts []string) ([]string, error) + GetCRUCardsForHost(hostname string) ([]string, error) + GetEndpointsForCRUCard(hostname, cardSerial string) ([]string, error) + + GetRuntimeEntry(component string, key string) (string, error) + SetRuntimeEntry(component string, key string, value string) error +} + +func (sf Sequence) Execute(confSvc ConfigurationService, + parentPath string, + varStack VarStack, + buildObjectStack BuildObjectStackFunc, + baseConfigStack map[string]string, + stringTemplateCache map[string]template.Template, + workflowRepo repos.IRepo, + stageCallback StageCallbackFunc) (err error) { + for i := 0; i < int(_STAGE_MAX); i++ { + currentStage := Stage(i) + + var stagedStack map[string]string + stagedStack, err = varStack.consolidated(currentStage) + if err != nil { + return + } + + objectStack := buildObjectStack(currentStage) + + if fields, ok := sf[currentStage]; ok { + if DEBUG_TEMPLATE_SYSTEM { + log.WithFields(logrus.Fields{ + "path": parentPath, + "stage": currentStage, + "fields": func() string { + accumulator := make([]string, len(fields)) + for i, v := range fields { + accumulator[i] = v.Get() + } + return strings.Join(accumulator, ", ") + }(), + }).Trace("about to process fields for stage") + } + err = fields.Execute(confSvc, parentPath, stagedStack, objectStack, baseConfigStack, stringTemplateCache, workflowRepo) + err = stageCallback(currentStage, err) + if err != nil { + var roleDisabledErrorType *RoleDisabledError + if isRoleDisabled := errors.As(err, &roleDisabledErrorType); isRoleDisabled { + return + } + + log.WithError(err). + Errorf("template processing error") + return + } + } + } + return +} + +type Fields []Field + +type Stage int + +const ( + // RESOLUTION STAGE ↓ VALUES AVAILABLE ↓ + STAGE0 Stage = iota // parent stack only (for enabled) + STAGE1 // parent stack only + locals + STAGE2 // parent stack + defaults + locals + STAGE3 // parent stack + defaults + vars + locals + STAGE4 // parent stack + defaults + vars + uservars + locals + STAGE5 // parent stack + defaults + vars + uservars + locals + full self-object = full stack + _STAGE_MAX +) + +type VarStack struct { + Locals map[string]string + Defaults *gera.WrapMap[string, string] + Vars *gera.WrapMap[string, string] + UserVars *gera.WrapMap[string, string] +} + +func (vs *VarStack) consolidated(stage Stage) (consolidatedStack map[string]string, err error) { + var defaults, vars, userVars map[string]string + vars, err = vs.Vars.Flattened() + if err != nil { + return + } + userVars, err = vs.UserVars.Flattened() + if err != nil { + return + } + + switch stage { + case STAGE0: + fallthrough + case STAGE1: + defaults, err = vs.Defaults.FlattenedParent() + if err != nil { + return + } + vars, err = vs.Vars.FlattenedParent() + if err != nil { + return + } + userVars, err = vs.UserVars.FlattenedParent() + if err != nil { + return + } + case STAGE2: + defaults, err = vs.Defaults.Flattened() + if err != nil { + return + } + vars, err = vs.Vars.FlattenedParent() + if err != nil { + return + } + userVars, err = vs.UserVars.FlattenedParent() + if err != nil { + return + } + case STAGE3: + defaults, err = vs.Defaults.Flattened() + if err != nil { + return + } + vars, err = vs.Vars.Flattened() + if err != nil { + return + } + userVars, err = vs.UserVars.FlattenedParent() + if err != nil { + return + } + case STAGE4: + fallthrough + case STAGE5: + defaults, err = vs.Defaults.Flattened() + if err != nil { + return + } + vars, err = vs.Vars.Flattened() + if err != nil { + return + } + userVars, err = vs.UserVars.Flattened() + if err != nil { + return + } + case _STAGE_MAX: + } + + consolidated := gera.MakeMapWithMap(vs.Locals).Wrap(gera.MakeMapWithMap(userVars).Wrap(gera.MakeMapWithMap(vars).Wrap(gera.MakeMapWithMap(defaults)))) + consolidatedStack, err = consolidated.Flattened() + if err != nil { + return + } + return +} + +func (fields Fields) Execute(confSvc ConfigurationService, parentPath string, varStack map[string]string, objStack map[string]interface{}, baseConfigStack map[string]string, stringTemplateCache map[string]template.Template, workflowRepo repos.IRepo) (err error) { + environment := make(map[string]interface{}, len(varStack)) + strOpStack := MakeUtilFuncMap(varStack) + for k, v := range varStack { + environment[k] = v + } + copyMap(objStack, environment) // needed for deep copy e.g. odc.Configure + + for k, v := range strOpStack { + environment[k] = v + } + + if workflowRepo != nil { + repoAccessFuncs := MakeConfigAndRepoAccessFuncs(confSvc, varStack, workflowRepo) + for k, v := range repoAccessFuncs { + environment[k] = v + } + } + + configAccessFuncs := MakeConfigAccessFuncs(confSvc, varStack) + for k, v := range configAccessFuncs { + environment[k] = v + } + + multiVarconfigAccessFuncs := MakeConfigAccessFuncsMultiVar(confSvc, varStack) + for k, v := range multiVarconfigAccessFuncs { + environment[k] = v + } + + pluginObjects := MakePluginObjectStack(varStack, baseConfigStack) + copyMap(pluginObjects, environment) + + configAccessObj := MakeConfigAccessObject(confSvc, varStack) + copyMap(configAccessObj, environment) + + // We override the "+" operator so that in situations like + // "string" + + // + "string" + // + + // we always treat as string and return a string. + environment["addNil"] = func(a, b *string) string { + first, second := "", "" + if a != nil { + first = *a + } + if b != nil { + second = *b + } + return first + second + } + + for _, field := range fields { + buf := new(bytes.Buffer) + // FIXME: the line below implements the cache + //tmpl, ok := stringTemplateCache[*str] + var tmpl *fasttemplate.Template // dummy + ok := false // dummy + if !ok { + tmpl, err = fasttemplate.NewTemplate(field.Get(), "{{", "}}") + if err != nil { + log.WithError(err).WithField("role", parentPath).Warn("template processing error (bad workflow file)") + return + } + } + + _, err = tmpl.ExecuteFunc(buf, func(w io.Writer, tag string) (i int, err error) { + var ( + program *vm.Program + rawOutput interface{} + ) + program, err = expr.Compile(tag, []expr.Option{ + expr.Env(environment), + expr.Operator("+", "addNil"), + }...) + if err != nil { + return + } + + rawOutput, err = expr.Run(program, environment) + if err != nil { + return + } + + switch rawOutput.(type) { + case []interface{}, []string: + var jsonOutput []byte + jsonOutput, err = json.Marshal(rawOutput) + if err != nil { + return + } + return w.Write(jsonOutput) + default: + return w.Write([]byte(fmt.Sprintf("%v", rawOutput))) + } + }) + if DEBUG_TEMPLATE_SYSTEM { + log.WithFields(logrus.Fields{ + "path": parentPath, + "fieldBefore": field.Get(), + "fieldAfter": buf.String(), + "error": err, + }).Trace("processed field for stage") + } + if err != nil { + log.WithError(err).WithField("role", parentPath).Warn("template processing error (bad variable or workflow file)") + return + } + field.Set(buf.String()) + } + return +} + +func copyMap(src map[string]interface{}, dest map[string]interface{}) { + for k, v := range src { + vmap, ok := v.(map[string]interface{}) + if ok { + var destk map[string]interface{} + if _, exists := dest[k]; exists { + destk, ok = dest[k].(map[string]interface{}) + if !ok { + destk = make(map[string]interface{}) + } + } else { + destk = make(map[string]interface{}) + } + copyMap(vmap, destk) + dest[k] = destk + } else { + dest[k] = v + } + } +} diff --git a/configuration/template/fieldwrappers.go b/configuration/template/fieldwrappers.go new file mode 100644 index 000000000..3b79c9574 --- /dev/null +++ b/configuration/template/fieldwrappers.go @@ -0,0 +1,119 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2018-2020 CERN and copyright holders of ALICE O². + * Author: Teo Mrnjavac + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package template + +type Field interface { + Get() string + Set(value string) +} + +type PointerWrapper struct { + field *string +} + +func WrapPointer(field *string) Field { + return &PointerWrapper{field: field} +} + +func (t *PointerWrapper) Get() string { + return *t.field +} + +func (t *PointerWrapper) Set(value string) { + *t.field = value +} + +type GetterFunc func() string +type SetterFunc func(value string) +type GenericWrapper struct { + Getter GetterFunc + Setter SetterFunc +} + +func (t *GenericWrapper) Get() string { + return t.Getter() +} + +func (t *GenericWrapper) Set(value string) { + t.Setter(value) +} + +func WrapGeneric(getterF GetterFunc, setterF SetterFunc) Field { + return &GenericWrapper{ + Getter: getterF, + Setter: setterF, + } +} + +// WrapMapItems creates a slice of Fields from a map of string key-value pairs. +// It wraps each map item in a GenericWrapper, allowing for dynamic access and +// modification of the original map's values through the Fields interface. +// +// Parameters: +// - items: A map[string]string to be wrapped +// +// Returns: +// - Fields: A slice of Field interfaces, each corresponding to a map item +func WrapMapItems(items map[string]string) Fields { + fields := make(Fields, 0) + for k := range items { + key := k // we need a local copy for the Getter/Setter closures + fields = append(fields, &GenericWrapper{ + Getter: func() string { + return items[key] + }, + Setter: func(value string) { + items[key] = value + }, + }) + } + return fields +} + +// WrapSliceItems creates a slice of Fields from a slice of strings. +// It wraps each string item in a GenericWrapper, allowing for dynamic +// access and modification of the original slice's elements through +// the Fields interface. +// +// Parameters: +// - items: A []string to be wrapped +// +// Returns: +// - Fields: A slice of Field interfaces, each corresponding to a slice item +func WrapSliceItems(items []string) Fields { + fields := make(Fields, 0) + for i := range items { + index := i // we need a local copy for the Getter/Setter closures + fields = append(fields, &GenericWrapper{ + Getter: func() string { + return items[index] + }, + Setter: func(value string) { + items[index] = value + }, + }) + } + return fields +} diff --git a/configuration/template/fieldwrappers_test.go b/configuration/template/fieldwrappers_test.go new file mode 100644 index 000000000..ed20b7d0d --- /dev/null +++ b/configuration/template/fieldwrappers_test.go @@ -0,0 +1,78 @@ +package template_test + +import ( + "github.com/AliceO2Group/Control/configuration/template" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +var _ = Describe("Field Wrappers", func() { + Describe("PointerWrapper", func() { + It("should get and set values correctly", func() { + value := "test" + pw := template.WrapPointer(&value) + + Expect(pw.Get()).To(Equal("test")) + + pw.Set("new value") + Expect(value).To(Equal("new value")) + }) + }) + + Describe("GenericWrapper", func() { + It("should get and set values correctly", func() { + value := "test" + gw := template.WrapGeneric( + func() string { return value }, + func(v string) { value = v }, + ) + + Expect(gw.Get()).To(Equal("test")) + + gw.Set("new value") + Expect(value).To(Equal("new value")) + }) + }) + + Describe("WrapMapItems", func() { + It("should wrap map items correctly", func() { + items := map[string]string{ + "key1": "value1", + "key2": "value2", + } + fields := template.WrapMapItems(items) + + Expect(fields).To(HaveLen(2)) + + for _, field := range fields { + initialValue := field.Get() + field.Set("new " + initialValue) + } + + expectedItems := map[string]string{ + "key1": "new value1", + "key2": "new value2", + } + + Expect(items).To(Equal(expectedItems)) + }) + }) + + Describe("WrapSliceItems", func() { + It("should wrap slice items correctly", func() { + items := []string{"item1", "item2", "item3"} + fields := template.WrapSliceItems(items) + + Expect(fields).To(HaveLen(3)) + + for i, field := range fields { + Expect(field.Get()).To(Equal(items[i])) + field.Set("new " + items[i]) + } + + expectedItems := []string{"new item1", "new item2", "new item3"} + + Expect(items).To(Equal(expectedItems)) + }) + }) +}) diff --git a/configuration/template/loader.go b/configuration/template/loader.go new file mode 100644 index 000000000..e6c4ea75a --- /dev/null +++ b/configuration/template/loader.go @@ -0,0 +1,75 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2020 CERN and copyright holders of ALICE O². + * Author: Teo Mrnjavac + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package template + +import ( + "fmt" + "io" + "strings" + + "github.com/AliceO2Group/Control/configuration/componentcfg" +) + +// Implements pongo2.TemplateLoader to fetch included templates from Consul paths +type ConsulTemplateLoader struct { + basePath string + confSvc ConfigurationService +} + +func NewConsulTemplateLoader(confSvc ConfigurationService, basePath string) *ConsulTemplateLoader { + return &ConsulTemplateLoader{ + basePath: basePath, + confSvc: confSvc, + } +} + +func (c *ConsulTemplateLoader) Abs(base, name string) string { + if strings.HasPrefix(name, "/") { + return name + } + if strings.HasPrefix(name, c.basePath) { + return name + } + + if c.basePath == "" { + return name + } + return c.basePath + "/" + name +} + +func (c *ConsulTemplateLoader) Get(path string) (io.Reader, error) { + query, err := componentcfg.NewQuery(path) + if err != nil { + return strings.NewReader(fmt.Sprintf("{\"error\":\"%s\"}", err.Error())), err + } + payload, err := c.confSvc.GetComponentConfiguration(query) + if err != nil { + log.WithError(err). + WithField("path", path). + Warn("failed to include component configuration in template") + return strings.NewReader(fmt.Sprintf("{\"error\":\"%s\"}", err.Error())), err + } + return strings.NewReader(payload), nil +} diff --git a/configuration/template/stack.go b/configuration/template/stack.go new file mode 100644 index 000000000..2ba94b6e4 --- /dev/null +++ b/configuration/template/stack.go @@ -0,0 +1,365 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2020-2022 CERN and copyright holders of ALICE O². + * Author: Teo Mrnjavac + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package template + +import ( + "encoding/json" + "errors" + "fmt" + "io/ioutil" + "slices" + "strconv" + "strings" + + "dario.cat/mergo" + apricotpb "github.com/AliceO2Group/Control/apricot/protos" + "github.com/AliceO2Group/Control/configuration/componentcfg" + "github.com/AliceO2Group/Control/core/integration" + "github.com/AliceO2Group/Control/core/repos" + + "github.com/AliceO2Group/Control/common/utils" + "github.com/AliceO2Group/Control/common/utils/uid" + "github.com/sirupsen/logrus" +) + +type GetConfigFunc func(string) string +type ConfigAccessFuncs map[string]GetConfigFunc +type ToPtreeFunc func(string, string) string +type MultiVarConfigAccessFuncs map[string]GetMultiVarConfigFunc +type GetMultiVarConfigFunc func(string, string) string + +func MakeConfigAccessObject(confSvc ConfigurationService, varStack map[string]string) map[string]interface{} { + obj := make(map[string]interface{}) + obj["config"] = map[string]interface{}{ + "Get": func(path string) string { + return getConfig(confSvc, varStack, path) + }, + "GetLegacy": func(path string) string { + return getConfigLegacy(confSvc, varStack, path) + }, + "Resolve": func(component string, runType string, roleName string, entryKey string) string { + rt, ok := apricotpb.RunType_value[runType] + if !ok { + rt = int32(apricotpb.RunType_NULL) + } + return resolveConfig(confSvc, &componentcfg.Query{ + Component: component, + RunType: apricotpb.RunType(rt), + RoleName: roleName, + EntryKey: entryKey, + }) + }, + "ResolvePath": func(path string) string { + return resolveConfigPath(confSvc, path) + }, + } + obj["inventory"] = map[string]interface{}{ + "DetectorForHost": func(hostname string) string { + return detectorForHost(confSvc, hostname) + }, + "DetectorsForHosts": func(hosts string) string { + return detectorsForHosts(confSvc, hosts) + }, + "CRUCardsForHost": func(hostname string) string { + return cruCardsForHost(confSvc, hostname) + }, + "EndpointsForCRUCard": func(hostname, cardSerial string) string { + return endpointsForCruCard(confSvc, hostname, cardSerial) + }, + } + obj["runtime"] = map[string]interface{}{ + "Get": func(component string, key string) string { + return getRuntimeConfig(confSvc, component, key) + }, + "Set": func(component string, key string, value string) string { + return setRuntimeConfig(confSvc, component, key, value) + }, + } + return obj +} + +// deprecated +func MakeConfigAccessFuncs(confSvc ConfigurationService, varStack map[string]string) ConfigAccessFuncs { + return ConfigAccessFuncs{ + "GetConfigLegacy": func(path string) string { + log.WithPrefix("template").Warn("GetConfigLegacy is deprecated, use config.GetLegacy instead") + return getConfigLegacy(confSvc, varStack, path) + }, + "GetConfig": func(path string) string { + log.WithPrefix("template").Warn("GetConfig is deprecated, use config.Get instead") + return getConfig(confSvc, varStack, path) + }, + "ResolveConfigPath": func(path string) string { + log.WithPrefix("template").Warn("ResolveConfigPath is deprecated, use config.ResolvePath instead") + return resolveConfigPath(confSvc, path) + }, + "DetectorForHost": func(hostname string) string { + log.WithPrefix("template").Warn("DetectorForHost is deprecated, use inventory.DetectorForHost instead") + return detectorForHost(confSvc, hostname) + }, + "DetectorsForHosts": func(hosts string) string { + log.WithPrefix("template").Warn("DetectorsForHosts is deprecated, use inventory.DetectorsForHosts instead") + return detectorsForHosts(confSvc, hosts) + }, + "CRUCardsForHost": func(hostname string) string { + log.WithPrefix("template").Warn("CRUCardsForHost is deprecated, use inventory.CRUCardsForHost instead") + return cruCardsForHost(confSvc, hostname) + }, + } +} + +func MakeConfigAccessFuncsMultiVar(confSvc ConfigurationService, varStack map[string]string) MultiVarConfigAccessFuncs { + return MultiVarConfigAccessFuncs{ + "EndpointsForCRUCard": func(hostname, cardSerial string) string { + log.WithPrefix("template").Warn("EndpointsForCRUCard is deprecated, use inventory.EndpointsForCRUCard instead") + return endpointsForCruCard(confSvc, hostname, cardSerial) + }, + "GetRuntimeConfig": func(component string, key string) string { + log.WithPrefix("template").Warn("GetRuntimeConfig is deprecated, use runtime.Get instead") + return getRuntimeConfig(confSvc, component, key) + }, + } +} + +func MakePluginObjectStack(varStack map[string]string, baseConfigStack map[string]string) map[string]interface{} { + return integration.PluginsInstance().ObjectStack(varStack, baseConfigStack) +} + +func MakeConfigAndRepoAccessFuncs(confSvc ConfigurationService, varStack map[string]string, workflowRepo repos.IRepo) map[string]interface{} { + return map[string]interface{}{ + "GenerateDplSubworkflow": func(dplCommand string) (string, error) { + log.WithPrefix("template").Warn("GenerateDplSubworkflow is deprecated, use dpl.Generate instead") + return generateDplSubworkflow(confSvc, varStack, workflowRepo, dplCommand, "unknown") + }, + "GenerateDplSubworkflowFromUri": func(dplCommandUri string) (string, error) { + log.WithPrefix("template").Warn("GenerateDplSubworkflowFromUri is deprecated, use dpl.GenerateFromUri instead") + return generateDplSubworkflowFromUri(confSvc, varStack, workflowRepo, dplCommandUri, false) + }, + "dpl": map[string]interface{}{ + "Generate": func(dplCommand string) (string, error) { + return generateDplSubworkflow(confSvc, varStack, workflowRepo, dplCommand, "unknown") + }, + "GenerateFromUri": func(dplCommandUri string) (string, error) { + return generateDplSubworkflowFromUri(confSvc, varStack, workflowRepo, dplCommandUri, false) + }, + "GenerateFromUriOrFallbackToTemplate": func(dplCommandUri string) (string, error) { + return generateDplSubworkflowFromUri(confSvc, varStack, workflowRepo, dplCommandUri, true) + }, + }, + } +} + +func MakeToPtreeFunc(varStack map[string]string, propMap map[string]string) ToPtreeFunc { + return func(payload string, syntax string) string { + // This function is a no-op with respect to the payload, but it stores the payload + // under a new key which the OCC plugin then processes into a ptree. + // The payload in the current key is overwritten. + localPayload := payload + syntaxLC := strings.ToLower(strings.TrimSpace(syntax)) + + if !utils.StringSliceContains([]string{"ini", "json", "xml"}, syntaxLC) { + err := errors.New("bad ToPtree syntax argument, allowed values: ini, json, xml") + log.WithError(err). + WithField("syntax", syntax). + Warn("failed to generate ptree descriptor") + localPayload = fmt.Sprintf("{\"error\":\"%s\"}", err.Error()) + syntaxLC = "json" + } + + ptreeId := fmt.Sprintf("__ptree__:%s:%s", syntaxLC, uid.New().String()) + propMap[ptreeId] = localPayload + return ptreeId + } +} + +func MakeUtilFuncMap(varStack map[string]string) map[string]interface{} { + legacy := make(map[string]interface{}) + stringsMap := map[string]interface{}{ + "Atoi": func(in string) (out int) { + var err error + out, err = strconv.Atoi(in) + if err != nil { + log.WithError(err).Warn("error converting string/int in template system") + return + } + return + }, + "Itoa": func(in int) (out string) { + out = strconv.Itoa(in) + return + }, + "TrimQuotes": func(in string) (out string) { + out = strings.Trim(in, "\"") + return + }, + "TrimSpace": func(in string) (out string) { + out = strings.TrimSpace(in) + return + }, + "ToUpper": func(in string) (out string) { + out = strings.ToUpper(in) + return + }, + "ToLower": func(in string) (out string) { + out = strings.ToLower(in) + return + }, + "IsTruthy": func(in string) (out bool) { + toLower := strings.TrimSpace(strings.ToLower(in)) + out = slices.Contains([]string{"true", "yes", "y", "1", "on", "ok"}, toLower) + return + }, + "IsFalsy": func(in string) (out bool) { + toLower := strings.TrimSpace(strings.ToLower(in)) + out = len(toLower) == 0 || slices.Contains([]string{"false", "no", "n", "0", "off", "none"}, toLower) + return + }, + } + _ = mergo.Merge(&legacy, stringsMap) + + jsonMap := map[string]interface{}{ + "Unmarshal": func(in string) (out interface{}) { + bytes := []byte(in) + err := json.Unmarshal(bytes, &out) + log.WithFields(logrus.Fields{ + "in": in, + "out": out, + }). + Debug("FromJson") + if err != nil { + log.WithError(err).Warn("error unmarshaling JSON/YAML in template system") + return + } + return + }, + "Marshal": func(in interface{}) (out string) { + bytes, err := json.Marshal(in) + if err != nil { + log.WithError(err).Warn("error marshaling JSON/YAML in template system") + return + } + out = string(bytes) + return + }, + "Sort": func(in interface{}) (out interface{}) { + switch inSlice := in.(type) { + case []interface{}: + stringSlice := make([]string, len(inSlice)) + for idx, item := range inSlice { + stringSlice[idx] = fmt.Sprintf("%v", item) + } + slices.Sort(stringSlice) + out = stringSlice + default: + out = in + } + return + }, + } + jsonMap["Deserialize"] = jsonMap["Unmarshal"] + jsonMap["Serialize"] = jsonMap["Marshal"] + + legacy["FromJson"] = jsonMap["Unmarshal"] + legacy["ToJson"] = jsonMap["Marshal"] + + uidMap := map[string]interface{}{ + "New": func() (out string) { + return uid.New().String() + }, + } + legacy["NewID"] = uidMap["New"] + + utilMap := map[string]interface{}{ + "PrefixedOverride": func(varname, prefix string) string { + prefixed, prefixedOk := varStack[prefix+"_"+varname] + fallback, fallbackOk := varStack[varname] + + // Handle explicit null values + if prefixedOk && (prefixed == "none" || len(strings.TrimSpace(prefixed)) == 0) { + prefixedOk = false + } + if fallbackOk && (fallback == "none" || len(strings.TrimSpace(fallback)) == 0) { + fallbackOk = false + } + + if !prefixedOk { + if fallbackOk { + return fallback + } + return "" // Neither value exists + } + + // prefixedOk is true, fallbackOk we don't know & don't care at this point + return prefixed + }, + "Dump": func(in, filepath string) string { + err := ioutil.WriteFile(filepath, []byte(in), 0644) + if err != nil { + log.WithError(err).Warn("could not dump variable to file") + } + return in + }, + "Nullable": func(in *string) string { + if in == nil { + return "" + } + return *in + }, + "SuffixInRange": func(input string, prefix string, idMinStr string, idMaxStr string) string { + trimmed := strings.TrimPrefix(input, prefix) + if input == trimmed { + return "false" + } + id, err := strconv.Atoi(trimmed) + if err != nil { + return "false" + } + idMin, err := strconv.Atoi(idMinStr) + if err != nil { + log.Errorf("Argument idMinStr to SuffixInRange is not a number: %s", idMinStr) + return "false" + } + idMax, err := strconv.Atoi(idMaxStr) + if err != nil { + log.Errorf("Argument idMaxStr to SuffixInRange is not a number: %s", idMaxStr) + return "false" + } + + if id <= idMax && id >= idMin { + return "true" + } else { + return "false" + } + }, + } + _ = mergo.Merge(&legacy, utilMap) + + legacy["strings"] = stringsMap + legacy["json"] = jsonMap + legacy["uid"] = uidMap + legacy["util"] = utilMap + + return legacy +} diff --git a/configuration/template/stack_test.go b/configuration/template/stack_test.go new file mode 100644 index 000000000..8348f4fe5 --- /dev/null +++ b/configuration/template/stack_test.go @@ -0,0 +1,216 @@ +package template_test + +import ( + "encoding/json" + "github.com/AliceO2Group/Control/apricot/local" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "strings" + + "github.com/AliceO2Group/Control/configuration/template" +) + +var _ = Describe("Template function stack", func() { + + Describe("utility functions", func() { + var utilFuncMap map[string]interface{} + + BeforeEach(func() { + varStack := map[string]string{ + "test_var": "test_value", + "prefix_var": "prefixed_value", + "fallback_var": "fallback_value", + "prefixed_none_fallback_var": "none", + } + utilFuncMap = template.MakeUtilFuncMap(varStack) + }) + + Context("strings functions", func() { + It("should validate inputs and convert string to int", func() { + atoiFunc := utilFuncMap["Atoi"].(func(string) int) + + result := atoiFunc("123") + Expect(result).To(Equal(123)) + // we only produce an error log if unexpected input appears + result = atoiFunc("abc") + Expect(result).To(Equal(0)) + result = atoiFunc("") + Expect(result).To(Equal(0)) + }) + + It("should validate inputs and convert int to string", func() { + itoaFunc := utilFuncMap["Itoa"].(func(int) string) + + Expect(itoaFunc(123)).To(Equal("123")) + Expect(itoaFunc(0)).To(Equal("0")) + Expect(itoaFunc(-456)).To(Equal("-456")) + }) + + It("should trim quotes", func() { + trimQuotesFunc := utilFuncMap["TrimQuotes"].(func(string) string) + + Expect(trimQuotesFunc("\"test\"")).To(Equal("test")) + // Fixme: should it support also single quotes? + //Expect(trimQuotesFunc("'test'")).To(Equal("test")) + + Expect(trimQuotesFunc("test")).To(Equal("test")) + Expect(trimQuotesFunc("")).To(Equal("")) + Expect(trimQuotesFunc("\"test")).To(Equal("test")) + Expect(trimQuotesFunc("test\"")).To(Equal("test")) + }) + + It("should trim spaces", func() { + trimSpaceFunc := utilFuncMap["TrimSpace"].(func(string) string) + + Expect(trimSpaceFunc(" test ")).To(Equal("test")) + Expect(trimSpaceFunc("test")).To(Equal("test")) + Expect(trimSpaceFunc("test test ")).To(Equal("test test")) + }) + + It("should convert to upper case", func() { + toUpperFunc := utilFuncMap["ToUpper"].(func(string) string) + Expect(toUpperFunc("test")).To(Equal("TEST")) + Expect(toUpperFunc("Test")).To(Equal("TEST")) + Expect(toUpperFunc("1")).To(Equal("1")) + }) + + It("should convert to lower case", func() { + toLowerFunc := utilFuncMap["ToLower"].(func(string) string) + Expect(toLowerFunc("TEST")).To(Equal("test")) + Expect(toLowerFunc("Test")).To(Equal("test")) + Expect(toLowerFunc("1")).To(Equal("1")) + }) + + It("should check if a string is truthy", func() { + isTruthyFunc := utilFuncMap["IsTruthy"].(func(string) bool) + + Expect(isTruthyFunc("true")).To(BeTrue()) + Expect(isTruthyFunc("TRUE")).To(BeTrue()) + Expect(isTruthyFunc("yes")).To(BeTrue()) + Expect(isTruthyFunc("y")).To(BeTrue()) + Expect(isTruthyFunc("1")).To(BeTrue()) + Expect(isTruthyFunc("on")).To(BeTrue()) + Expect(isTruthyFunc("ok")).To(BeTrue()) + + Expect(isTruthyFunc("false")).To(BeFalse()) + Expect(isTruthyFunc("")).To(BeFalse()) + Expect(isTruthyFunc("0")).To(BeFalse()) + Expect(isTruthyFunc("truthy")).To(BeFalse()) + }) + + It("should check if a string is falsy", func() { + isFalsyFunc := utilFuncMap["IsFalsy"].(func(string) bool) + Expect(isFalsyFunc("false")).To(BeTrue()) + Expect(isFalsyFunc("FALSE")).To(BeTrue()) + Expect(isFalsyFunc("no")).To(BeTrue()) + Expect(isFalsyFunc("n")).To(BeTrue()) + Expect(isFalsyFunc("0")).To(BeTrue()) + Expect(isFalsyFunc("off")).To(BeTrue()) + Expect(isFalsyFunc("none")).To(BeTrue()) + Expect(isFalsyFunc("")).To(BeTrue()) + + Expect(isFalsyFunc("true")).To(BeFalse()) + Expect(isFalsyFunc("1")).To(BeFalse()) + }) + }) + + Context("json functions", func() { + It("should unmarshal JSON", func() { + unmarshalFunc := utilFuncMap["json"].(map[string]interface{})["Unmarshal"].(func(string) interface{}) + result := unmarshalFunc(`{"key": "value"}`) + Expect(result).To(HaveKeyWithValue("key", "value")) + }) + + It("should marshal to JSON", func() { + marshalFunc := utilFuncMap["json"].(map[string]interface{})["Marshal"].(func(interface{}) string) + input := map[string]string{"key": "value"} + result := marshalFunc(input) + Expect(result).To(MatchJSON(`{"key": "value"}`)) + }) + }) + + Context("uid functions", func() { + It("should generate a new UID", func() { + newFunc := utilFuncMap["uid"].(map[string]interface{})["New"].(func() string) + uid1 := newFunc() + uid2 := newFunc() + Expect(uid1).NotTo(Equal(uid2)) + }) + }) + + Context("util functions", func() { + It("should handle prefixed override", func() { + prefixedOverrideFunc := utilFuncMap["util"].(map[string]interface{})["PrefixedOverride"].(func(string, string) string) + Expect(prefixedOverrideFunc("var", "prefix")).To(Equal("prefixed_value")) + Expect(prefixedOverrideFunc("fallback_var", "nonexistent_prefix")).To(Equal("fallback_value")) + Expect(prefixedOverrideFunc("fallback_var", "prefixed_none")).To(Equal("fallback_value")) + Expect(prefixedOverrideFunc("nonexistent_fallback_var", "prefix")).To(Equal("")) + }) + + It("should handle nullable strings", func() { + nullableFunc := utilFuncMap["util"].(map[string]interface{})["Nullable"].(func(*string) string) + var nilString *string + Expect(nullableFunc(nilString)).To(Equal("")) + + nonNilString := "test" + Expect(nullableFunc(&nonNilString)).To(Equal("test")) + }) + + It("should check if suffix is in range", func() { + suffixInRangeFunc := utilFuncMap["util"].(map[string]interface{})["SuffixInRange"].(func(string, string, string, string) string) + Expect(suffixInRangeFunc("test5", "test", "1", "10")).To(Equal("true")) + Expect(suffixInRangeFunc("test15", "test", "1", "10")).To(Equal("false")) + // no prefix + Expect(suffixInRangeFunc("other5", "test", "1", "10")).To(Equal("false")) + // no suffix + Expect(suffixInRangeFunc("test", "test", "1", "10")).To(Equal("false")) + // range arguments are not numbers + Expect(suffixInRangeFunc("test", "test", "a", "10")).To(Equal("false")) + Expect(suffixInRangeFunc("test", "test", "1", "b")).To(Equal("false")) + }) + }) + }) + + Describe("inventory functions", func() { + var ( + svc *local.Service + configAccessObject map[string]interface{} + err error + ) + BeforeEach(func() { + svc, err = local.NewService("file://" + *tmpDir + "/" + serviceConfigFile) + Expect(err).NotTo(HaveOccurred()) + + varStack := map[string]string{} + configAccessObject = template.MakeConfigAccessObject(svc, varStack) + }) + + It("should get detector for host", func() { + getDetectorFunc := configAccessObject["inventory"].(map[string]interface{})["DetectorForHost"].(func(string) string) + Expect(getDetectorFunc("flp001")).To(Equal("ABC")) + Expect(getDetectorFunc("NOPE")).To(ContainSubstring("error")) + }) + It("should get detectors for a list of hosts", func() { + getDetectorsFunc := configAccessObject["inventory"].(map[string]interface{})["DetectorsForHosts"].(func(string) string) + Expect(getDetectorsFunc("[ \"flp001\", \"flp002\" ]")).To(Equal("[\"ABC\",\"DEF\"]")) + Expect(getDetectorsFunc("[ \"flp001\", \"NOPE\" ]")).To(ContainSubstring("error")) + Expect(getDetectorsFunc("[ \"NOPE\" ]")).To(ContainSubstring("error")) + Expect(getDetectorsFunc("flp001")).To(ContainSubstring("error")) + }) + It("should get CRU cards for host", func() { + getCruCardsFunc := configAccessObject["inventory"].(map[string]interface{})["CRUCardsForHost"].(func(string) string) + var result []string + err := json.Unmarshal([]byte(getCruCardsFunc("flp001")), &result) + Expect(err).NotTo(HaveOccurred()) + Expect(result).To(ConsistOf("0228", "0229")) + Expect(getCruCardsFunc("NOPE")).To(ContainSubstring("error")) + }) + It("should get endpoints for CRU card", func() { + getEndpointsFunc := configAccessObject["inventory"].(map[string]interface{})["EndpointsForCRUCard"].(func(string, string) string) + endpoints := strings.Split(getEndpointsFunc("flp001", "0228"), " ") + Expect(endpoints).To(ConsistOf("0", "1")) + // fixme: probably incorrect behaviour, but I don't want to risk breaking something + Expect(getEndpointsFunc("flp001", "NOPE")).To(BeEmpty()) + }) + }) +}) diff --git a/configuration/template/stack_test.yaml b/configuration/template/stack_test.yaml new file mode 100644 index 000000000..cca28db8e --- /dev/null +++ b/configuration/template/stack_test.yaml @@ -0,0 +1,58 @@ +o2: + components: + qc: + TECHNICAL: + any: + entry: "config" + PHYSICS: + role1: + entry1: "entry1 config PHYSICS role1" + entry2: "entry2 config PHYSICS role1" + ANY: + role1: + entry1: "entry1 config ANY role1" + any: + entry1: "entry1 config ANY any" + entry10: "{{ var1 }} {% include \"entry11\" %}" + entry11: "world" + entry12: "hello {% include \"sub/entry12\" %}" + sub: + entry12: "world" + runtime: + aliecs: + defaults: + key1: value1 + vars: + key2: value2 + hardware: + detectors: + ABC: + flps: + flp001: + cards: "{ \"key\" : \"value\" }" + DEF: + flps: + flp002: + cards: "{ \"key\" : \"value\" }" + flp003: + cards: "{ \"key\" : \"value\" }" + TRG: + flps: + flp100: + cards: "{ \"key\" : \"value\" }" + XYZ: + flps: {} + flps: + flp001: + cards: "{ + \"0\": {\"serial\": \"0228\", \"endpoint\": \"0\"}, + \"1\": {\"serial\": \"0229\", \"endpoint\": \"0\"}, + \"2\": {\"serial\": \"0228\", \"endpoint\": \"1\"}, + \"3\": {\"serial\": \"0229\", \"endpoint\": \"1\"} + }" + flp002: + cards: "{ \"key\" : \"value\" }" + flp003: + cards: "{ \"key\" : \"value\" }" + flp100: + cards: "{ \"key\" : \"value\" }" \ No newline at end of file diff --git a/configuration/template/stackutil.go b/configuration/template/stackutil.go new file mode 100644 index 000000000..ced41e5f0 --- /dev/null +++ b/configuration/template/stackutil.go @@ -0,0 +1,180 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2020-2022 CERN and copyright holders of ALICE O². + * Author: Teo Mrnjavac + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package template + +import ( + "encoding/json" + "fmt" + "github.com/AliceO2Group/Control/common/logger/infologger" + "strings" + texttemplate "text/template" + "time" + + "github.com/AliceO2Group/Control/common/utils" + "github.com/AliceO2Group/Control/configuration/componentcfg" +) + +func getConfigLegacy(confSvc ConfigurationService, varStack map[string]string, path string) string { + defer utils.TimeTrack(time.Now(), "GetConfigLegacy", log.WithPrefix("template")) + query, err := componentcfg.NewQuery(path) + if err != nil { + return fmt.Sprintf("{\"error\":\"%s\"}", err.Error()) + } + + payload, err := confSvc.GetComponentConfiguration(query) + if err != nil { + log.WithError(err). + WithField("path", query.Path()). + Warn("failed to get component configuration") + return fmt.Sprintf("{\"error\":\"%s\"}", err.Error()) + } + + fields := Fields{WrapPointer(&payload)} + err = fields.Execute(confSvc, query.Path(), varStack, nil, nil, make(map[string]texttemplate.Template), nil) + log.WithField("level", infologger.IL_Devel).Debug(varStack) + log.WithField("level", infologger.IL_Devel).Debug(payload) + return payload +} + +func getConfig(confSvc ConfigurationService, varStack map[string]string, path string) string { + defer utils.TimeTrack(time.Now(), "GetConfig", log.WithPrefix("template")) + query, err := componentcfg.NewQuery(path) + if err != nil { + return fmt.Sprintf("{\"error\":\"%s\"}", err.Error()) + } + + payload, err := confSvc.GetAndProcessComponentConfiguration(query, varStack) + if err != nil { + return fmt.Sprintf("{\"error\":\"%s\"}", err.Error()) + } + + log.WithField("level", infologger.IL_Devel).Debug(varStack) + log.WithField("level", infologger.IL_Devel).Debug(payload) + return payload +} + +func resolveConfig(confSvc ConfigurationService, query *componentcfg.Query) string { + defer utils.TimeTrack(time.Now(), "ResolveConfig", log.WithPrefix("template")) + var resolved *componentcfg.Query + resolved, err := confSvc.ResolveComponentQuery(query) + if err != nil { + return fmt.Sprintf("{\"error\":\"%s\"}", err.Error()) + } + + return resolved.Raw() +} + +func resolveConfigPath(confSvc ConfigurationService, path string) string { + defer utils.TimeTrack(time.Now(), "ResolveConfigPath", log.WithPrefix("template")) + query, err := componentcfg.NewQuery(path) + if err != nil { + return fmt.Sprintf("{\"error\":\"%s\"}", err.Error()) + } + + var resolved *componentcfg.Query + resolved, err = confSvc.ResolveComponentQuery(query) + if err != nil { + return fmt.Sprintf("{\"error\":\"%s\"}", err.Error()) + } + + return resolved.Raw() +} + +func detectorForHost(confSvc ConfigurationService, hostname string) string { + payload, err := confSvc.GetDetectorForHost(hostname) + if err != nil { + return fmt.Sprintf("{\"error\":\"%s\"}", err.Error()) + } + return payload +} + +func detectorsForHosts(confSvc ConfigurationService, hosts string) string { + defer utils.TimeTrack(time.Now(), "DetectorsForHosts", log.WithPrefix("template")) + hostsSlice := make([]string, 0) + // first we convert the incoming string treated as JSON list into a []string + bytes := []byte(hosts) + err := json.Unmarshal(bytes, &hostsSlice) + if err != nil { + return fmt.Sprintf("{\"error\":\"DetectorsForHosts function: %s\"}", err.Error()) + } + + payload, err := confSvc.GetDetectorsForHosts(hostsSlice) + if err != nil { + return fmt.Sprintf("{\"error\":\"%s\"}", err.Error()) + } + + // and back to JSON list for the active detectors slice + bytes, err = json.Marshal(payload) + if err != nil { + return fmt.Sprintf("{\"error\":\"DetectorsForHosts function: %s\"}", err.Error()) + } + outString := string(bytes) + + return outString +} + +func cruCardsForHost(confSvc ConfigurationService, hostname string) string { + defer utils.TimeTrack(time.Now(), "CRUCardsForHost", log.WithPrefix("template")) + cards, err := confSvc.GetCRUCardsForHost(hostname) + if err != nil { + return fmt.Sprintf("[\"error: %s\"]", err.Error()) + } + cardsJson, err := json.Marshal(cards) + if err != nil { + return fmt.Sprintf("[\"error: %s\"]", err.Error()) + } + return string(cardsJson) +} + +func endpointsForCruCard(confSvc ConfigurationService, hostname string, cardSerial string) string { + defer utils.TimeTrack(time.Now(), "EndpointsForCRUCard", log.WithPrefix("template")) + endpoints, err := confSvc.GetEndpointsForCRUCard(hostname, cardSerial) + if err != nil { + return fmt.Sprintf("{\"error\":\"%s\"}", err.Error()) + } + return strings.Join(endpoints, " ") +} + +func getRuntimeConfig(confSvc ConfigurationService, component string, key string) string { + defer utils.TimeTrack(time.Now(), "GetRuntimeConfig", log.WithPrefix("template")) + + payload, err := confSvc.GetRuntimeEntry(component, key) + if err != nil { + return fmt.Sprintf("{\"error\":\"%s\"}", err.Error()) + } + + return payload +} + +func setRuntimeConfig(confSvc ConfigurationService, component, key, value string) string { + defer utils.TimeTrack(time.Now(), "SetRuntimeConfig", log.WithPrefix("template")) + + err := confSvc.SetRuntimeEntry(component, key, value) + if err != nil { + return fmt.Sprintf("{\"error\":\"%s\"}", err.Error()) + } + + return value +} diff --git a/configuration/template/template_test.go b/configuration/template/template_test.go new file mode 100644 index 000000000..5c4d6d424 --- /dev/null +++ b/configuration/template/template_test.go @@ -0,0 +1,55 @@ +package template_test + +import ( + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "io" + "os" + "testing" +) + +var tmpDir *string + +const serviceConfigFile = "stack_test.yaml" + +func TestTemplate(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Component Configuration Test Suite") +} + +var _ = BeforeSuite(func() { + var err error + tmpDir = new(string) + *tmpDir, err = os.MkdirTemp("", "o2control-local-service") + Expect(err).NotTo(HaveOccurred()) + + // copy config files + configFiles := []string{serviceConfigFile} + for _, configFile := range configFiles { + from, err := os.Open("./" + configFile) + Expect(err).NotTo(HaveOccurred()) + defer func() { + err := from.Close() + if err != nil { + Expect(err).NotTo(HaveOccurred()) + } + }() + + to, err := os.OpenFile(*tmpDir+"/"+configFile, os.O_RDWR|os.O_CREATE, 0666) + Expect(err).NotTo(HaveOccurred()) + defer func() { + err := to.Close() + if err != nil { + Expect(err).NotTo(HaveOccurred()) + } + }() + + _, err = io.Copy(to, from) + Expect(err).NotTo(HaveOccurred()) + } +}) + +var _ = AfterSuite(func() { + err := os.RemoveAll(*tmpDir) + Expect(err).NotTo(HaveOccurred()) +}) diff --git a/control-operator/.gitignore b/control-operator/.gitignore new file mode 100644 index 000000000..2a14aa847 --- /dev/null +++ b/control-operator/.gitignore @@ -0,0 +1,3 @@ +bin +cover.out +/internal/controller/protos/generated diff --git a/control-operator/Dockerfile b/control-operator/Dockerfile new file mode 100644 index 000000000..b4548ab78 --- /dev/null +++ b/control-operator/Dockerfile @@ -0,0 +1,33 @@ +# Build the manager binary +FROM golang:1.26.8 AS builder +ARG TARGETOS +ARG TARGETARCH +ARG MANAGER=task-manager + +WORKDIR /workspace +# Copy the Go Modules manifests +COPY go.mod go.mod +COPY go.sum go.sum +# cache deps before building and copying source so that we don't need to re-download as much +# and so that source changes don't invalidate our downloaded layer +RUN go mod download + +# Copy the go source +COPY cmd/ cmd/ +COPY api/ api/ +COPY internal/controller/ internal/controller/ +# Build +# the GOARCH has not a default value to allow the binary be built according to the host where the command +# was called. For example, if we call make docker-build in a local env which has the Apple Silicon M1 SO +# the docker BUILDPLATFORM arg will be linux/arm64 when for Apple x86 it will be linux/amd64. Therefore, +# by leaving it empty we can ensure that the container and binary shipped on it will have the same platform. +RUN CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} go build -a -o manager ./cmd/${MANAGER}/ + +# Use distroless as minimal base image to package the manager binary +# Refer to https://github.com/GoogleContainerTools/distroless for more details +FROM gcr.io/distroless/static:nonroot +WORKDIR / +COPY --from=builder /workspace/manager . +USER 65532:65532 + +ENTRYPOINT ["/manager"] diff --git a/control-operator/Makefile b/control-operator/Makefile new file mode 100644 index 000000000..5a57c0c50 --- /dev/null +++ b/control-operator/Makefile @@ -0,0 +1,360 @@ +# VERSION defines the project version for the bundle. +# Update this value when you upgrade the version of your project. +# To re-generate a bundle for another specific version without changing the standard setup, you can: +# - use the VERSION as arg of the bundle target (e.g make bundle VERSION=0.0.2) +# - use environment variables to overwrite this value (e.g export VERSION=0.0.2) +VERSION ?= 0.1.0 + +# CHANNELS define the bundle channels used in the bundle. +# Add a new line here if you would like to change its default config. (E.g CHANNELS = "candidate,fast,stable") +# To re-generate a bundle for other specific channels without changing the standard setup, you can: +# - use the CHANNELS as arg of the bundle target (e.g make bundle CHANNELS=candidate,fast,stable) +# - use environment variables to overwrite this value (e.g export CHANNELS="candidate,fast,stable") +ifneq ($(origin CHANNELS), undefined) +BUNDLE_CHANNELS := --channels=$(CHANNELS) +endif + +# DEFAULT_CHANNEL defines the default channel used in the bundle. +# Add a new line here if you would like to change its default config. (E.g DEFAULT_CHANNEL = "stable") +# To re-generate a bundle for any other default channel without changing the default setup, you can: +# - use the DEFAULT_CHANNEL as arg of the bundle target (e.g make bundle DEFAULT_CHANNEL=stable) +# - use environment variables to overwrite this value (e.g export DEFAULT_CHANNEL="stable") +ifneq ($(origin DEFAULT_CHANNEL), undefined) +BUNDLE_DEFAULT_CHANNEL := --default-channel=$(DEFAULT_CHANNEL) +endif +BUNDLE_METADATA_OPTS ?= $(BUNDLE_CHANNELS) $(BUNDLE_DEFAULT_CHANNEL) + +# IMAGE_TAG_BASE defines the docker.io namespace and part of the image name for remote images. +# This variable is used to construct full image tags for bundle and catalog images. +# +# For example, running 'make bundle-build bundle-push catalog-build catalog-push' will build and push both +# alice.cern/operator-bundle:$VERSION and alice.cern/operator-catalog:$VERSION. +IMAGE_TAG_BASE ?= gitlab-registry.cern.ch/aliceo2group/dockerfiles/aliecs + +# BUNDLE_IMG defines the image:tag used for the bundle. +# You can use it as an arg. (E.g make bundle-build BUNDLE_IMG=/:) +BUNDLE_IMG ?= $(IMAGE_TAG_BASE)/bundle:v$(VERSION) + +# BUNDLE_GEN_FLAGS are the flags passed to the operator-sdk generate bundle command +BUNDLE_GEN_FLAGS ?= -q --overwrite --version $(VERSION) $(BUNDLE_METADATA_OPTS) + +# USE_IMAGE_DIGESTS defines if images are resolved via tags or digests +# You can enable this value if you would like to use SHA Based Digests +# To enable set flag to true +USE_IMAGE_DIGESTS ?= false +ifeq ($(USE_IMAGE_DIGESTS), true) + BUNDLE_GEN_FLAGS += --use-image-digests +endif + +# Set the Operator SDK version to use. By default, what is installed on the system is used. +# This is useful for CI or a project to utilize a specific version of the operator-sdk toolkit. +OPERATOR_SDK_VERSION ?= unknown + +# Image URL to use all building/pushing image targets +TASK_IMG ?= gitlab-registry.cern.ch/aliceo2group/dockerfiles/aliecs/task-manager:latest +ENVIRONMENT_IMG ?= gitlab-registry.cern.ch/aliceo2group/dockerfiles/aliecs/environment-manager:latest +# Name of the image pull secret (in operator-system namespace) used by deployed controllers +PULL_SECRET ?= ghcr-pull +# ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary. +ENVTEST_K8S_VERSION = 1.27.1 + +# Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set) +ifeq (,$(shell go env GOBIN)) +GOBIN=$(shell go env GOPATH)/bin +else +GOBIN=$(shell go env GOBIN) +endif + +# CONTAINER_TOOL defines the container tool to be used for building images. +# Be aware that the target commands are only tested with Docker which is +# scaffolded by default. However, you might want to replace it to use other +# tools. (i.e. podman) +CONTAINER_TOOL ?= docker + +# Setting SHELL to bash allows bash commands to be executed by recipes. +# Options are set to exit when a recipe line exits non-zero or a piped command fails. +SHELL = /usr/bin/env bash -o pipefail +.SHELLFLAGS = -ec + +.PHONY: all +all: build + +##@ General + +# The help target prints out all targets with their descriptions organized +# beneath their categories. The categories are represented by '##@' and the +# target descriptions by '##'. The awk commands is responsible for reading the +# entire set of makefiles included in this invocation, looking for lines of the +# file as xyz: ## something, and then pretty-format the target and help. Then, +# if there's a line with ##@ something, that gets pretty-printed as a category. +# More info on the usage of ANSI control characters for terminal formatting: +# https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_parameters +# More info on the awk command: +# http://linuxcommand.org/lc3_adv_awk.php + +.PHONY: help +help: ## Display this help. + @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m\033[0m\n"} /^[a-zA-Z_0-9-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST) + +##@ Development + +.PHONY: manifests +manifests: controller-gen generate-proto ## Generate WebhookConfiguration, ClusterRole and CustomResourceDefinition objects. + # Note that the option maxDescLen=0 was added in the default scaffold in order to sort out the issue + # Too long: must have at most 262144 bytes. By using kubectl apply to create / update resources an annotation + # is created by K8s API to store the latest version of the resource ( kubectl.kubernetes.io/last-applied-configuration). + # However, it has a size limit and if the CRD is too big with so many long descriptions as this one it will cause the failure. + $(CONTROLLER_GEN) rbac:roleName=manager-role crd:maxDescLen=0 webhook paths="./..." output:crd:artifacts:config=config/crd/bases + +# .PHONY: manifests +# manifests: controller-gen ## Generate WebhookConfiguration, ClusterRole and CustomResourceDefinition objects. +# $(CONTROLLER_GEN) rbac:roleName=manager-role crd webhook paths="./..." output:crd:artifacts:config=config/crd/bases + +.PHONY: generate +generate: controller-gen generate-proto ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations. + $(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt" paths="./..." + +.PHONY: fmt +fmt: ## Run go fmt against code. + go fmt ./... + +.PHONY: vet +vet: ## Run go vet against code. + go vet ./... + +.PHONY: test +test: manifests generate fmt vet envtest ## Run tests. + KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" go test ./... -coverprofile cover.out + +##@ Protobuf + +.PHONY: generate-proto +generate-proto: protoc-gen-go protoc-gen-go-grpc ## Generate protobuf and gRPC Go files from occ.proto. + mkdir -p internal/controller/protos/generated + PATH="$(LOCALBIN):$$PATH" protoc --go_out=internal/controller/protos/generated --go_opt=paths=source_relative --go-grpc_out=internal/controller/protos/generated --go-grpc_opt=paths=source_relative -I internal/controller/protos internal/controller/protos/occ.proto + +.PHONY: clean-proto +clean-proto: ## Remove generated protobuf and gRPC Go files. + rm -rf internal/controller/protos/generated/* + +##@ Build + +.PHONY: build +build: manifests generate fmt vet ## Build manager binaries. + go build -o bin/task-manager ./cmd/task-manager/ + go build -o bin/environment-manager ./cmd/environment-manager/ + +.PHONY: run-task +run-task: manifests generate fmt vet ## Run the task controller from your host. + go run ./cmd/task-manager/ + +.PHONY: run-environment +run-environment: manifests generate fmt vet ## Run the environment controller from your host. + go run ./cmd/environment-manager/ + +# If you wish built the manager image targeting other platforms you can use the --platform flag. +# (i.e. docker build --platform linux/arm64 ). However, you must enable docker buildKit for it. +# More info: https://docs.docker.com/develop/develop-images/build_enhancements/ +.PHONY: docker-build +docker-build: docker-build-task docker-build-environment ## Build all docker images. + +.PHONY: docker-build-task +docker-build-task: test ## Build docker image for the task manager. + $(CONTAINER_TOOL) build --platform linux/amd64 --build-arg MANAGER=task-manager -t ${TASK_IMG} . + +.PHONY: docker-build-environment +docker-build-environment: test ## Build docker image for the environment manager. + $(CONTAINER_TOOL) build --platform linux/amd64 --build-arg MANAGER=environment-manager -t ${ENVIRONMENT_IMG} . + +.PHONY: docker-push +docker-push: docker-push-task docker-push-environment ## Push all docker images. + +.PHONY: docker-push-task +docker-push-task: ## Push docker image for the task manager. + $(CONTAINER_TOOL) push ${TASK_IMG} + +.PHONY: docker-push-environment +docker-push-environment: ## Push docker image for the environment manager. + $(CONTAINER_TOOL) push ${ENVIRONMENT_IMG} + +# PLATFORMS defines the target platforms for the manager image be build to provide support to multiple +# architectures. (i.e. make docker-buildx IMG=myregistry/mypoperator:0.0.1). To use this option you need to: +# - able to use docker buildx . More info: https://docs.docker.com/build/buildx/ +# - have enable BuildKit, More info: https://docs.docker.com/develop/develop-images/build_enhancements/ +# - be able to push the image for your registry (i.e. if you do not inform a valid value via IMG=> then the export will fail) +# To properly provided solutions that supports more than one platform you should use this option. +PLATFORMS ?= linux/arm64,linux/amd64,linux/s390x,linux/ppc64le +.PHONY: docker-buildx +docker-buildx: test ## Build and push docker image for the manager for cross-platform support + # copy existing Dockerfile and insert --platform=${BUILDPLATFORM} into Dockerfile.cross, and preserve the original Dockerfile + sed -e '1 s/\(^FROM\)/FROM --platform=\$$\{BUILDPLATFORM\}/; t' -e ' 1,// s//FROM --platform=\$$\{BUILDPLATFORM\}/' Dockerfile > Dockerfile.cross + - $(CONTAINER_TOOL) buildx create --name project-v3-builder + $(CONTAINER_TOOL) buildx use project-v3-builder + - $(CONTAINER_TOOL) buildx build --push --platform=$(PLATFORMS) --tag ${IMG} -f Dockerfile.cross . + - $(CONTAINER_TOOL) buildx rm project-v3-builder + rm Dockerfile.cross + +##@ Deployment + +ifndef ignore-not-found + ignore-not-found = false +endif + +.PHONY: install +install: manifests kustomize ## Install CRDs into the K8s cluster specified in ~/.kube/config. + $(KUSTOMIZE) build config/crd | $(KUBECTL) apply --server-side -f - + +.PHONY: uninstall +uninstall: manifests kustomize ## Uninstall CRDs from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion. + $(KUSTOMIZE) build config/crd | $(KUBECTL) delete --ignore-not-found=$(ignore-not-found) -f - + +.PHONY: deploy +deploy: deploy-task deploy-environment ## Deploy both controllers to the K8s cluster specified in ~/.kube/config. + +.PHONY: deploy-task +deploy-task: manifests kustomize ## Deploy task controller to the K8s cluster specified in ~/.kube/config. + cd config/task && $(KUSTOMIZE) edit set image task-manager=${TASK_IMG} + $(KUSTOMIZE) build config/task | sed 's/- name: pull-secret-placeholder$$/- name: $(PULL_SECRET)/' | $(KUBECTL) apply -f - --server-side + +.PHONY: deploy-environment +deploy-environment: manifests kustomize ## Deploy environment controller to the K8s cluster specified in ~/.kube/config. + cd config/environment && $(KUSTOMIZE) edit set image environment-manager=${ENVIRONMENT_IMG} + $(KUSTOMIZE) build config/environment | sed 's/- name: pull-secret-placeholder$$/- name: $(PULL_SECRET)/' | $(KUBECTL) apply -f - --server-side + +.PHONY: undeploy +undeploy: undeploy-task undeploy-environment ## Undeploy both controllers from the K8s cluster specified in ~/.kube/config. + +.PHONY: undeploy-task +undeploy-task: kustomize ## Undeploy task controller from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion. + $(KUSTOMIZE) build config/task | $(KUBECTL) delete --ignore-not-found=$(ignore-not-found) -f - + +.PHONY: undeploy-environment +undeploy-environment: kustomize ## Undeploy environment controller from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion. + $(KUSTOMIZE) build config/environment | $(KUBECTL) delete --ignore-not-found=$(ignore-not-found) -f - + +##@ Build Dependencies + +## Location to install dependencies to +LOCALBIN ?= $(shell pwd)/bin +$(LOCALBIN): + mkdir -p $(LOCALBIN) + +## Tool Binaries +KUBECTL ?= kubectl +KUSTOMIZE ?= $(LOCALBIN)/kustomize +CONTROLLER_GEN ?= $(LOCALBIN)/controller-gen +ENVTEST ?= $(LOCALBIN)/setup-envtest +PROTOC_GEN_GO ?= $(LOCALBIN)/protoc-gen-go +PROTOC_GEN_GO_GRPC ?= $(LOCALBIN)/protoc-gen-go-grpc + +## Tool Versions +KUSTOMIZE_VERSION ?= v5.0.1 +CONTROLLER_TOOLS_VERSION ?= v0.16.5 +PROTOC_GEN_GO_VERSION ?= v1.36.11 +PROTOC_GEN_GO_GRPC_VERSION ?= v1.6.2 + +.PHONY: kustomize +kustomize: $(KUSTOMIZE) ## Download kustomize locally if necessary. If wrong version is installed, it will be removed before downloading. +$(KUSTOMIZE): $(LOCALBIN) + @if test -x $(LOCALBIN)/kustomize && ! $(LOCALBIN)/kustomize version | grep -q $(KUSTOMIZE_VERSION); then \ + echo "$(LOCALBIN)/kustomize version is not expected $(KUSTOMIZE_VERSION). Removing it before installing."; \ + rm -rf $(LOCALBIN)/kustomize; \ + fi + test -s $(LOCALBIN)/kustomize || GOBIN=$(LOCALBIN) GO111MODULE=on go install sigs.k8s.io/kustomize/kustomize/v5@$(KUSTOMIZE_VERSION) + +.PHONY: controller-gen +controller-gen: $(CONTROLLER_GEN) ## Download controller-gen locally if necessary. If wrong version is installed, it will be overwritten. +$(CONTROLLER_GEN): $(LOCALBIN) + test -s $(LOCALBIN)/controller-gen && $(LOCALBIN)/controller-gen --version | grep -q $(CONTROLLER_TOOLS_VERSION) || \ + GOBIN=$(LOCALBIN) go install sigs.k8s.io/controller-tools/cmd/controller-gen@$(CONTROLLER_TOOLS_VERSION) + +.PHONY: envtest +envtest: $(ENVTEST) ## Download envtest-setup locally if necessary. +$(ENVTEST): $(LOCALBIN) + test -s $(LOCALBIN)/setup-envtest || GOBIN=$(LOCALBIN) go install sigs.k8s.io/controller-runtime/tools/setup-envtest@latest + +.PHONY: protoc-gen-go +protoc-gen-go: $(PROTOC_GEN_GO) ## Download protoc-gen-go locally if necessary. If wrong version is installed, it will be overwritten. +$(PROTOC_GEN_GO): $(LOCALBIN) + test -s $(LOCALBIN)/protoc-gen-go && $(LOCALBIN)/protoc-gen-go --version | grep -q $(PROTOC_GEN_GO_VERSION) || \ + GOBIN=$(LOCALBIN) go install google.golang.org/protobuf/cmd/protoc-gen-go@$(PROTOC_GEN_GO_VERSION) + +.PHONY: protoc-gen-go-grpc +protoc-gen-go-grpc: $(PROTOC_GEN_GO_GRPC) ## Download protoc-gen-go-grpc locally if necessary. If wrong version is installed, it will be overwritten. +$(PROTOC_GEN_GO_GRPC): $(LOCALBIN) + test -s $(LOCALBIN)/protoc-gen-go-grpc && $(LOCALBIN)/protoc-gen-go-grpc --version | grep -q $(subst v,,$(PROTOC_GEN_GO_GRPC_VERSION)) || \ + GOBIN=$(LOCALBIN) go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@$(PROTOC_GEN_GO_GRPC_VERSION) + +.PHONY: operator-sdk +OPERATOR_SDK ?= $(LOCALBIN)/operator-sdk +operator-sdk: ## Download operator-sdk locally if necessary. +ifeq (,$(wildcard $(OPERATOR_SDK))) +ifeq (, $(shell which operator-sdk 2>/dev/null)) + @{ \ + set -e ;\ + mkdir -p $(dir $(OPERATOR_SDK)) ;\ + OS=$(shell go env GOOS) && ARCH=$(shell go env GOARCH) && \ + curl -sSLo $(OPERATOR_SDK) https://github.com/operator-framework/operator-sdk/releases/download/$(OPERATOR_SDK_VERSION)/operator-sdk_$${OS}_$${ARCH} ;\ + chmod +x $(OPERATOR_SDK) ;\ + } +else +OPERATOR_SDK = $(shell which operator-sdk) +endif +endif + +.PHONY: bundle +bundle: manifests kustomize operator-sdk ## Generate bundle manifests and metadata, then validate generated files. + $(OPERATOR_SDK) generate kustomize manifests -q + cd config/manager && $(KUSTOMIZE) edit set image task-manager=$(TASK_IMG) + cd config/manager && $(KUSTOMIZE) edit set image environment-manager=$(ENVIRONMENT_IMG) + $(KUSTOMIZE) build config/manifests | $(OPERATOR_SDK) generate bundle $(BUNDLE_GEN_FLAGS) + $(OPERATOR_SDK) bundle validate ./bundle + +.PHONY: bundle-build +bundle-build: ## Build the bundle image. + docker build -f bundle.Dockerfile -t $(BUNDLE_IMG) . + +.PHONY: bundle-push +bundle-push: ## Push the bundle image. + $(MAKE) docker-push IMG=$(BUNDLE_IMG) + +.PHONY: opm +OPM = ./bin/opm +opm: ## Download opm locally if necessary. +ifeq (,$(wildcard $(OPM))) +ifeq (,$(shell which opm 2>/dev/null)) + @{ \ + set -e ;\ + mkdir -p $(dir $(OPM)) ;\ + OS=$(shell go env GOOS) && ARCH=$(shell go env GOARCH) && \ + curl -sSLo $(OPM) https://github.com/operator-framework/operator-registry/releases/download/v1.23.0/$${OS}-$${ARCH}-opm ;\ + chmod +x $(OPM) ;\ + } +else +OPM = $(shell which opm) +endif +endif + +# A comma-separated list of bundle images (e.g. make catalog-build BUNDLE_IMGS=example.com/operator-bundle:v0.1.0,example.com/operator-bundle:v0.2.0). +# These images MUST exist in a registry and be pull-able. +BUNDLE_IMGS ?= $(BUNDLE_IMG) + +# The image tag given to the resulting catalog image (e.g. make catalog-build CATALOG_IMG=example.com/operator-catalog:v0.2.0). +CATALOG_IMG ?= $(IMAGE_TAG_BASE)/catalog:v$(VERSION) + +# Set CATALOG_BASE_IMG to an existing catalog image tag to add $BUNDLE_IMGS to that image. +ifneq ($(origin CATALOG_BASE_IMG), undefined) +FROM_INDEX_OPT := --from-index $(CATALOG_BASE_IMG) +endif + +# Build a catalog image by adding bundle images to an empty catalog using the operator package manager tool, 'opm'. +# This recipe invokes 'opm' in 'semver' bundle add mode. For more information on add modes, see: +# https://github.com/operator-framework/community-operators/blob/7f1438c/docs/packaging-operator.md#updating-your-existing-operator +.PHONY: catalog-build +catalog-build: opm ## Build a catalog image. + $(OPM) index add --container-tool docker --mode semver --tag $(CATALOG_IMG) --bundles $(BUNDLE_IMGS) $(FROM_INDEX_OPT) + +# Push the catalog image. +.PHONY: catalog-push +catalog-push: ## Push a catalog image. + $(MAKE) docker-push IMG=$(CATALOG_IMG) diff --git a/control-operator/PROJECT b/control-operator/PROJECT new file mode 100644 index 000000000..0f052ac36 --- /dev/null +++ b/control-operator/PROJECT @@ -0,0 +1,40 @@ +# Code generated by tool. DO NOT EDIT. +# This file is used to track the info used to scaffold your project +# and allow the plugins properly work. +# More info: https://book.kubebuilder.io/reference/project-config.html +domain: alice.cern +layout: +- go.kubebuilder.io/v4 +plugins: + manifests.sdk.operatorframework.io/v2: {} + scorecard.sdk.operatorframework.io/v2: {} +projectName: operator +repo: github.com/AliceO2Group/Control/control-operator +resources: +- api: + crdVersion: v1 + namespaced: true + controller: true + domain: alice.cern + group: aliecs + kind: Task + path: github.com/AliceO2Group/Control/control-operator/api/v1alpha1 + version: v1alpha1 +- api: + crdVersion: v1 + namespaced: true + controller: true + domain: alice.cern + group: aliecs + kind: Environment + path: github.com/AliceO2Group/Control/control-operator/api/v1alpha1 + version: v1alpha1 +- api: + crdVersion: v1 + namespaced: true + domain: alice.cern + group: aliecs + kind: TaskTemplate + path: github.com/AliceO2Group/Control/control-operator/api/v1alpha1 + version: v1alpha1 +version: "3" diff --git a/control-operator/README.md b/control-operator/README.md new file mode 100644 index 000000000..475060914 --- /dev/null +++ b/control-operator/README.md @@ -0,0 +1,133 @@ +# operator + +Folder with operators for Task and Environment deployment. For a better understanding of how the operators are used by ECS and the Control repository, see [docs/kubernetes_ecs.md](../docs/kubernetes_ecs.md). + +## Description + +In order to deploy Task and Environment workflows to the k8s cluster, you need controllers and operators +controlling the CRDs that define ALICE custom workloads. This folder defines and implements all the moving parts, together with a Makefile +to build, deploy, and install the CRDs and operators. + +## Architecture + +The operator is split into two separate binaries with different deployment strategies: + +**task-manager** runs as a DaemonSet — one pod per node. Each pod is responsible only for `Task` resources assigned to its node (matched via `spec.nodeName`). This is necessary because the task-manager communicates with OCC gRPC processes running locally on the same node via `hostNetwork`. + +**environment-manager** runs as a Deployment with a single replica per cluster. It is responsible for `Environment` resources which are cluster-scoped and not tied to a specific node. + +## Getting Started + +You’ll need a Kubernetes cluster to run against. You can use [KIND](https://sigs.k8s.io/kind) to get a local cluster for testing, or run against a remote cluster. The author had the most success with K3s ([see](/docs/kubernetes_ecs.md)). +**Note:** Your controller will automatically use the current context in your kubeconfig file (usually `~/.kube/config`) (i.e. whatever cluster `kubectl cluster-info` shows). + +### Running on the cluster + +#### Namespaces + +This project expects two namespaces: `alice-tasks` and `operator-system`. `alice-tasks` is where the actual `Task` resources run, and it must be created before the first Task is deployed. `operator-system` is where all operator pods run, and it is created during deployment to the cluster. + +#### Secrets + +When deploying operator containers, you are required to set up proper secrets to access the registry where the operator images reside. At the moment they are built by a GitHub pipeline and pushed to the AliceO2Group GitHub [package registry](https://github.com/orgs/AliceO2Group/packages). + +The second required secret must be set up to access images used by `Task` CRs. At the moment these images are located in a GitLab registry, and the secret name is hardcoded in this repository as `gitlab-registry-secret`. + +#### make commands + +The following commands show basic usage of the Makefile. However, this isn't an exhaustive list. + +- Install instances of Custom Resources: + +```sh +kubectl apply -f config/samples/ +``` + +- Build and push your images. Default image tags are defined in the Makefile via `TASK_IMG` and `ENVIRONMENT_IMG`. Override them only if you want to use a different registry or tag: + +```sh +make docker-build docker-push TASK_IMG=/task-manager:tag ENVIRONMENT_IMG=/environment-manager:tag +``` + +- Deploy the controllers to the cluster. Uses the same `TASK_IMG` and `ENVIRONMENT_IMG` defaults; override them if needed. Images are pulled using the secret named by `PULL_SECRET` (default `ghcr-pull`), which must exist in the `operator-system` namespace: + +```sh +make deploy TASK_IMG=/task-manager:tag ENVIRONMENT_IMG=/environment-manager:tag PULL_SECRET= +``` + +### Uninstall CRDs + +To delete the CRDs from the cluster (careful: this also deletes all `Task`, `Environment` and `TaskTemplate` resources in the whole cluster): + +```sh +make uninstall +``` + +### Undeploy controller + +Undeploy the controllers from the cluster (careful: this deletes the whole `operator-system` namespace, the RBAC resources and the CRDs together with all `Task`, `Environment` and `TaskTemplate` resources; this also applies to `make undeploy-task` and `make undeploy-environment` on their own): + +```sh +make undeploy +``` + +## Contributing + +// TODO(user): Add detailed information on how you would like others to contribute to this project + +### How it works + +This project aims to follow the Kubernetes [Operator pattern](https://kubernetes.io/docs/concepts/extend-kubernetes/operator/). + +It uses [Controllers](https://kubernetes.io/docs/concepts/architecture/controller/), +which provide a reconcile function responsible for synchronizing resources until the desired state is reached on the cluster. + +### Test It Out + +1. Install the CRDs into the cluster: + +```sh +make install +``` + +1. Run a controller (this will run in the foreground, so switch to a new terminal if you want to leave it running): + +```sh +make run-environment +``` + +The task-manager requires a `NODE_NAME` environment variable to know which node it is responsible for. In-cluster this is injected automatically via the downward API. When running locally you must set it manually: + +```sh +NODE_NAME= make run-task +``` + +**NOTE:** You can also install the CRDs and run in one step: `make install run-environment` or `make install run-task`. + +### Modifying the API definitions + +If you are editing the API definitions, generate the manifests such as CRs or CRDs using: + +```sh +make manifests +``` + +**NOTE:** Run `make --help` for more information on all available `make` targets. + +More information can be found in the [Kubebuilder Documentation](https://book.kubebuilder.io/introduction.html). + +## License + +Copyright 2024. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/control-operator/api/v1alpha1/environment_types.go b/control-operator/api/v1alpha1/environment_types.go new file mode 100644 index 000000000..330fbde11 --- /dev/null +++ b/control-operator/api/v1alpha1/environment_types.go @@ -0,0 +1,133 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2026 CERN and copyright holders of ALICE O². + * Author: Michal Tichak + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package v1alpha1 + +import ( + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! +// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. + +// TaskDefinition is a lightweight inline task definition used within an Environment spec. +// Unlike TaskTemplate, it is not a cluster resource — it carries a plain name and the same spec fields. +type TaskDefinition struct { + Name string `json:"name"` + Spec TaskTemplateSpec `json:"spec"` +} + +// EnvironmentSpec defines the desired state of Environment +type EnvironmentSpec struct { + // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster + // Important: Run "make" to regenerate code after modifying this file + // The following markers will use OpenAPI v3 schema to validate the value + // More info: https://book.kubebuilder.io/reference/markers/crd-validation.html + + Tasks map[string][]TaskDefinition `json:"tasks"` + // +kubebuilder:validation:Enum=standby;deployed;configured;running + State string `json:"state"` +} + +// EnvironmentStatus defines the observed state of Environment. +type EnvironmentStatus struct { + // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster + // Important: Run "make" to regenerate code after modifying this file + + // For Kubernetes API conventions, see: + // https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties + // conditions represent the current state of the Environment resource. + // Each condition has a unique type and reflects the status of a specific aspect of the resource. + // + // Standard condition types include: + // - "Available": the resource is fully functional + // - "Progressing": the resource is being created or updated + // - "Degraded": the resource failed to reach or maintain its desired state + // + // TODO: use conditions properly during deployment + // + // The status of each condition is one of True, False, or Unknown. + // +listType=map + // +listMapKey=type + // +optional + Conditions []metav1.Condition `json:"conditions,omitempty"` + + Tasks map[string]map[string]string `json:"tasks"` + State string `json:"state,omitempty"` +} + +type TaskReference struct { + Name string `json:"name"` + TaskID string `json:"taskID,omitempty"` + NameSuffix string `json:"nameSuffix,omitempty"` + Env []v1.EnvVar `json:"env"` + ArgsCLI []string `json:"argsCLI"` + ArgsTransition map[string]string `json:"argsTransition,omitempty"` +} + +type TemplateSpecification struct { + Tasks map[string][]TaskReference `json:"tasks"` +} + +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status +// +kubebuilder:printcolumn:name="Desired",type="string",JSONPath=".spec.state" +// +kubebuilder:printcolumn:name="Actual",type="string",JSONPath=".status.state" +// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp" + +// Environment is the Schema for the environments API +type Environment struct { + metav1.TypeMeta `json:",inline"` + + // metadata is a standard object metadata + // +optional + metav1.ObjectMeta `json:"metadata,omitzero"` + + // taskTemplates defines templates stored in cluster to be used + // for task creation, meant for more common tasks + // +optional + TaskTemplates TemplateSpecification `json:"taskTemplates"` + + // spec defines the desired state of Environment + // +required + Spec EnvironmentSpec `json:"spec"` + + // status defines the observed state of Environment + // +optional + Status EnvironmentStatus `json:"status,omitzero"` +} + +// +kubebuilder:object:root=true + +// EnvironmentList contains a list of Environment +type EnvironmentList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitzero"` + Items []Environment `json:"items"` +} + +func init() { + SchemeBuilder.Register(&Environment{}, &EnvironmentList{}) +} diff --git a/control-operator/api/v1alpha1/groupversion_info.go b/control-operator/api/v1alpha1/groupversion_info.go new file mode 100644 index 000000000..55a752379 --- /dev/null +++ b/control-operator/api/v1alpha1/groupversion_info.go @@ -0,0 +1,44 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2026 CERN and copyright holders of ALICE O². + * Author: Michal Tichak + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +// Package v1alpha1 contains API Schema definitions for the aliecs v1alpha1 API group +// +kubebuilder:object:generate=true +// +groupName=aliecs.alice.cern +package v1alpha1 + +import ( + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/controller-runtime/pkg/scheme" +) + +var ( + // GroupVersion is group version used to register these objects + GroupVersion = schema.GroupVersion{Group: "aliecs.alice.cern", Version: "v1alpha1"} + + // SchemeBuilder is used to add go types to the GroupVersionKind scheme + SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} + + // AddToScheme adds the types in this group-version to the given scheme. + AddToScheme = SchemeBuilder.AddToScheme +) diff --git a/control-operator/api/v1alpha1/task_types.go b/control-operator/api/v1alpha1/task_types.go new file mode 100644 index 000000000..6b690d8be --- /dev/null +++ b/control-operator/api/v1alpha1/task_types.go @@ -0,0 +1,137 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2026 CERN and copyright holders of ALICE O². + * Author: Michal Tichak + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package v1alpha1 + +import ( + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! +// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. + +type TaskSpecControl struct { + // +kubebuilder:validation:Enum=direct;fairmq;basic;hook + Mode string `json:"mode,omitempty"` + // +kubebuilder:validation:Minimum:1024 + // +kubebuilder:validation:Maximum:49151 + Port int `json:"port,omitempty"` +} + +type TaskSpecChannelInbound struct { + Name string `json:"name"` + // +kubebuilder:validation:Enum=push;pull;pub;sub + Type string `json:"type"` + SndBufSize int `json:"sndBufSize,omitempty"` + RcvBufSize int `json:"rcvBufSize,omitempty"` + RateLogging string `json:"rateLogging,omitempty"` + // +kubebuilder:validation:Enum=default;zeromq;nanomsg;shmem + // +kubebuilder:default=default + Transport string `json:"transport,omitempty"` + Target string `json:"target"` + + Global string `json:"global"` + // +kubebuilder:validation:Enum=tcp;ipc + // +kubebuilder:default=tcp + Addressing string `json:"addressing,omitempty"` +} + +type TaskSpecChannelOutbound struct { + Name string `json:"name"` + // +kubebuilder:validation:Enum=push;pull;pub;sub + Type string `json:"type"` + SndBufSize int `json:"sndBufSize,omitempty"` + RcvBufSize int `json:"rcvBufSize,omitempty"` + RateLogging string `json:"rateLogging,omitempty"` + // +kubebuilder:validation:Enum=default;zeromq;nanomsg;shmem + // +kubebuilder:default=default + Transport string `json:"transport,omitempty"` + Target string `json:"target"` +} + +// TaskSpec defines the desired state of Task +type TaskSpec struct { + // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster + // Important: Run "make" to regenerate code after modifying this file + + // Foo is an example field of Task. Edit task_types.go to remove/update + Pod v1.PodSpec `json:"pod,omitempty"` + Control TaskSpecControl `json:"control,omitempty"` + Bind []TaskSpecChannelInbound `json:"bind,omitempty"` + Connect []TaskSpecChannelOutbound `json:"connect,omitempty"` + Properties map[string]string `json:"properties,omitempty"` + Arguments map[string]string `json:"arguments,omitempty"` + // +kubebuilder:validation:Enum=standby;deployed;configured;running + State string `json:"state,omitempty"` // this is the *requested* state, there are other states the task may end up in but cannot be requested + NodeName string `json:"nodeName,omitempty"` +} + +const ( + ConditionPodReady = "PodReady" + ConditionGRPCConnected = "GRPCConnected" + ConditionStateAccessible = "StateAccessible" + ConditionStateTransitionResult = "StateTransitionResult" +) + +// TaskStatus defines the observed state of Task +type TaskStatus struct { + // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster + // Important: Run "make" to regenerate code after modifying this file + Pod v1.PodStatus `json:"pod,omitempty"` + State string `json:"state,omitempty"` + Error string `json:"error,omitempty"` + // +listType=map + // +listMapKey=type + // +optional + Conditions []metav1.Condition `json:"conditions,omitempty"` +} + +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status +// +kubebuilder:printcolumn:name="Desired",type="string",JSONPath=".spec.state" +// +kubebuilder:printcolumn:name="Actual",type="string",JSONPath=".status.state" +// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp" + +// Task is the Schema for the tasks API +type Task struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec TaskSpec `json:"spec,omitempty"` + Status TaskStatus `json:"status,omitempty"` +} + +//+kubebuilder:object:root=true + +// TaskList contains a list of Task +type TaskList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []Task `json:"items"` +} + +func init() { + SchemeBuilder.Register(&Task{}, &TaskList{}) +} diff --git a/control-operator/api/v1alpha1/tasktemplate_types.go b/control-operator/api/v1alpha1/tasktemplate_types.go new file mode 100644 index 000000000..29e510324 --- /dev/null +++ b/control-operator/api/v1alpha1/tasktemplate_types.go @@ -0,0 +1,91 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2026 CERN and copyright holders of ALICE O². + * Author: Michal Tichak + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package v1alpha1 + +import ( + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! +// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. + +// TaskTemplateSpec defines the static part of a task that is reused across environments. +// Dynamic parts (node, arguments) are provided by the Environment at instantiation time. +type TaskTemplateSpec struct { + // Pod defines the pod spec for the task container, excluding node assignment. + Pod v1.PodSpec `json:"pod,omitempty"` + + // Control defines the OCC gRPC control mode and port. + Control TaskSpecControl `json:"control,omitempty"` + + // Bind defines the inbound FairMQ channels exposed by this task. + // +optional + Bind []TaskSpecChannelInbound `json:"bind,omitempty"` + + // Connect defines the outbound FairMQ channels this task connects to. + // +optional + Connect []TaskSpecChannelOutbound `json:"connect,omitempty"` + + // Properties defines static key-value properties passed to the task. + // +optional + Properties map[string]string `json:"properties,omitempty"` + + // Arguments defines transition arguments passed to the task via OCC gRPC. + // +optional + Arguments map[string]string `json:"arguments,omitempty"` + + // Names of the expected Environment variables to be passed to Pod + // +optional + EnvVars []string `json:"envVars,omitempty"` +} + +// +kubebuilder:object:root=true + +// TaskTemplate is the Schema for the tasktemplates API +type TaskTemplate struct { + metav1.TypeMeta `json:",inline"` + + // metadata is a standard object metadata + // +optional + metav1.ObjectMeta `json:"metadata,omitzero"` + + // spec defines the static task definition + // +required + Spec TaskTemplateSpec `json:"spec"` +} + +// +kubebuilder:object:root=true + +// TaskTemplateList contains a list of TaskTemplate +type TaskTemplateList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitzero"` + Items []TaskTemplate `json:"items"` +} + +func init() { + SchemeBuilder.Register(&TaskTemplate{}, &TaskTemplateList{}) +} diff --git a/control-operator/api/v1alpha1/zz_generated.deepcopy.go b/control-operator/api/v1alpha1/zz_generated.deepcopy.go new file mode 100644 index 000000000..695e2f3cd --- /dev/null +++ b/control-operator/api/v1alpha1/zz_generated.deepcopy.go @@ -0,0 +1,523 @@ +//go:build !ignore_autogenerated + +/* + * === This file is part of ALICE O² === + * + * Copyright 2026 CERN and copyright holders of ALICE O². + * Author: Michal Tichak + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +// Code generated by controller-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Environment) DeepCopyInto(out *Environment) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.TaskTemplates.DeepCopyInto(&out.TaskTemplates) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Environment. +func (in *Environment) DeepCopy() *Environment { + if in == nil { + return nil + } + out := new(Environment) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Environment) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EnvironmentList) DeepCopyInto(out *EnvironmentList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Environment, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvironmentList. +func (in *EnvironmentList) DeepCopy() *EnvironmentList { + if in == nil { + return nil + } + out := new(EnvironmentList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *EnvironmentList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EnvironmentSpec) DeepCopyInto(out *EnvironmentSpec) { + *out = *in + if in.Tasks != nil { + in, out := &in.Tasks, &out.Tasks + *out = make(map[string][]TaskDefinition, len(*in)) + for key, val := range *in { + var outVal []TaskDefinition + if val == nil { + (*out)[key] = nil + } else { + inVal := (*in)[key] + in, out := &inVal, &outVal + *out = make([]TaskDefinition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + (*out)[key] = outVal + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvironmentSpec. +func (in *EnvironmentSpec) DeepCopy() *EnvironmentSpec { + if in == nil { + return nil + } + out := new(EnvironmentSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EnvironmentStatus) DeepCopyInto(out *EnvironmentStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]v1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Tasks != nil { + in, out := &in.Tasks, &out.Tasks + *out = make(map[string]map[string]string, len(*in)) + for key, val := range *in { + var outVal map[string]string + if val == nil { + (*out)[key] = nil + } else { + inVal := (*in)[key] + in, out := &inVal, &outVal + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + (*out)[key] = outVal + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvironmentStatus. +func (in *EnvironmentStatus) DeepCopy() *EnvironmentStatus { + if in == nil { + return nil + } + out := new(EnvironmentStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Task) DeepCopyInto(out *Task) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Task. +func (in *Task) DeepCopy() *Task { + if in == nil { + return nil + } + out := new(Task) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Task) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TaskDefinition) DeepCopyInto(out *TaskDefinition) { + *out = *in + in.Spec.DeepCopyInto(&out.Spec) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TaskDefinition. +func (in *TaskDefinition) DeepCopy() *TaskDefinition { + if in == nil { + return nil + } + out := new(TaskDefinition) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TaskList) DeepCopyInto(out *TaskList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Task, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TaskList. +func (in *TaskList) DeepCopy() *TaskList { + if in == nil { + return nil + } + out := new(TaskList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *TaskList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TaskReference) DeepCopyInto(out *TaskReference) { + *out = *in + if in.Env != nil { + in, out := &in.Env, &out.Env + *out = make([]corev1.EnvVar, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.ArgsCLI != nil { + in, out := &in.ArgsCLI, &out.ArgsCLI + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.ArgsTransition != nil { + in, out := &in.ArgsTransition, &out.ArgsTransition + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TaskReference. +func (in *TaskReference) DeepCopy() *TaskReference { + if in == nil { + return nil + } + out := new(TaskReference) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TaskSpec) DeepCopyInto(out *TaskSpec) { + *out = *in + in.Pod.DeepCopyInto(&out.Pod) + out.Control = in.Control + if in.Bind != nil { + in, out := &in.Bind, &out.Bind + *out = make([]TaskSpecChannelInbound, len(*in)) + copy(*out, *in) + } + if in.Connect != nil { + in, out := &in.Connect, &out.Connect + *out = make([]TaskSpecChannelOutbound, len(*in)) + copy(*out, *in) + } + if in.Properties != nil { + in, out := &in.Properties, &out.Properties + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.Arguments != nil { + in, out := &in.Arguments, &out.Arguments + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TaskSpec. +func (in *TaskSpec) DeepCopy() *TaskSpec { + if in == nil { + return nil + } + out := new(TaskSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TaskSpecChannelInbound) DeepCopyInto(out *TaskSpecChannelInbound) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TaskSpecChannelInbound. +func (in *TaskSpecChannelInbound) DeepCopy() *TaskSpecChannelInbound { + if in == nil { + return nil + } + out := new(TaskSpecChannelInbound) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TaskSpecChannelOutbound) DeepCopyInto(out *TaskSpecChannelOutbound) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TaskSpecChannelOutbound. +func (in *TaskSpecChannelOutbound) DeepCopy() *TaskSpecChannelOutbound { + if in == nil { + return nil + } + out := new(TaskSpecChannelOutbound) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TaskSpecControl) DeepCopyInto(out *TaskSpecControl) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TaskSpecControl. +func (in *TaskSpecControl) DeepCopy() *TaskSpecControl { + if in == nil { + return nil + } + out := new(TaskSpecControl) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TaskStatus) DeepCopyInto(out *TaskStatus) { + *out = *in + in.Pod.DeepCopyInto(&out.Pod) + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]v1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TaskStatus. +func (in *TaskStatus) DeepCopy() *TaskStatus { + if in == nil { + return nil + } + out := new(TaskStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TaskTemplate) DeepCopyInto(out *TaskTemplate) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TaskTemplate. +func (in *TaskTemplate) DeepCopy() *TaskTemplate { + if in == nil { + return nil + } + out := new(TaskTemplate) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *TaskTemplate) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TaskTemplateList) DeepCopyInto(out *TaskTemplateList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]TaskTemplate, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TaskTemplateList. +func (in *TaskTemplateList) DeepCopy() *TaskTemplateList { + if in == nil { + return nil + } + out := new(TaskTemplateList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *TaskTemplateList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TaskTemplateSpec) DeepCopyInto(out *TaskTemplateSpec) { + *out = *in + in.Pod.DeepCopyInto(&out.Pod) + out.Control = in.Control + if in.Bind != nil { + in, out := &in.Bind, &out.Bind + *out = make([]TaskSpecChannelInbound, len(*in)) + copy(*out, *in) + } + if in.Connect != nil { + in, out := &in.Connect, &out.Connect + *out = make([]TaskSpecChannelOutbound, len(*in)) + copy(*out, *in) + } + if in.Properties != nil { + in, out := &in.Properties, &out.Properties + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.Arguments != nil { + in, out := &in.Arguments, &out.Arguments + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.EnvVars != nil { + in, out := &in.EnvVars, &out.EnvVars + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TaskTemplateSpec. +func (in *TaskTemplateSpec) DeepCopy() *TaskTemplateSpec { + if in == nil { + return nil + } + out := new(TaskTemplateSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TemplateSpecification) DeepCopyInto(out *TemplateSpecification) { + *out = *in + if in.Tasks != nil { + in, out := &in.Tasks, &out.Tasks + *out = make(map[string][]TaskReference, len(*in)) + for key, val := range *in { + var outVal []TaskReference + if val == nil { + (*out)[key] = nil + } else { + inVal := (*in)[key] + in, out := &inVal, &outVal + *out = make([]TaskReference, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + (*out)[key] = outVal + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TemplateSpecification. +func (in *TemplateSpecification) DeepCopy() *TemplateSpecification { + if in == nil { + return nil + } + out := new(TemplateSpecification) + in.DeepCopyInto(out) + return out +} diff --git a/control-operator/cmd/environment-manager/main.go b/control-operator/cmd/environment-manager/main.go new file mode 100644 index 000000000..bdc4cc342 --- /dev/null +++ b/control-operator/cmd/environment-manager/main.go @@ -0,0 +1,107 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2026 CERN and copyright holders of ALICE O². + * Author: Michal Tichak + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package main + +import ( + "flag" + "os" + + "k8s.io/apimachinery/pkg/runtime" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" + clientgoscheme "k8s.io/client-go/kubernetes/scheme" + _ "k8s.io/client-go/plugin/pkg/client/auth" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/healthz" + "sigs.k8s.io/controller-runtime/pkg/log/zap" + "sigs.k8s.io/controller-runtime/pkg/metrics/server" + "sigs.k8s.io/controller-runtime/pkg/webhook" + + aliecsv1alpha1 "github.com/AliceO2Group/Control/control-operator/api/v1alpha1" + "github.com/AliceO2Group/Control/control-operator/internal/controller" +) + +var ( + scheme = runtime.NewScheme() + setupLog = ctrl.Log.WithName("setup") +) + +func init() { + utilruntime.Must(clientgoscheme.AddToScheme(scheme)) + utilruntime.Must(aliecsv1alpha1.AddToScheme(scheme)) +} + +func main() { + var metricsAddr string + var enableLeaderElection bool + var probeAddr string + flag.StringVar(&metricsAddr, "metrics-bind-address", ":9080", "The address the metric endpoint binds to.") + flag.StringVar(&probeAddr, "health-probe-bind-address", ":9081", "The address the probe endpoint binds to.") + flag.BoolVar(&enableLeaderElection, "leader-elect", false, + "Enable leader election for controller manager. "+ + "Enabling this will ensure there is only one active controller manager.") + opts := zap.Options{ + Development: true, + } + opts.BindFlags(flag.CommandLine) + flag.Parse() + + ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts))) + + mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{ + Scheme: scheme, + Metrics: server.Options{BindAddress: metricsAddr}, + HealthProbeBindAddress: probeAddr, + LeaderElection: enableLeaderElection, + LeaderElectionID: "03b91661-environment.alice.cern", + WebhookServer: webhook.NewServer(webhook.Options{Port: 9443}), + }) + if err != nil { + setupLog.Error(err, "unable to start manager") + os.Exit(1) + } + + if err = (&controller.EnvironmentReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "Environment") + os.Exit(1) + } + + if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil { + setupLog.Error(err, "unable to set up health check") + os.Exit(1) + } + if err := mgr.AddReadyzCheck("readyz", healthz.Ping); err != nil { + setupLog.Error(err, "unable to set up ready check") + os.Exit(1) + } + + setupLog.Info("starting environment manager") + if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil { + setupLog.Error(err, "problem running manager") + os.Exit(1) + } +} diff --git a/control-operator/cmd/task-manager/main.go b/control-operator/cmd/task-manager/main.go new file mode 100644 index 000000000..7701356a5 --- /dev/null +++ b/control-operator/cmd/task-manager/main.go @@ -0,0 +1,120 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2026 CERN and copyright holders of ALICE O². + * Author: Michal Tichak + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package main + +import ( + "flag" + "fmt" + "os" + + "k8s.io/apimachinery/pkg/runtime" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" + clientgoscheme "k8s.io/client-go/kubernetes/scheme" + _ "k8s.io/client-go/plugin/pkg/client/auth" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/healthz" + "sigs.k8s.io/controller-runtime/pkg/log/zap" + "sigs.k8s.io/controller-runtime/pkg/metrics/server" + "sigs.k8s.io/controller-runtime/pkg/webhook" + + aliecsv1alpha1 "github.com/AliceO2Group/Control/control-operator/api/v1alpha1" + "github.com/AliceO2Group/Control/control-operator/internal/controller" +) + +var ( + scheme = runtime.NewScheme() + setupLog = ctrl.Log.WithName("setup") +) + +func init() { + utilruntime.Must(clientgoscheme.AddToScheme(scheme)) + utilruntime.Must(aliecsv1alpha1.AddToScheme(scheme)) +} + +func main() { + var metricsAddr string + var enableLeaderElection bool + var probeAddr string + var maxConcurrentReconciles int + flag.StringVar(&metricsAddr, "metrics-bind-address", ":9082", "The address the metric endpoint binds to.") + flag.StringVar(&probeAddr, "health-probe-bind-address", ":9083", "The address the probe endpoint binds to.") + flag.BoolVar(&enableLeaderElection, "leader-elect", false, + "Enable leader election for controller manager. "+ + "Enabling this will ensure there is only one active controller manager.") + flag.IntVar(&maxConcurrentReconciles, "max-concurrent-reconciles", 1, + "Number of Tasks that are reconciled in parallel. Reconciles of a single Task are always serialized.") + opts := zap.Options{ + Development: true, + } + opts.BindFlags(flag.CommandLine) + flag.Parse() + + ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts))) + + mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{ + Scheme: scheme, + Metrics: server.Options{BindAddress: metricsAddr}, + HealthProbeBindAddress: probeAddr, + LeaderElection: enableLeaderElection, + LeaderElectionID: "03b91661-task.alice.cern", + WebhookServer: webhook.NewServer(webhook.Options{Port: 9443}), + }) + if err != nil { + setupLog.Error(err, "unable to start manager") + os.Exit(1) + } + + nodeName := os.Getenv("NODE_NAME") + if nodeName == "" { + setupLog.Error(fmt.Errorf("NODE_NAME environment variable not set"), "NODE_NAME is required") + os.Exit(1) + } + + if err = (&controller.TaskReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + Recorder: mgr.GetEventRecorderFor("task-controller"), + NodeName: nodeName, + MaxConcurrentReconciles: maxConcurrentReconciles, + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "Task") + os.Exit(1) + } + + if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil { + setupLog.Error(err, "unable to set up health check") + os.Exit(1) + } + if err := mgr.AddReadyzCheck("readyz", healthz.Ping); err != nil { + setupLog.Error(err, "unable to set up ready check") + os.Exit(1) + } + + setupLog.Info("starting task manager") + if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil { + setupLog.Error(err, "problem running manager") + os.Exit(1) + } +} diff --git a/control-operator/config/base/kustomization.yaml b/control-operator/config/base/kustomization.yaml new file mode 100644 index 000000000..863ffa069 --- /dev/null +++ b/control-operator/config/base/kustomization.yaml @@ -0,0 +1,7 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: +- namespace.yaml +- ../crd +- ../rbac diff --git a/control-operator/config/base/namespace.yaml b/control-operator/config/base/namespace.yaml new file mode 100644 index 000000000..bf2900319 --- /dev/null +++ b/control-operator/config/base/namespace.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Namespace +metadata: + labels: + control-plane: controller-manager + app.kubernetes.io/name: namespace + app.kubernetes.io/instance: system + app.kubernetes.io/component: manager + app.kubernetes.io/created-by: operator + app.kubernetes.io/part-of: operator + app.kubernetes.io/managed-by: kustomize + name: system diff --git a/control-operator/config/crd/bases/aliecs.alice.cern_environments.yaml b/control-operator/config/crd/bases/aliecs.alice.cern_environments.yaml new file mode 100644 index 000000000..e3184eb46 --- /dev/null +++ b/control-operator/config/crd/bases/aliecs.alice.cern_environments.yaml @@ -0,0 +1,4123 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.5 + name: environments.aliecs.alice.cern +spec: + group: aliecs.alice.cern + names: + kind: Environment + listKind: EnvironmentList + plural: environments + singular: environment + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.state + name: Desired + type: string + - jsonPath: .status.state + name: Actual + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + state: + enum: + - standby + - deployed + - configured + - running + type: string + tasks: + additionalProperties: + items: + properties: + name: + type: string + spec: + properties: + arguments: + additionalProperties: + type: string + type: object + bind: + items: + properties: + addressing: + default: tcp + enum: + - tcp + - ipc + type: string + global: + type: string + name: + type: string + rateLogging: + type: string + rcvBufSize: + type: integer + sndBufSize: + type: integer + target: + type: string + transport: + default: default + enum: + - default + - zeromq + - nanomsg + - shmem + type: string + type: + enum: + - push + - pull + - pub + - sub + type: string + required: + - global + - name + - target + - type + type: object + type: array + connect: + items: + properties: + name: + type: string + rateLogging: + type: string + rcvBufSize: + type: integer + sndBufSize: + type: integer + target: + type: string + transport: + default: default + enum: + - default + - zeromq + - nanomsg + - shmem + type: string + type: + enum: + - push + - pull + - pub + - sub + type: string + required: + - name + - target + - type + type: object + type: array + control: + properties: + mode: + enum: + - direct + - fairmq + - basic + - hook + type: string + port: + type: integer + type: object + envVars: + items: + type: string + type: array + pod: + properties: + activeDeadlineSeconds: + format: int64 + type: integer + affinity: + properties: + nodeAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + preference: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchFields: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + weight: + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + properties: + nodeSelectorTerms: + items: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchFields: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + type: array + x-kubernetes-list-type: atomic + required: + - nodeSelectorTerms + type: object + x-kubernetes-map-type: atomic + type: object + podAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + podAffinityTerm: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + podAntiAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + podAffinityTerm: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + type: object + automountServiceAccountToken: + type: boolean + containers: + items: + properties: + args: + items: + type: string + type: array + x-kubernetes-list-type: atomic + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + fileKeyRef: + properties: + key: + type: string + optional: + default: false + type: boolean + path: + type: string + volumeName: + type: string + required: + - key + - path + - volumeName + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + envFrom: + items: + properties: + configMapRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + type: string + secretRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + x-kubernetes-list-type: atomic + image: + type: string + imagePullPolicy: + type: string + lifecycle: + properties: + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + stopSignal: + type: string + type: object + livenessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + name: + type: string + ports: + items: + properties: + containerPort: + format: int32 + type: integer + hostIP: + type: string + hostPort: + format: int32 + type: integer + name: + type: string + protocol: + default: TCP + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + resizePolicy: + items: + properties: + resourceName: + type: string + restartPolicy: + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + restartPolicy: + type: string + restartPolicyRules: + items: + properties: + action: + type: string + exitCodes: + properties: + operator: + type: string + values: + items: + format: int32 + type: integer + type: array + x-kubernetes-list-type: set + required: + - operator + type: object + required: + - action + type: object + type: array + x-kubernetes-list-type: atomic + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + startupProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + stdin: + type: boolean + stdinOnce: + type: boolean + terminationMessagePath: + type: string + terminationMessagePolicy: + type: string + tty: + type: boolean + volumeDevices: + items: + properties: + devicePath: + type: string + name: + type: string + required: + - devicePath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - devicePath + x-kubernetes-list-type: map + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + recursiveReadOnly: + type: string + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + workingDir: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + dnsConfig: + properties: + nameservers: + items: + type: string + type: array + x-kubernetes-list-type: atomic + options: + items: + properties: + name: + type: string + value: + type: string + type: object + type: array + x-kubernetes-list-type: atomic + searches: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + dnsPolicy: + type: string + enableServiceLinks: + type: boolean + ephemeralContainers: + items: + properties: + args: + items: + type: string + type: array + x-kubernetes-list-type: atomic + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + fileKeyRef: + properties: + key: + type: string + optional: + default: false + type: boolean + path: + type: string + volumeName: + type: string + required: + - key + - path + - volumeName + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + envFrom: + items: + properties: + configMapRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + type: string + secretRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + x-kubernetes-list-type: atomic + image: + type: string + imagePullPolicy: + type: string + lifecycle: + properties: + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + stopSignal: + type: string + type: object + livenessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + name: + type: string + ports: + items: + properties: + containerPort: + format: int32 + type: integer + hostIP: + type: string + hostPort: + format: int32 + type: integer + name: + type: string + protocol: + default: TCP + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + resizePolicy: + items: + properties: + resourceName: + type: string + restartPolicy: + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + restartPolicy: + type: string + restartPolicyRules: + items: + properties: + action: + type: string + exitCodes: + properties: + operator: + type: string + values: + items: + format: int32 + type: integer + type: array + x-kubernetes-list-type: set + required: + - operator + type: object + required: + - action + type: object + type: array + x-kubernetes-list-type: atomic + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + startupProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + stdin: + type: boolean + stdinOnce: + type: boolean + targetContainerName: + type: string + terminationMessagePath: + type: string + terminationMessagePolicy: + type: string + tty: + type: boolean + volumeDevices: + items: + properties: + devicePath: + type: string + name: + type: string + required: + - devicePath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - devicePath + x-kubernetes-list-type: map + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + recursiveReadOnly: + type: string + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + workingDir: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + hostAliases: + items: + properties: + hostnames: + items: + type: string + type: array + x-kubernetes-list-type: atomic + ip: + type: string + required: + - ip + type: object + type: array + x-kubernetes-list-map-keys: + - ip + x-kubernetes-list-type: map + hostIPC: + type: boolean + hostNetwork: + type: boolean + hostPID: + type: boolean + hostUsers: + type: boolean + hostname: + type: string + hostnameOverride: + type: string + imagePullSecrets: + items: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + initContainers: + items: + properties: + args: + items: + type: string + type: array + x-kubernetes-list-type: atomic + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + fileKeyRef: + properties: + key: + type: string + optional: + default: false + type: boolean + path: + type: string + volumeName: + type: string + required: + - key + - path + - volumeName + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + envFrom: + items: + properties: + configMapRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + type: string + secretRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + x-kubernetes-list-type: atomic + image: + type: string + imagePullPolicy: + type: string + lifecycle: + properties: + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + stopSignal: + type: string + type: object + livenessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + name: + type: string + ports: + items: + properties: + containerPort: + format: int32 + type: integer + hostIP: + type: string + hostPort: + format: int32 + type: integer + name: + type: string + protocol: + default: TCP + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + resizePolicy: + items: + properties: + resourceName: + type: string + restartPolicy: + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + restartPolicy: + type: string + restartPolicyRules: + items: + properties: + action: + type: string + exitCodes: + properties: + operator: + type: string + values: + items: + format: int32 + type: integer + type: array + x-kubernetes-list-type: set + required: + - operator + type: object + required: + - action + type: object + type: array + x-kubernetes-list-type: atomic + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + startupProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + stdin: + type: boolean + stdinOnce: + type: boolean + terminationMessagePath: + type: string + terminationMessagePolicy: + type: string + tty: + type: boolean + volumeDevices: + items: + properties: + devicePath: + type: string + name: + type: string + required: + - devicePath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - devicePath + x-kubernetes-list-type: map + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + recursiveReadOnly: + type: string + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + workingDir: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + nodeName: + type: string + nodeSelector: + additionalProperties: + type: string + type: object + x-kubernetes-map-type: atomic + os: + properties: + name: + type: string + required: + - name + type: object + overhead: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + preemptionPolicy: + type: string + priority: + format: int32 + type: integer + priorityClassName: + type: string + readinessGates: + items: + properties: + conditionType: + type: string + required: + - conditionType + type: object + type: array + x-kubernetes-list-type: atomic + resourceClaims: + items: + properties: + name: + type: string + resourceClaimName: + type: string + resourceClaimTemplateName: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + restartPolicy: + type: string + runtimeClassName: + type: string + schedulerName: + type: string + schedulingGates: + items: + properties: + name: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + securityContext: + properties: + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + fsGroup: + format: int64 + type: integer + fsGroupChangePolicy: + type: string + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxChangePolicy: + type: string + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + supplementalGroups: + items: + format: int64 + type: integer + type: array + x-kubernetes-list-type: atomic + supplementalGroupsPolicy: + type: string + sysctls: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + serviceAccount: + type: string + serviceAccountName: + type: string + setHostnameAsFQDN: + type: boolean + shareProcessNamespace: + type: boolean + subdomain: + type: string + terminationGracePeriodSeconds: + format: int64 + type: integer + tolerations: + items: + properties: + effect: + type: string + key: + type: string + operator: + type: string + tolerationSeconds: + format: int64 + type: integer + value: + type: string + type: object + type: array + x-kubernetes-list-type: atomic + topologySpreadConstraints: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + maxSkew: + format: int32 + type: integer + minDomains: + format: int32 + type: integer + nodeAffinityPolicy: + type: string + nodeTaintsPolicy: + type: string + topologyKey: + type: string + whenUnsatisfiable: + type: string + required: + - maxSkew + - topologyKey + - whenUnsatisfiable + type: object + type: array + x-kubernetes-list-map-keys: + - topologyKey + - whenUnsatisfiable + x-kubernetes-list-type: map + volumes: + items: + properties: + awsElasticBlockStore: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + azureDisk: + properties: + cachingMode: + type: string + diskName: + type: string + diskURI: + type: string + fsType: + default: ext4 + type: string + kind: + type: string + readOnly: + default: false + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + properties: + readOnly: + type: boolean + secretName: + type: string + shareName: + type: string + required: + - secretName + - shareName + type: object + cephfs: + properties: + monitors: + items: + type: string + type: array + x-kubernetes-list-type: atomic + path: + type: string + readOnly: + type: boolean + secretFile: + type: string + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + user: + type: string + required: + - monitors + type: object + cinder: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + volumeID: + type: string + required: + - volumeID + type: object + configMap: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + csi: + properties: + driver: + type: string + fsType: + type: string + nodePublishSecretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + readOnly: + type: boolean + volumeAttributes: + additionalProperties: + type: string + type: object + required: + - driver + type: object + downwardAPI: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + x-kubernetes-list-type: atomic + type: object + emptyDir: + properties: + medium: + type: string + sizeLimit: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + ephemeral: + properties: + volumeClaimTemplate: + properties: + metadata: + type: object + spec: + properties: + accessModes: + items: + type: string + type: array + x-kubernetes-list-type: atomic + dataSource: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + required: + - kind + - name + type: object + x-kubernetes-map-type: atomic + dataSourceRef: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + required: + - kind + - name + type: object + resources: + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + storageClassName: + type: string + volumeAttributesClassName: + type: string + volumeMode: + type: string + volumeName: + type: string + type: object + required: + - spec + type: object + type: object + fc: + properties: + fsType: + type: string + lun: + format: int32 + type: integer + readOnly: + type: boolean + targetWWNs: + items: + type: string + type: array + x-kubernetes-list-type: atomic + wwids: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + flexVolume: + properties: + driver: + type: string + fsType: + type: string + options: + additionalProperties: + type: string + type: object + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + required: + - driver + type: object + flocker: + properties: + datasetName: + type: string + datasetUUID: + type: string + type: object + gcePersistentDisk: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + pdName: + type: string + readOnly: + type: boolean + required: + - pdName + type: object + gitRepo: + properties: + directory: + type: string + repository: + type: string + revision: + type: string + required: + - repository + type: object + glusterfs: + properties: + endpoints: + type: string + path: + type: string + readOnly: + type: boolean + required: + - endpoints + - path + type: object + hostPath: + properties: + path: + type: string + type: + type: string + required: + - path + type: object + image: + properties: + pullPolicy: + type: string + reference: + type: string + type: object + iscsi: + properties: + chapAuthDiscovery: + type: boolean + chapAuthSession: + type: boolean + fsType: + type: string + initiatorName: + type: string + iqn: + type: string + iscsiInterface: + default: default + type: string + lun: + format: int32 + type: integer + portals: + items: + type: string + type: array + x-kubernetes-list-type: atomic + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + targetPortal: + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + type: string + nfs: + properties: + path: + type: string + readOnly: + type: boolean + server: + type: string + required: + - path + - server + type: object + persistentVolumeClaim: + properties: + claimName: + type: string + readOnly: + type: boolean + required: + - claimName + type: object + photonPersistentDisk: + properties: + fsType: + type: string + pdID: + type: string + required: + - pdID + type: object + portworxVolume: + properties: + fsType: + type: string + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + projected: + properties: + defaultMode: + format: int32 + type: integer + sources: + items: + properties: + clusterTrustBundle: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + name: + type: string + optional: + type: boolean + path: + type: string + signerName: + type: string + required: + - path + type: object + configMap: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + downwardAPI: + properties: + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + x-kubernetes-list-type: atomic + type: object + podCertificate: + properties: + certificateChainPath: + type: string + credentialBundlePath: + type: string + keyPath: + type: string + keyType: + type: string + maxExpirationSeconds: + format: int32 + type: integer + signerName: + type: string + userAnnotations: + additionalProperties: + type: string + type: object + required: + - keyType + - signerName + type: object + secret: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + serviceAccountToken: + properties: + audience: + type: string + expirationSeconds: + format: int64 + type: integer + path: + type: string + required: + - path + type: object + type: object + type: array + x-kubernetes-list-type: atomic + type: object + quobyte: + properties: + group: + type: string + readOnly: + type: boolean + registry: + type: string + tenant: + type: string + user: + type: string + volume: + type: string + required: + - registry + - volume + type: object + rbd: + properties: + fsType: + type: string + image: + type: string + keyring: + default: /etc/ceph/keyring + type: string + monitors: + items: + type: string + type: array + x-kubernetes-list-type: atomic + pool: + default: rbd + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + user: + default: admin + type: string + required: + - image + - monitors + type: object + scaleIO: + properties: + fsType: + default: xfs + type: string + gateway: + type: string + protectionDomain: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + sslEnabled: + type: boolean + storageMode: + default: ThinProvisioned + type: string + storagePool: + type: string + system: + type: string + volumeName: + type: string + required: + - gateway + - secretRef + - system + type: object + secret: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + optional: + type: boolean + secretName: + type: string + type: object + storageos: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + volumeName: + type: string + volumeNamespace: + type: string + type: object + vsphereVolume: + properties: + fsType: + type: string + storagePolicyID: + type: string + storagePolicyName: + type: string + volumePath: + type: string + required: + - volumePath + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + workloadRef: + properties: + name: + type: string + podGroup: + type: string + podGroupReplicaKey: + type: string + required: + - name + - podGroup + type: object + required: + - containers + type: object + properties: + additionalProperties: + type: string + type: object + type: object + required: + - name + - spec + type: object + type: array + type: object + required: + - state + - tasks + type: object + status: + properties: + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + state: + type: string + tasks: + additionalProperties: + additionalProperties: + type: string + type: object + type: object + required: + - tasks + type: object + taskTemplates: + properties: + tasks: + additionalProperties: + items: + properties: + argsCLI: + items: + type: string + type: array + argsTransition: + additionalProperties: + type: string + type: object + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + fileKeyRef: + properties: + key: + type: string + optional: + default: false + type: boolean + path: + type: string + volumeName: + type: string + required: + - key + - path + - volumeName + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + name: + type: string + nameSuffix: + type: string + taskID: + type: string + required: + - argsCLI + - env + - name + type: object + type: array + type: object + required: + - tasks + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} diff --git a/control-operator/config/crd/bases/aliecs.alice.cern_tasks.yaml b/control-operator/config/crd/bases/aliecs.alice.cern_tasks.yaml new file mode 100644 index 000000000..415cdee8a --- /dev/null +++ b/control-operator/config/crd/bases/aliecs.alice.cern_tasks.yaml @@ -0,0 +1,4778 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.5 + name: tasks.aliecs.alice.cern +spec: + group: aliecs.alice.cern + names: + kind: Task + listKind: TaskList + plural: tasks + singular: task + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.state + name: Desired + type: string + - jsonPath: .status.state + name: Actual + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + arguments: + additionalProperties: + type: string + type: object + bind: + items: + properties: + addressing: + default: tcp + enum: + - tcp + - ipc + type: string + global: + type: string + name: + type: string + rateLogging: + type: string + rcvBufSize: + type: integer + sndBufSize: + type: integer + target: + type: string + transport: + default: default + enum: + - default + - zeromq + - nanomsg + - shmem + type: string + type: + enum: + - push + - pull + - pub + - sub + type: string + required: + - global + - name + - target + - type + type: object + type: array + connect: + items: + properties: + name: + type: string + rateLogging: + type: string + rcvBufSize: + type: integer + sndBufSize: + type: integer + target: + type: string + transport: + default: default + enum: + - default + - zeromq + - nanomsg + - shmem + type: string + type: + enum: + - push + - pull + - pub + - sub + type: string + required: + - name + - target + - type + type: object + type: array + control: + properties: + mode: + enum: + - direct + - fairmq + - basic + - hook + type: string + port: + type: integer + type: object + nodeName: + type: string + pod: + properties: + activeDeadlineSeconds: + format: int64 + type: integer + affinity: + properties: + nodeAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + preference: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchFields: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + weight: + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + properties: + nodeSelectorTerms: + items: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchFields: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + type: array + x-kubernetes-list-type: atomic + required: + - nodeSelectorTerms + type: object + x-kubernetes-map-type: atomic + type: object + podAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + podAffinityTerm: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + podAntiAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + podAffinityTerm: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + type: object + automountServiceAccountToken: + type: boolean + containers: + items: + properties: + args: + items: + type: string + type: array + x-kubernetes-list-type: atomic + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + fileKeyRef: + properties: + key: + type: string + optional: + default: false + type: boolean + path: + type: string + volumeName: + type: string + required: + - key + - path + - volumeName + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + envFrom: + items: + properties: + configMapRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + type: string + secretRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + x-kubernetes-list-type: atomic + image: + type: string + imagePullPolicy: + type: string + lifecycle: + properties: + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + stopSignal: + type: string + type: object + livenessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + name: + type: string + ports: + items: + properties: + containerPort: + format: int32 + type: integer + hostIP: + type: string + hostPort: + format: int32 + type: integer + name: + type: string + protocol: + default: TCP + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + resizePolicy: + items: + properties: + resourceName: + type: string + restartPolicy: + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + restartPolicy: + type: string + restartPolicyRules: + items: + properties: + action: + type: string + exitCodes: + properties: + operator: + type: string + values: + items: + format: int32 + type: integer + type: array + x-kubernetes-list-type: set + required: + - operator + type: object + required: + - action + type: object + type: array + x-kubernetes-list-type: atomic + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + startupProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + stdin: + type: boolean + stdinOnce: + type: boolean + terminationMessagePath: + type: string + terminationMessagePolicy: + type: string + tty: + type: boolean + volumeDevices: + items: + properties: + devicePath: + type: string + name: + type: string + required: + - devicePath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - devicePath + x-kubernetes-list-type: map + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + recursiveReadOnly: + type: string + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + workingDir: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + dnsConfig: + properties: + nameservers: + items: + type: string + type: array + x-kubernetes-list-type: atomic + options: + items: + properties: + name: + type: string + value: + type: string + type: object + type: array + x-kubernetes-list-type: atomic + searches: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + dnsPolicy: + type: string + enableServiceLinks: + type: boolean + ephemeralContainers: + items: + properties: + args: + items: + type: string + type: array + x-kubernetes-list-type: atomic + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + fileKeyRef: + properties: + key: + type: string + optional: + default: false + type: boolean + path: + type: string + volumeName: + type: string + required: + - key + - path + - volumeName + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + envFrom: + items: + properties: + configMapRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + type: string + secretRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + x-kubernetes-list-type: atomic + image: + type: string + imagePullPolicy: + type: string + lifecycle: + properties: + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + stopSignal: + type: string + type: object + livenessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + name: + type: string + ports: + items: + properties: + containerPort: + format: int32 + type: integer + hostIP: + type: string + hostPort: + format: int32 + type: integer + name: + type: string + protocol: + default: TCP + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + resizePolicy: + items: + properties: + resourceName: + type: string + restartPolicy: + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + restartPolicy: + type: string + restartPolicyRules: + items: + properties: + action: + type: string + exitCodes: + properties: + operator: + type: string + values: + items: + format: int32 + type: integer + type: array + x-kubernetes-list-type: set + required: + - operator + type: object + required: + - action + type: object + type: array + x-kubernetes-list-type: atomic + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + startupProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + stdin: + type: boolean + stdinOnce: + type: boolean + targetContainerName: + type: string + terminationMessagePath: + type: string + terminationMessagePolicy: + type: string + tty: + type: boolean + volumeDevices: + items: + properties: + devicePath: + type: string + name: + type: string + required: + - devicePath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - devicePath + x-kubernetes-list-type: map + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + recursiveReadOnly: + type: string + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + workingDir: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + hostAliases: + items: + properties: + hostnames: + items: + type: string + type: array + x-kubernetes-list-type: atomic + ip: + type: string + required: + - ip + type: object + type: array + x-kubernetes-list-map-keys: + - ip + x-kubernetes-list-type: map + hostIPC: + type: boolean + hostNetwork: + type: boolean + hostPID: + type: boolean + hostUsers: + type: boolean + hostname: + type: string + hostnameOverride: + type: string + imagePullSecrets: + items: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + initContainers: + items: + properties: + args: + items: + type: string + type: array + x-kubernetes-list-type: atomic + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + fileKeyRef: + properties: + key: + type: string + optional: + default: false + type: boolean + path: + type: string + volumeName: + type: string + required: + - key + - path + - volumeName + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + envFrom: + items: + properties: + configMapRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + type: string + secretRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + x-kubernetes-list-type: atomic + image: + type: string + imagePullPolicy: + type: string + lifecycle: + properties: + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + stopSignal: + type: string + type: object + livenessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + name: + type: string + ports: + items: + properties: + containerPort: + format: int32 + type: integer + hostIP: + type: string + hostPort: + format: int32 + type: integer + name: + type: string + protocol: + default: TCP + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + resizePolicy: + items: + properties: + resourceName: + type: string + restartPolicy: + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + restartPolicy: + type: string + restartPolicyRules: + items: + properties: + action: + type: string + exitCodes: + properties: + operator: + type: string + values: + items: + format: int32 + type: integer + type: array + x-kubernetes-list-type: set + required: + - operator + type: object + required: + - action + type: object + type: array + x-kubernetes-list-type: atomic + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + startupProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + stdin: + type: boolean + stdinOnce: + type: boolean + terminationMessagePath: + type: string + terminationMessagePolicy: + type: string + tty: + type: boolean + volumeDevices: + items: + properties: + devicePath: + type: string + name: + type: string + required: + - devicePath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - devicePath + x-kubernetes-list-type: map + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + recursiveReadOnly: + type: string + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + workingDir: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + nodeName: + type: string + nodeSelector: + additionalProperties: + type: string + type: object + x-kubernetes-map-type: atomic + os: + properties: + name: + type: string + required: + - name + type: object + overhead: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + preemptionPolicy: + type: string + priority: + format: int32 + type: integer + priorityClassName: + type: string + readinessGates: + items: + properties: + conditionType: + type: string + required: + - conditionType + type: object + type: array + x-kubernetes-list-type: atomic + resourceClaims: + items: + properties: + name: + type: string + resourceClaimName: + type: string + resourceClaimTemplateName: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + restartPolicy: + type: string + runtimeClassName: + type: string + schedulerName: + type: string + schedulingGates: + items: + properties: + name: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + securityContext: + properties: + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + fsGroup: + format: int64 + type: integer + fsGroupChangePolicy: + type: string + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxChangePolicy: + type: string + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + supplementalGroups: + items: + format: int64 + type: integer + type: array + x-kubernetes-list-type: atomic + supplementalGroupsPolicy: + type: string + sysctls: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + serviceAccount: + type: string + serviceAccountName: + type: string + setHostnameAsFQDN: + type: boolean + shareProcessNamespace: + type: boolean + subdomain: + type: string + terminationGracePeriodSeconds: + format: int64 + type: integer + tolerations: + items: + properties: + effect: + type: string + key: + type: string + operator: + type: string + tolerationSeconds: + format: int64 + type: integer + value: + type: string + type: object + type: array + x-kubernetes-list-type: atomic + topologySpreadConstraints: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + maxSkew: + format: int32 + type: integer + minDomains: + format: int32 + type: integer + nodeAffinityPolicy: + type: string + nodeTaintsPolicy: + type: string + topologyKey: + type: string + whenUnsatisfiable: + type: string + required: + - maxSkew + - topologyKey + - whenUnsatisfiable + type: object + type: array + x-kubernetes-list-map-keys: + - topologyKey + - whenUnsatisfiable + x-kubernetes-list-type: map + volumes: + items: + properties: + awsElasticBlockStore: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + azureDisk: + properties: + cachingMode: + type: string + diskName: + type: string + diskURI: + type: string + fsType: + default: ext4 + type: string + kind: + type: string + readOnly: + default: false + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + properties: + readOnly: + type: boolean + secretName: + type: string + shareName: + type: string + required: + - secretName + - shareName + type: object + cephfs: + properties: + monitors: + items: + type: string + type: array + x-kubernetes-list-type: atomic + path: + type: string + readOnly: + type: boolean + secretFile: + type: string + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + user: + type: string + required: + - monitors + type: object + cinder: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + volumeID: + type: string + required: + - volumeID + type: object + configMap: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + csi: + properties: + driver: + type: string + fsType: + type: string + nodePublishSecretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + readOnly: + type: boolean + volumeAttributes: + additionalProperties: + type: string + type: object + required: + - driver + type: object + downwardAPI: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + x-kubernetes-list-type: atomic + type: object + emptyDir: + properties: + medium: + type: string + sizeLimit: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + ephemeral: + properties: + volumeClaimTemplate: + properties: + metadata: + type: object + spec: + properties: + accessModes: + items: + type: string + type: array + x-kubernetes-list-type: atomic + dataSource: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + required: + - kind + - name + type: object + x-kubernetes-map-type: atomic + dataSourceRef: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + required: + - kind + - name + type: object + resources: + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + storageClassName: + type: string + volumeAttributesClassName: + type: string + volumeMode: + type: string + volumeName: + type: string + type: object + required: + - spec + type: object + type: object + fc: + properties: + fsType: + type: string + lun: + format: int32 + type: integer + readOnly: + type: boolean + targetWWNs: + items: + type: string + type: array + x-kubernetes-list-type: atomic + wwids: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + flexVolume: + properties: + driver: + type: string + fsType: + type: string + options: + additionalProperties: + type: string + type: object + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + required: + - driver + type: object + flocker: + properties: + datasetName: + type: string + datasetUUID: + type: string + type: object + gcePersistentDisk: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + pdName: + type: string + readOnly: + type: boolean + required: + - pdName + type: object + gitRepo: + properties: + directory: + type: string + repository: + type: string + revision: + type: string + required: + - repository + type: object + glusterfs: + properties: + endpoints: + type: string + path: + type: string + readOnly: + type: boolean + required: + - endpoints + - path + type: object + hostPath: + properties: + path: + type: string + type: + type: string + required: + - path + type: object + image: + properties: + pullPolicy: + type: string + reference: + type: string + type: object + iscsi: + properties: + chapAuthDiscovery: + type: boolean + chapAuthSession: + type: boolean + fsType: + type: string + initiatorName: + type: string + iqn: + type: string + iscsiInterface: + default: default + type: string + lun: + format: int32 + type: integer + portals: + items: + type: string + type: array + x-kubernetes-list-type: atomic + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + targetPortal: + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + type: string + nfs: + properties: + path: + type: string + readOnly: + type: boolean + server: + type: string + required: + - path + - server + type: object + persistentVolumeClaim: + properties: + claimName: + type: string + readOnly: + type: boolean + required: + - claimName + type: object + photonPersistentDisk: + properties: + fsType: + type: string + pdID: + type: string + required: + - pdID + type: object + portworxVolume: + properties: + fsType: + type: string + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + projected: + properties: + defaultMode: + format: int32 + type: integer + sources: + items: + properties: + clusterTrustBundle: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + name: + type: string + optional: + type: boolean + path: + type: string + signerName: + type: string + required: + - path + type: object + configMap: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + downwardAPI: + properties: + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + x-kubernetes-list-type: atomic + type: object + podCertificate: + properties: + certificateChainPath: + type: string + credentialBundlePath: + type: string + keyPath: + type: string + keyType: + type: string + maxExpirationSeconds: + format: int32 + type: integer + signerName: + type: string + userAnnotations: + additionalProperties: + type: string + type: object + required: + - keyType + - signerName + type: object + secret: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + serviceAccountToken: + properties: + audience: + type: string + expirationSeconds: + format: int64 + type: integer + path: + type: string + required: + - path + type: object + type: object + type: array + x-kubernetes-list-type: atomic + type: object + quobyte: + properties: + group: + type: string + readOnly: + type: boolean + registry: + type: string + tenant: + type: string + user: + type: string + volume: + type: string + required: + - registry + - volume + type: object + rbd: + properties: + fsType: + type: string + image: + type: string + keyring: + default: /etc/ceph/keyring + type: string + monitors: + items: + type: string + type: array + x-kubernetes-list-type: atomic + pool: + default: rbd + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + user: + default: admin + type: string + required: + - image + - monitors + type: object + scaleIO: + properties: + fsType: + default: xfs + type: string + gateway: + type: string + protectionDomain: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + sslEnabled: + type: boolean + storageMode: + default: ThinProvisioned + type: string + storagePool: + type: string + system: + type: string + volumeName: + type: string + required: + - gateway + - secretRef + - system + type: object + secret: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + optional: + type: boolean + secretName: + type: string + type: object + storageos: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + volumeName: + type: string + volumeNamespace: + type: string + type: object + vsphereVolume: + properties: + fsType: + type: string + storagePolicyID: + type: string + storagePolicyName: + type: string + volumePath: + type: string + required: + - volumePath + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + workloadRef: + properties: + name: + type: string + podGroup: + type: string + podGroupReplicaKey: + type: string + required: + - name + - podGroup + type: object + required: + - containers + type: object + properties: + additionalProperties: + type: string + type: object + state: + enum: + - standby + - deployed + - configured + - running + type: string + type: object + status: + properties: + conditions: + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + maxLength: 32768 + type: string + observedGeneration: + format: int64 + minimum: 0 + type: integer + reason: + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + enum: + - "True" + - "False" + - Unknown + type: string + type: + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + error: + type: string + pod: + properties: + allocatedResources: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + conditions: + items: + properties: + lastProbeTime: + format: date-time + type: string + lastTransitionTime: + format: date-time + type: string + message: + type: string + observedGeneration: + format: int64 + type: integer + reason: + type: string + status: + type: string + type: + type: string + required: + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + containerStatuses: + items: + properties: + allocatedResources: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + allocatedResourcesStatus: + items: + properties: + name: + type: string + resources: + items: + properties: + health: + type: string + resourceID: + type: string + required: + - resourceID + type: object + type: array + x-kubernetes-list-map-keys: + - resourceID + x-kubernetes-list-type: map + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + containerID: + type: string + image: + type: string + imageID: + type: string + lastState: + properties: + running: + properties: + startedAt: + format: date-time + type: string + type: object + terminated: + properties: + containerID: + type: string + exitCode: + format: int32 + type: integer + finishedAt: + format: date-time + type: string + message: + type: string + reason: + type: string + signal: + format: int32 + type: integer + startedAt: + format: date-time + type: string + required: + - exitCode + type: object + waiting: + properties: + message: + type: string + reason: + type: string + type: object + type: object + name: + type: string + ready: + type: boolean + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + restartCount: + format: int32 + type: integer + started: + type: boolean + state: + properties: + running: + properties: + startedAt: + format: date-time + type: string + type: object + terminated: + properties: + containerID: + type: string + exitCode: + format: int32 + type: integer + finishedAt: + format: date-time + type: string + message: + type: string + reason: + type: string + signal: + format: int32 + type: integer + startedAt: + format: date-time + type: string + required: + - exitCode + type: object + waiting: + properties: + message: + type: string + reason: + type: string + type: object + type: object + stopSignal: + type: string + user: + properties: + linux: + properties: + gid: + format: int64 + type: integer + supplementalGroups: + items: + format: int64 + type: integer + type: array + x-kubernetes-list-type: atomic + uid: + format: int64 + type: integer + required: + - gid + - uid + type: object + type: object + volumeMounts: + items: + properties: + mountPath: + type: string + name: + type: string + readOnly: + type: boolean + recursiveReadOnly: + type: string + required: + - mountPath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + required: + - image + - imageID + - name + - ready + - restartCount + type: object + type: array + x-kubernetes-list-type: atomic + ephemeralContainerStatuses: + items: + properties: + allocatedResources: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + allocatedResourcesStatus: + items: + properties: + name: + type: string + resources: + items: + properties: + health: + type: string + resourceID: + type: string + required: + - resourceID + type: object + type: array + x-kubernetes-list-map-keys: + - resourceID + x-kubernetes-list-type: map + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + containerID: + type: string + image: + type: string + imageID: + type: string + lastState: + properties: + running: + properties: + startedAt: + format: date-time + type: string + type: object + terminated: + properties: + containerID: + type: string + exitCode: + format: int32 + type: integer + finishedAt: + format: date-time + type: string + message: + type: string + reason: + type: string + signal: + format: int32 + type: integer + startedAt: + format: date-time + type: string + required: + - exitCode + type: object + waiting: + properties: + message: + type: string + reason: + type: string + type: object + type: object + name: + type: string + ready: + type: boolean + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + restartCount: + format: int32 + type: integer + started: + type: boolean + state: + properties: + running: + properties: + startedAt: + format: date-time + type: string + type: object + terminated: + properties: + containerID: + type: string + exitCode: + format: int32 + type: integer + finishedAt: + format: date-time + type: string + message: + type: string + reason: + type: string + signal: + format: int32 + type: integer + startedAt: + format: date-time + type: string + required: + - exitCode + type: object + waiting: + properties: + message: + type: string + reason: + type: string + type: object + type: object + stopSignal: + type: string + user: + properties: + linux: + properties: + gid: + format: int64 + type: integer + supplementalGroups: + items: + format: int64 + type: integer + type: array + x-kubernetes-list-type: atomic + uid: + format: int64 + type: integer + required: + - gid + - uid + type: object + type: object + volumeMounts: + items: + properties: + mountPath: + type: string + name: + type: string + readOnly: + type: boolean + recursiveReadOnly: + type: string + required: + - mountPath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + required: + - image + - imageID + - name + - ready + - restartCount + type: object + type: array + x-kubernetes-list-type: atomic + extendedResourceClaimStatus: + properties: + requestMappings: + items: + properties: + containerName: + type: string + requestName: + type: string + resourceName: + type: string + required: + - containerName + - requestName + - resourceName + type: object + type: array + x-kubernetes-list-type: atomic + resourceClaimName: + type: string + required: + - requestMappings + - resourceClaimName + type: object + hostIP: + type: string + hostIPs: + items: + properties: + ip: + type: string + required: + - ip + type: object + type: array + x-kubernetes-list-type: atomic + initContainerStatuses: + items: + properties: + allocatedResources: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + allocatedResourcesStatus: + items: + properties: + name: + type: string + resources: + items: + properties: + health: + type: string + resourceID: + type: string + required: + - resourceID + type: object + type: array + x-kubernetes-list-map-keys: + - resourceID + x-kubernetes-list-type: map + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + containerID: + type: string + image: + type: string + imageID: + type: string + lastState: + properties: + running: + properties: + startedAt: + format: date-time + type: string + type: object + terminated: + properties: + containerID: + type: string + exitCode: + format: int32 + type: integer + finishedAt: + format: date-time + type: string + message: + type: string + reason: + type: string + signal: + format: int32 + type: integer + startedAt: + format: date-time + type: string + required: + - exitCode + type: object + waiting: + properties: + message: + type: string + reason: + type: string + type: object + type: object + name: + type: string + ready: + type: boolean + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + restartCount: + format: int32 + type: integer + started: + type: boolean + state: + properties: + running: + properties: + startedAt: + format: date-time + type: string + type: object + terminated: + properties: + containerID: + type: string + exitCode: + format: int32 + type: integer + finishedAt: + format: date-time + type: string + message: + type: string + reason: + type: string + signal: + format: int32 + type: integer + startedAt: + format: date-time + type: string + required: + - exitCode + type: object + waiting: + properties: + message: + type: string + reason: + type: string + type: object + type: object + stopSignal: + type: string + user: + properties: + linux: + properties: + gid: + format: int64 + type: integer + supplementalGroups: + items: + format: int64 + type: integer + type: array + x-kubernetes-list-type: atomic + uid: + format: int64 + type: integer + required: + - gid + - uid + type: object + type: object + volumeMounts: + items: + properties: + mountPath: + type: string + name: + type: string + readOnly: + type: boolean + recursiveReadOnly: + type: string + required: + - mountPath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + required: + - image + - imageID + - name + - ready + - restartCount + type: object + type: array + x-kubernetes-list-type: atomic + message: + type: string + nominatedNodeName: + type: string + observedGeneration: + format: int64 + type: integer + phase: + type: string + podIP: + type: string + podIPs: + items: + properties: + ip: + type: string + required: + - ip + type: object + type: array + x-kubernetes-list-map-keys: + - ip + x-kubernetes-list-type: map + qosClass: + type: string + reason: + type: string + resize: + type: string + resourceClaimStatuses: + items: + properties: + name: + type: string + resourceClaimName: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + startTime: + format: date-time + type: string + type: object + state: + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/control-operator/config/crd/bases/aliecs.alice.cern_tasktemplates.yaml b/control-operator/config/crd/bases/aliecs.alice.cern_tasktemplates.yaml new file mode 100644 index 000000000..a45b860e2 --- /dev/null +++ b/control-operator/config/crd/bases/aliecs.alice.cern_tasktemplates.yaml @@ -0,0 +1,3921 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.5 + name: tasktemplates.aliecs.alice.cern +spec: + group: aliecs.alice.cern + names: + kind: TaskTemplate + listKind: TaskTemplateList + plural: tasktemplates + singular: tasktemplate + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + arguments: + additionalProperties: + type: string + type: object + bind: + items: + properties: + addressing: + default: tcp + enum: + - tcp + - ipc + type: string + global: + type: string + name: + type: string + rateLogging: + type: string + rcvBufSize: + type: integer + sndBufSize: + type: integer + target: + type: string + transport: + default: default + enum: + - default + - zeromq + - nanomsg + - shmem + type: string + type: + enum: + - push + - pull + - pub + - sub + type: string + required: + - global + - name + - target + - type + type: object + type: array + connect: + items: + properties: + name: + type: string + rateLogging: + type: string + rcvBufSize: + type: integer + sndBufSize: + type: integer + target: + type: string + transport: + default: default + enum: + - default + - zeromq + - nanomsg + - shmem + type: string + type: + enum: + - push + - pull + - pub + - sub + type: string + required: + - name + - target + - type + type: object + type: array + control: + properties: + mode: + enum: + - direct + - fairmq + - basic + - hook + type: string + port: + type: integer + type: object + envVars: + items: + type: string + type: array + pod: + properties: + activeDeadlineSeconds: + format: int64 + type: integer + affinity: + properties: + nodeAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + preference: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchFields: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + weight: + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + properties: + nodeSelectorTerms: + items: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchFields: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + type: object + x-kubernetes-map-type: atomic + type: array + x-kubernetes-list-type: atomic + required: + - nodeSelectorTerms + type: object + x-kubernetes-map-type: atomic + type: object + podAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + podAffinityTerm: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + podAntiAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + podAffinityTerm: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + type: object + automountServiceAccountToken: + type: boolean + containers: + items: + properties: + args: + items: + type: string + type: array + x-kubernetes-list-type: atomic + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + fileKeyRef: + properties: + key: + type: string + optional: + default: false + type: boolean + path: + type: string + volumeName: + type: string + required: + - key + - path + - volumeName + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + envFrom: + items: + properties: + configMapRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + type: string + secretRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + x-kubernetes-list-type: atomic + image: + type: string + imagePullPolicy: + type: string + lifecycle: + properties: + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + stopSignal: + type: string + type: object + livenessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + name: + type: string + ports: + items: + properties: + containerPort: + format: int32 + type: integer + hostIP: + type: string + hostPort: + format: int32 + type: integer + name: + type: string + protocol: + default: TCP + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + resizePolicy: + items: + properties: + resourceName: + type: string + restartPolicy: + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + restartPolicy: + type: string + restartPolicyRules: + items: + properties: + action: + type: string + exitCodes: + properties: + operator: + type: string + values: + items: + format: int32 + type: integer + type: array + x-kubernetes-list-type: set + required: + - operator + type: object + required: + - action + type: object + type: array + x-kubernetes-list-type: atomic + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + startupProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + stdin: + type: boolean + stdinOnce: + type: boolean + terminationMessagePath: + type: string + terminationMessagePolicy: + type: string + tty: + type: boolean + volumeDevices: + items: + properties: + devicePath: + type: string + name: + type: string + required: + - devicePath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - devicePath + x-kubernetes-list-type: map + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + recursiveReadOnly: + type: string + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + workingDir: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + dnsConfig: + properties: + nameservers: + items: + type: string + type: array + x-kubernetes-list-type: atomic + options: + items: + properties: + name: + type: string + value: + type: string + type: object + type: array + x-kubernetes-list-type: atomic + searches: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + dnsPolicy: + type: string + enableServiceLinks: + type: boolean + ephemeralContainers: + items: + properties: + args: + items: + type: string + type: array + x-kubernetes-list-type: atomic + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + fileKeyRef: + properties: + key: + type: string + optional: + default: false + type: boolean + path: + type: string + volumeName: + type: string + required: + - key + - path + - volumeName + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + envFrom: + items: + properties: + configMapRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + type: string + secretRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + x-kubernetes-list-type: atomic + image: + type: string + imagePullPolicy: + type: string + lifecycle: + properties: + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + stopSignal: + type: string + type: object + livenessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + name: + type: string + ports: + items: + properties: + containerPort: + format: int32 + type: integer + hostIP: + type: string + hostPort: + format: int32 + type: integer + name: + type: string + protocol: + default: TCP + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + resizePolicy: + items: + properties: + resourceName: + type: string + restartPolicy: + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + restartPolicy: + type: string + restartPolicyRules: + items: + properties: + action: + type: string + exitCodes: + properties: + operator: + type: string + values: + items: + format: int32 + type: integer + type: array + x-kubernetes-list-type: set + required: + - operator + type: object + required: + - action + type: object + type: array + x-kubernetes-list-type: atomic + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + startupProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + stdin: + type: boolean + stdinOnce: + type: boolean + targetContainerName: + type: string + terminationMessagePath: + type: string + terminationMessagePolicy: + type: string + tty: + type: boolean + volumeDevices: + items: + properties: + devicePath: + type: string + name: + type: string + required: + - devicePath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - devicePath + x-kubernetes-list-type: map + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + recursiveReadOnly: + type: string + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + workingDir: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + hostAliases: + items: + properties: + hostnames: + items: + type: string + type: array + x-kubernetes-list-type: atomic + ip: + type: string + required: + - ip + type: object + type: array + x-kubernetes-list-map-keys: + - ip + x-kubernetes-list-type: map + hostIPC: + type: boolean + hostNetwork: + type: boolean + hostPID: + type: boolean + hostUsers: + type: boolean + hostname: + type: string + hostnameOverride: + type: string + imagePullSecrets: + items: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + initContainers: + items: + properties: + args: + items: + type: string + type: array + x-kubernetes-list-type: atomic + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + fileKeyRef: + properties: + key: + type: string + optional: + default: false + type: boolean + path: + type: string + volumeName: + type: string + required: + - key + - path + - volumeName + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + envFrom: + items: + properties: + configMapRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + type: string + secretRef: + properties: + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + x-kubernetes-list-type: atomic + image: + type: string + imagePullPolicy: + type: string + lifecycle: + properties: + postStart: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + sleep: + properties: + seconds: + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + stopSignal: + type: string + type: object + livenessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + name: + type: string + ports: + items: + properties: + containerPort: + format: int32 + type: integer + hostIP: + type: string + hostPort: + format: int32 + type: integer + name: + type: string + protocol: + default: TCP + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + resizePolicy: + items: + properties: + resourceName: + type: string + restartPolicy: + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + restartPolicy: + type: string + restartPolicyRules: + items: + properties: + action: + type: string + exitCodes: + properties: + operator: + type: string + values: + items: + format: int32 + type: integer + type: array + x-kubernetes-list-type: set + required: + - operator + type: object + required: + - action + type: object + type: array + x-kubernetes-list-type: atomic + securityContext: + properties: + allowPrivilegeEscalation: + type: boolean + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + capabilities: + properties: + add: + items: + type: string + type: array + x-kubernetes-list-type: atomic + drop: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + type: boolean + procMount: + type: string + readOnlyRootFilesystem: + type: boolean + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + startupProbe: + properties: + exec: + properties: + command: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + format: int32 + type: integer + grpc: + properties: + port: + format: int32 + type: integer + service: + default: "" + type: string + required: + - port + type: object + httpGet: + properties: + host: + type: string + httpHeaders: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + type: string + required: + - port + type: object + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + tcpSocket: + properties: + host: + type: string + port: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + format: int64 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + stdin: + type: boolean + stdinOnce: + type: boolean + terminationMessagePath: + type: string + terminationMessagePolicy: + type: string + tty: + type: boolean + volumeDevices: + items: + properties: + devicePath: + type: string + name: + type: string + required: + - devicePath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - devicePath + x-kubernetes-list-type: map + volumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + recursiveReadOnly: + type: string + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + workingDir: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + nodeName: + type: string + nodeSelector: + additionalProperties: + type: string + type: object + x-kubernetes-map-type: atomic + os: + properties: + name: + type: string + required: + - name + type: object + overhead: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + preemptionPolicy: + type: string + priority: + format: int32 + type: integer + priorityClassName: + type: string + readinessGates: + items: + properties: + conditionType: + type: string + required: + - conditionType + type: object + type: array + x-kubernetes-list-type: atomic + resourceClaims: + items: + properties: + name: + type: string + resourceClaimName: + type: string + resourceClaimTemplateName: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + resources: + properties: + claims: + items: + properties: + name: + type: string + request: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + restartPolicy: + type: string + runtimeClassName: + type: string + schedulerName: + type: string + schedulingGates: + items: + properties: + name: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + securityContext: + properties: + appArmorProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + fsGroup: + format: int64 + type: integer + fsGroupChangePolicy: + type: string + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxChangePolicy: + type: string + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + type: object + supplementalGroups: + items: + format: int64 + type: integer + type: array + x-kubernetes-list-type: atomic + supplementalGroupsPolicy: + type: string + sysctls: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + serviceAccount: + type: string + serviceAccountName: + type: string + setHostnameAsFQDN: + type: boolean + shareProcessNamespace: + type: boolean + subdomain: + type: string + terminationGracePeriodSeconds: + format: int64 + type: integer + tolerations: + items: + properties: + effect: + type: string + key: + type: string + operator: + type: string + tolerationSeconds: + format: int64 + type: integer + value: + type: string + type: object + type: array + x-kubernetes-list-type: atomic + topologySpreadConstraints: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + items: + type: string + type: array + x-kubernetes-list-type: atomic + maxSkew: + format: int32 + type: integer + minDomains: + format: int32 + type: integer + nodeAffinityPolicy: + type: string + nodeTaintsPolicy: + type: string + topologyKey: + type: string + whenUnsatisfiable: + type: string + required: + - maxSkew + - topologyKey + - whenUnsatisfiable + type: object + type: array + x-kubernetes-list-map-keys: + - topologyKey + - whenUnsatisfiable + x-kubernetes-list-type: map + volumes: + items: + properties: + awsElasticBlockStore: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + azureDisk: + properties: + cachingMode: + type: string + diskName: + type: string + diskURI: + type: string + fsType: + default: ext4 + type: string + kind: + type: string + readOnly: + default: false + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + properties: + readOnly: + type: boolean + secretName: + type: string + shareName: + type: string + required: + - secretName + - shareName + type: object + cephfs: + properties: + monitors: + items: + type: string + type: array + x-kubernetes-list-type: atomic + path: + type: string + readOnly: + type: boolean + secretFile: + type: string + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + user: + type: string + required: + - monitors + type: object + cinder: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + volumeID: + type: string + required: + - volumeID + type: object + configMap: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + csi: + properties: + driver: + type: string + fsType: + type: string + nodePublishSecretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + readOnly: + type: boolean + volumeAttributes: + additionalProperties: + type: string + type: object + required: + - driver + type: object + downwardAPI: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + x-kubernetes-list-type: atomic + type: object + emptyDir: + properties: + medium: + type: string + sizeLimit: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + ephemeral: + properties: + volumeClaimTemplate: + properties: + metadata: + type: object + spec: + properties: + accessModes: + items: + type: string + type: array + x-kubernetes-list-type: atomic + dataSource: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + required: + - kind + - name + type: object + x-kubernetes-map-type: atomic + dataSourceRef: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + required: + - kind + - name + type: object + resources: + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + storageClassName: + type: string + volumeAttributesClassName: + type: string + volumeMode: + type: string + volumeName: + type: string + type: object + required: + - spec + type: object + type: object + fc: + properties: + fsType: + type: string + lun: + format: int32 + type: integer + readOnly: + type: boolean + targetWWNs: + items: + type: string + type: array + x-kubernetes-list-type: atomic + wwids: + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + flexVolume: + properties: + driver: + type: string + fsType: + type: string + options: + additionalProperties: + type: string + type: object + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + required: + - driver + type: object + flocker: + properties: + datasetName: + type: string + datasetUUID: + type: string + type: object + gcePersistentDisk: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + pdName: + type: string + readOnly: + type: boolean + required: + - pdName + type: object + gitRepo: + properties: + directory: + type: string + repository: + type: string + revision: + type: string + required: + - repository + type: object + glusterfs: + properties: + endpoints: + type: string + path: + type: string + readOnly: + type: boolean + required: + - endpoints + - path + type: object + hostPath: + properties: + path: + type: string + type: + type: string + required: + - path + type: object + image: + properties: + pullPolicy: + type: string + reference: + type: string + type: object + iscsi: + properties: + chapAuthDiscovery: + type: boolean + chapAuthSession: + type: boolean + fsType: + type: string + initiatorName: + type: string + iqn: + type: string + iscsiInterface: + default: default + type: string + lun: + format: int32 + type: integer + portals: + items: + type: string + type: array + x-kubernetes-list-type: atomic + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + targetPortal: + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + type: string + nfs: + properties: + path: + type: string + readOnly: + type: boolean + server: + type: string + required: + - path + - server + type: object + persistentVolumeClaim: + properties: + claimName: + type: string + readOnly: + type: boolean + required: + - claimName + type: object + photonPersistentDisk: + properties: + fsType: + type: string + pdID: + type: string + required: + - pdID + type: object + portworxVolume: + properties: + fsType: + type: string + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + projected: + properties: + defaultMode: + format: int32 + type: integer + sources: + items: + properties: + clusterTrustBundle: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + name: + type: string + optional: + type: boolean + path: + type: string + signerName: + type: string + required: + - path + type: object + configMap: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + downwardAPI: + properties: + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + x-kubernetes-list-type: atomic + type: object + podCertificate: + properties: + certificateChainPath: + type: string + credentialBundlePath: + type: string + keyPath: + type: string + keyType: + type: string + maxExpirationSeconds: + format: int32 + type: integer + signerName: + type: string + userAnnotations: + additionalProperties: + type: string + type: object + required: + - keyType + - signerName + type: object + secret: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + serviceAccountToken: + properties: + audience: + type: string + expirationSeconds: + format: int64 + type: integer + path: + type: string + required: + - path + type: object + type: object + type: array + x-kubernetes-list-type: atomic + type: object + quobyte: + properties: + group: + type: string + readOnly: + type: boolean + registry: + type: string + tenant: + type: string + user: + type: string + volume: + type: string + required: + - registry + - volume + type: object + rbd: + properties: + fsType: + type: string + image: + type: string + keyring: + default: /etc/ceph/keyring + type: string + monitors: + items: + type: string + type: array + x-kubernetes-list-type: atomic + pool: + default: rbd + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + user: + default: admin + type: string + required: + - image + - monitors + type: object + scaleIO: + properties: + fsType: + default: xfs + type: string + gateway: + type: string + protectionDomain: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + sslEnabled: + type: boolean + storageMode: + default: ThinProvisioned + type: string + storagePool: + type: string + system: + type: string + volumeName: + type: string + required: + - gateway + - secretRef + - system + type: object + secret: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + optional: + type: boolean + secretName: + type: string + type: object + storageos: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + default: "" + type: string + type: object + x-kubernetes-map-type: atomic + volumeName: + type: string + volumeNamespace: + type: string + type: object + vsphereVolume: + properties: + fsType: + type: string + storagePolicyID: + type: string + storagePolicyName: + type: string + volumePath: + type: string + required: + - volumePath + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + workloadRef: + properties: + name: + type: string + podGroup: + type: string + podGroupReplicaKey: + type: string + required: + - name + - podGroup + type: object + required: + - containers + type: object + properties: + additionalProperties: + type: string + type: object + type: object + required: + - spec + type: object + served: true + storage: true diff --git a/control-operator/config/crd/kustomization.yaml b/control-operator/config/crd/kustomization.yaml new file mode 100644 index 000000000..fdb11dc4d --- /dev/null +++ b/control-operator/config/crd/kustomization.yaml @@ -0,0 +1,23 @@ +# This kustomization.yaml is not intended to be run by itself, +# since it depends on service name and namespace that are out of this kustomize package. +# It should be run by config/default +resources: +- bases/aliecs.alice.cern_tasks.yaml +- bases/aliecs.alice.cern_environments.yaml +- bases/aliecs.alice.cern_tasktemplates.yaml +#+kubebuilder:scaffold:crdkustomizeresource + +patches: +# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix. +# patches here are for enabling the conversion webhook for each CRD +#- path: patches/webhook_in_tasks.yaml +#+kubebuilder:scaffold:crdkustomizewebhookpatch + +# [CERTMANAGER] To enable cert-manager, uncomment all the sections with [CERTMANAGER] prefix. +# patches here are for enabling the CA injection for each CRD +#- path: patches/cainjection_in_tasks.yaml +#+kubebuilder:scaffold:crdkustomizecainjectionpatch + +# the following config is for teaching kustomize how to do kustomization for CRDs. +configurations: +- kustomizeconfig.yaml diff --git a/control-operator/config/crd/kustomizeconfig.yaml b/control-operator/config/crd/kustomizeconfig.yaml new file mode 100644 index 000000000..ec5c150a9 --- /dev/null +++ b/control-operator/config/crd/kustomizeconfig.yaml @@ -0,0 +1,19 @@ +# This file is for teaching kustomize how to substitute name and namespace reference in CRD +nameReference: +- kind: Service + version: v1 + fieldSpecs: + - kind: CustomResourceDefinition + version: v1 + group: apiextensions.k8s.io + path: spec/conversion/webhook/clientConfig/service/name + +namespace: +- kind: CustomResourceDefinition + version: v1 + group: apiextensions.k8s.io + path: spec/conversion/webhook/clientConfig/service/namespace + create: false + +varReference: +- path: metadata/annotations diff --git a/control-operator/config/crd/patches/cainjection_in_tasks.yaml b/control-operator/config/crd/patches/cainjection_in_tasks.yaml new file mode 100644 index 000000000..f5feffa47 --- /dev/null +++ b/control-operator/config/crd/patches/cainjection_in_tasks.yaml @@ -0,0 +1,7 @@ +# The following patch adds a directive for certmanager to inject CA into the CRD +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: CERTIFICATE_NAMESPACE/CERTIFICATE_NAME + name: tasks.aliecs.alice.cern diff --git a/control-operator/config/crd/patches/webhook_in_tasks.yaml b/control-operator/config/crd/patches/webhook_in_tasks.yaml new file mode 100644 index 000000000..b69d74838 --- /dev/null +++ b/control-operator/config/crd/patches/webhook_in_tasks.yaml @@ -0,0 +1,16 @@ +# The following patch enables a conversion webhook for the CRD +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: tasks.aliecs.alice.cern +spec: + conversion: + strategy: Webhook + webhook: + clientConfig: + service: + namespace: system + name: webhook-service + path: /convert + conversionReviewVersions: + - v1 diff --git a/control-operator/config/default/environment_auth_proxy_patch.yaml b/control-operator/config/default/environment_auth_proxy_patch.yaml new file mode 100644 index 000000000..150113a87 --- /dev/null +++ b/control-operator/config/default/environment_auth_proxy_patch.yaml @@ -0,0 +1,39 @@ +# This patch inject a sidecar container which is a HTTP proxy for the +# controller manager, it performs RBAC authorization against the Kubernetes API using SubjectAccessReviews. +apiVersion: apps/v1 +kind: Deployment +metadata: + name: environment-manager + namespace: system +spec: + template: + spec: + containers: + - name: kube-rbac-proxy + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - "ALL" + image: gcr.io/kubebuilder/kube-rbac-proxy:v0.14.1 + args: + - "--secure-listen-address=0.0.0.0:8443" + - "--upstream=http://127.0.0.1:8080/" + - "--logtostderr=true" + - "--v=0" + ports: + - containerPort: 8443 + protocol: TCP + name: https + resources: + limits: + cpu: 500m + memory: 128Mi + requests: + cpu: 5m + memory: 64Mi + - name: manager + args: + - "--health-probe-bind-address=:8081" + - "--metrics-bind-address=127.0.0.1:8080" + - "--leader-elect" diff --git a/control-operator/config/default/kustomization.yaml b/control-operator/config/default/kustomization.yaml new file mode 100644 index 000000000..9185c158d --- /dev/null +++ b/control-operator/config/default/kustomization.yaml @@ -0,0 +1,145 @@ +# Adds namespace to all resources. +namespace: operator-system + +# Value of this field is prepended to the +# names of all resources, e.g. a deployment named +# "wordpress" becomes "alices-wordpress". +# Note that it should also match with the prefix (text before '-') of the namespace +# field above. +namePrefix: operator- + +# Labels to add to all resources and selectors. +#labels: +#- includeSelectors: true +# pairs: +# someName: someValue + +resources: +- ../base +- ../manager +# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in +# crd/kustomization.yaml +#- ../webhook +# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. 'WEBHOOK' components are required. +#- ../certmanager +# [PROMETHEUS] To enable prometheus monitor, uncomment all sections with 'PROMETHEUS'. +#- ../prometheus + +patchesStrategicMerge: +# Protect the /metrics endpoint by putting it behind auth. +# If you want your controller-manager to expose the /metrics +# endpoint w/o any authn/z, please comment the following lines. +# TODO: replace kube-rbac-proxy with WithAuthenticationAndAuthorization (see kubebuilder deprecation notice) +#- manager_auth_proxy_patch.yaml +#- environment_auth_proxy_patch.yaml + + + +# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in +# crd/kustomization.yaml +#- manager_webhook_patch.yaml + +# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. +# Uncomment 'CERTMANAGER' sections in crd/kustomization.yaml to enable the CA injection in the admission webhooks. +# 'CERTMANAGER' needs to be enabled to use ca injection +#- webhookcainjection_patch.yaml + +# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER' prefix. +# Uncomment the following replacements to add the cert-manager CA injection annotations +#replacements: +# - source: # Add cert-manager annotation to ValidatingWebhookConfiguration, MutatingWebhookConfiguration and CRDs +# kind: Certificate +# group: cert-manager.io +# version: v1 +# name: serving-cert # this name should match the one in certificate.yaml +# fieldPath: .metadata.namespace # namespace of the certificate CR +# targets: +# - select: +# kind: ValidatingWebhookConfiguration +# fieldPaths: +# - .metadata.annotations.[cert-manager.io/inject-ca-from] +# options: +# delimiter: '/' +# index: 0 +# create: true +# - select: +# kind: MutatingWebhookConfiguration +# fieldPaths: +# - .metadata.annotations.[cert-manager.io/inject-ca-from] +# options: +# delimiter: '/' +# index: 0 +# create: true +# - select: +# kind: CustomResourceDefinition +# fieldPaths: +# - .metadata.annotations.[cert-manager.io/inject-ca-from] +# options: +# delimiter: '/' +# index: 0 +# create: true +# - source: +# kind: Certificate +# group: cert-manager.io +# version: v1 +# name: serving-cert # this name should match the one in certificate.yaml +# fieldPath: .metadata.name +# targets: +# - select: +# kind: ValidatingWebhookConfiguration +# fieldPaths: +# - .metadata.annotations.[cert-manager.io/inject-ca-from] +# options: +# delimiter: '/' +# index: 1 +# create: true +# - select: +# kind: MutatingWebhookConfiguration +# fieldPaths: +# - .metadata.annotations.[cert-manager.io/inject-ca-from] +# options: +# delimiter: '/' +# index: 1 +# create: true +# - select: +# kind: CustomResourceDefinition +# fieldPaths: +# - .metadata.annotations.[cert-manager.io/inject-ca-from] +# options: +# delimiter: '/' +# index: 1 +# create: true +# - source: # Add cert-manager annotation to the webhook Service +# kind: Service +# version: v1 +# name: webhook-service +# fieldPath: .metadata.name # namespace of the service +# targets: +# - select: +# kind: Certificate +# group: cert-manager.io +# version: v1 +# fieldPaths: +# - .spec.dnsNames.0 +# - .spec.dnsNames.1 +# options: +# delimiter: '.' +# index: 0 +# create: true +# - source: +# kind: Service +# version: v1 +# name: webhook-service +# fieldPath: .metadata.namespace # namespace of the service +# targets: +# - select: +# kind: Certificate +# group: cert-manager.io +# version: v1 +# fieldPaths: +# - .spec.dnsNames.0 +# - .spec.dnsNames.1 +# options: +# delimiter: '.' +# index: 1 +# create: true diff --git a/control-operator/config/default/manager_auth_proxy_patch.yaml b/control-operator/config/default/manager_auth_proxy_patch.yaml new file mode 100644 index 000000000..a2f54b4ab --- /dev/null +++ b/control-operator/config/default/manager_auth_proxy_patch.yaml @@ -0,0 +1,39 @@ +# This patch inject a sidecar container which is a HTTP proxy for the +# controller manager, it performs RBAC authorization against the Kubernetes API using SubjectAccessReviews. +apiVersion: apps/v1 +kind: Deployment +metadata: + name: task-manager + namespace: system +spec: + template: + spec: + containers: + - name: kube-rbac-proxy + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - "ALL" + image: gcr.io/kubebuilder/kube-rbac-proxy:v0.14.1 + args: + - "--secure-listen-address=0.0.0.0:8443" + - "--upstream=http://127.0.0.1:8080/" + - "--logtostderr=true" + - "--v=0" + ports: + - containerPort: 8443 + protocol: TCP + name: https + resources: + limits: + cpu: 500m + memory: 128Mi + requests: + cpu: 5m + memory: 64Mi + - name: manager + args: + - "--health-probe-bind-address=:8081" + - "--metrics-bind-address=127.0.0.1:8080" + - "--leader-elect" diff --git a/control-operator/config/default/manager_config_patch.yaml b/control-operator/config/default/manager_config_patch.yaml new file mode 100644 index 000000000..f6f589169 --- /dev/null +++ b/control-operator/config/default/manager_config_patch.yaml @@ -0,0 +1,10 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: controller-manager + namespace: system +spec: + template: + spec: + containers: + - name: manager diff --git a/control-operator/config/environment/kustomization.yaml b/control-operator/config/environment/kustomization.yaml new file mode 100644 index 000000000..c4c6010b5 --- /dev/null +++ b/control-operator/config/environment/kustomization.yaml @@ -0,0 +1,14 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +namespace: operator-system +namePrefix: operator- + +resources: +- ../base +- ../manager/environment-manager + +images: +- name: environment-manager + newName: gitlab-registry.cern.ch/aliceo2group/dockerfiles/aliecs/environment-manager + newTag: latest diff --git a/control-operator/config/manager/environment-manager/environment-manager.yaml b/control-operator/config/manager/environment-manager/environment-manager.yaml new file mode 100644 index 000000000..f4536ca02 --- /dev/null +++ b/control-operator/config/manager/environment-manager/environment-manager.yaml @@ -0,0 +1,66 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: environment-manager + namespace: system + labels: + control-plane: environment-manager + app.kubernetes.io/name: deployment + app.kubernetes.io/instance: environment-manager + app.kubernetes.io/component: manager + app.kubernetes.io/created-by: operator + app.kubernetes.io/part-of: operator + app.kubernetes.io/managed-by: kustomize +spec: + selector: + matchLabels: + control-plane: environment-manager + replicas: 1 + template: + metadata: + annotations: + kubectl.kubernetes.io/default-container: environment-manager + labels: + control-plane: environment-manager + spec: + # placeholder, replaced with $(PULL_SECRET) by `make deploy-task` / `make deploy-environment` + imagePullSecrets: + - name: pull-secret-placeholder + securityContext: + runAsNonRoot: true + containers: + - command: + - /manager + args: + - --leader-elect + - --health-probe-bind-address=:9080 + - --metrics-bind-address=:9081 + - --zap-encoder=json + image: environment-manager:latest + name: environment-manager + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - "ALL" + livenessProbe: + httpGet: + path: /healthz + port: 9080 + initialDelaySeconds: 15 + periodSeconds: 20 + readinessProbe: + httpGet: + path: /readyz + port: 9080 + initialDelaySeconds: 5 + periodSeconds: 10 + resources: + limits: + cpu: 500m + memory: 128Mi + requests: + cpu: 10m + memory: 64Mi + serviceAccountName: controller-manager + terminationGracePeriodSeconds: 10 diff --git a/control-operator/config/manager/environment-manager/kustomization.yaml b/control-operator/config/manager/environment-manager/kustomization.yaml new file mode 100644 index 000000000..3349a34d4 --- /dev/null +++ b/control-operator/config/manager/environment-manager/kustomization.yaml @@ -0,0 +1,5 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: +- environment-manager.yaml diff --git a/control-operator/config/manager/kustomization.yaml b/control-operator/config/manager/kustomization.yaml new file mode 100644 index 000000000..c958d22a6 --- /dev/null +++ b/control-operator/config/manager/kustomization.yaml @@ -0,0 +1,12 @@ +resources: +- task-manager +- environment-manager +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +images: +- name: environment-manager + newName: gitlab-registry.cern.ch/aliceo2group/dockerfiles/aliecs/environment-manager + newTag: latest +- name: task-manager + newName: gitlab-registry.cern.ch/aliceo2group/dockerfiles/aliecs/task-manager + newTag: latest diff --git a/control-operator/config/manager/task-manager/kustomization.yaml b/control-operator/config/manager/task-manager/kustomization.yaml new file mode 100644 index 000000000..578dd785e --- /dev/null +++ b/control-operator/config/manager/task-manager/kustomization.yaml @@ -0,0 +1,5 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: +- task-manager.yaml diff --git a/control-operator/config/manager/task-manager/task-manager.yaml b/control-operator/config/manager/task-manager/task-manager.yaml new file mode 100644 index 000000000..c7bcf8adb --- /dev/null +++ b/control-operator/config/manager/task-manager/task-manager.yaml @@ -0,0 +1,71 @@ +apiVersion: apps/v1 +kind: DaemonSet +metadata: + name: task-manager + namespace: system + labels: + control-plane: task-manager + app.kubernetes.io/name: deployment + app.kubernetes.io/instance: task-manager + app.kubernetes.io/component: manager + app.kubernetes.io/created-by: operator + app.kubernetes.io/part-of: operator + app.kubernetes.io/managed-by: kustomize +spec: + selector: + matchLabels: + control-plane: task-manager + template: + metadata: + annotations: + kubectl.kubernetes.io/default-container: task-manager + labels: + control-plane: task-manager + spec: + hostNetwork: true + # placeholder, replaced with $(PULL_SECRET) by `make deploy-task` / `make deploy-environment` + imagePullSecrets: + - name: pull-secret-placeholder + securityContext: + runAsNonRoot: true + containers: + - command: + - /manager + args: + - --health-probe-bind-address=:9082 + - --metrics-bind-address=:9083 + - --max-concurrent-reconciles=5 + - --zap-encoder=json + env: + - name: NODE_NAME + valueFrom: + fieldRef: + fieldPath: spec.nodeName + image: task-manager:latest + name: task-manager + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - "ALL" + livenessProbe: + httpGet: + path: /healthz + port: 9082 + initialDelaySeconds: 15 + periodSeconds: 20 + readinessProbe: + httpGet: + path: /readyz + port: 9082 + initialDelaySeconds: 5 + periodSeconds: 10 + resources: + limits: + cpu: 500m + memory: 128Mi + requests: + cpu: 10m + memory: 64Mi + serviceAccountName: controller-manager + terminationGracePeriodSeconds: 10 diff --git a/control-operator/config/manifests/kustomization.yaml b/control-operator/config/manifests/kustomization.yaml new file mode 100644 index 000000000..61b939e6f --- /dev/null +++ b/control-operator/config/manifests/kustomization.yaml @@ -0,0 +1,28 @@ +# These resources constitute the fully configured set of manifests +# used to generate the 'manifests/' directory in a bundle. +resources: +- bases/operator.clusterserviceversion.yaml +- ../default +- ../samples +- ../scorecard + +# [WEBHOOK] To enable webhooks, uncomment all the sections with [WEBHOOK] prefix. +# Do NOT uncomment sections with prefix [CERTMANAGER], as OLM does not support cert-manager. +# These patches remove the unnecessary "cert" volume and its manager container volumeMount. +#patchesJson6902: +#- target: +# group: apps +# version: v1 +# kind: Deployment +# name: controller-manager +# namespace: system +# patch: |- +# # Remove the manager container's "cert" volumeMount, since OLM will create and mount a set of certs. +# # Update the indices in this path if adding or removing containers/volumeMounts in the manager's Deployment. +# - op: remove + +# path: /spec/template/spec/containers/0/volumeMounts/0 +# # Remove the "cert" volume, since OLM will create and mount a set of certs. +# # Update the indices in this path if adding or removing volumes in the manager's Deployment. +# - op: remove +# path: /spec/template/spec/volumes/0 diff --git a/control-operator/config/prometheus/kustomization.yaml b/control-operator/config/prometheus/kustomization.yaml new file mode 100644 index 000000000..ed137168a --- /dev/null +++ b/control-operator/config/prometheus/kustomization.yaml @@ -0,0 +1,2 @@ +resources: +- monitor.yaml diff --git a/control-operator/config/prometheus/monitor.yaml b/control-operator/config/prometheus/monitor.yaml new file mode 100644 index 000000000..50225c34c --- /dev/null +++ b/control-operator/config/prometheus/monitor.yaml @@ -0,0 +1,26 @@ + +# Prometheus Monitor Service (Metrics) +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + labels: + control-plane: controller-manager + app.kubernetes.io/name: servicemonitor + app.kubernetes.io/instance: controller-manager-metrics-monitor + app.kubernetes.io/component: metrics + app.kubernetes.io/created-by: operator + app.kubernetes.io/part-of: operator + app.kubernetes.io/managed-by: kustomize + name: controller-manager-metrics-monitor + namespace: system +spec: + endpoints: + - path: /metrics + port: https + scheme: https + bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token + tlsConfig: + insecureSkipVerify: true + selector: + matchLabels: + control-plane: controller-manager diff --git a/control-operator/config/rbac/auth_proxy_client_clusterrole.yaml b/control-operator/config/rbac/auth_proxy_client_clusterrole.yaml new file mode 100644 index 000000000..8f6e4d166 --- /dev/null +++ b/control-operator/config/rbac/auth_proxy_client_clusterrole.yaml @@ -0,0 +1,16 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: clusterrole + app.kubernetes.io/instance: metrics-reader + app.kubernetes.io/component: kube-rbac-proxy + app.kubernetes.io/created-by: operator + app.kubernetes.io/part-of: operator + app.kubernetes.io/managed-by: kustomize + name: metrics-reader +rules: +- nonResourceURLs: + - "/metrics" + verbs: + - get diff --git a/control-operator/config/rbac/auth_proxy_role.yaml b/control-operator/config/rbac/auth_proxy_role.yaml new file mode 100644 index 000000000..c48234ef0 --- /dev/null +++ b/control-operator/config/rbac/auth_proxy_role.yaml @@ -0,0 +1,24 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: clusterrole + app.kubernetes.io/instance: proxy-role + app.kubernetes.io/component: kube-rbac-proxy + app.kubernetes.io/created-by: operator + app.kubernetes.io/part-of: operator + app.kubernetes.io/managed-by: kustomize + name: proxy-role +rules: +- apiGroups: + - authentication.k8s.io + resources: + - tokenreviews + verbs: + - create +- apiGroups: + - authorization.k8s.io + resources: + - subjectaccessreviews + verbs: + - create diff --git a/control-operator/config/rbac/auth_proxy_role_binding.yaml b/control-operator/config/rbac/auth_proxy_role_binding.yaml new file mode 100644 index 000000000..8cb153a80 --- /dev/null +++ b/control-operator/config/rbac/auth_proxy_role_binding.yaml @@ -0,0 +1,19 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: + app.kubernetes.io/name: clusterrolebinding + app.kubernetes.io/instance: proxy-rolebinding + app.kubernetes.io/component: kube-rbac-proxy + app.kubernetes.io/created-by: operator + app.kubernetes.io/part-of: operator + app.kubernetes.io/managed-by: kustomize + name: proxy-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: proxy-role +subjects: +- kind: ServiceAccount + name: controller-manager + namespace: system diff --git a/control-operator/config/rbac/auth_proxy_service.yaml b/control-operator/config/rbac/auth_proxy_service.yaml new file mode 100644 index 000000000..8fa979bad --- /dev/null +++ b/control-operator/config/rbac/auth_proxy_service.yaml @@ -0,0 +1,21 @@ +apiVersion: v1 +kind: Service +metadata: + labels: + control-plane: controller-manager + app.kubernetes.io/name: service + app.kubernetes.io/instance: controller-manager-metrics-service + app.kubernetes.io/component: kube-rbac-proxy + app.kubernetes.io/created-by: operator + app.kubernetes.io/part-of: operator + app.kubernetes.io/managed-by: kustomize + name: controller-manager-metrics-service + namespace: system +spec: + ports: + - name: https + port: 8443 + protocol: TCP + targetPort: https + selector: + control-plane: controller-manager diff --git a/control-operator/config/rbac/environment_admin_role.yaml b/control-operator/config/rbac/environment_admin_role.yaml new file mode 100644 index 000000000..69f7776af --- /dev/null +++ b/control-operator/config/rbac/environment_admin_role.yaml @@ -0,0 +1,27 @@ +# This rule is not used by the project operator itself. +# It is provided to allow the cluster admin to help manage permissions for users. +# +# Grants full permissions ('*') over aliecs.alice.cern. +# This role is intended for users authorized to modify roles and bindings within the cluster, +# enabling them to delegate specific permissions to other users or groups as needed. + +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: operator + app.kubernetes.io/managed-by: kustomize + name: environment-admin-role +rules: +- apiGroups: + - aliecs.alice.cern + resources: + - environments + verbs: + - '*' +- apiGroups: + - aliecs.alice.cern + resources: + - environments/status + verbs: + - get diff --git a/control-operator/config/rbac/environment_editor_role.yaml b/control-operator/config/rbac/environment_editor_role.yaml new file mode 100644 index 000000000..3af1791ec --- /dev/null +++ b/control-operator/config/rbac/environment_editor_role.yaml @@ -0,0 +1,33 @@ +# This rule is not used by the project operator itself. +# It is provided to allow the cluster admin to help manage permissions for users. +# +# Grants permissions to create, update, and delete resources within the aliecs.alice.cern. +# This role is intended for users who need to manage these resources +# but should not control RBAC or manage permissions for others. + +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: operator + app.kubernetes.io/managed-by: kustomize + name: environment-editor-role +rules: +- apiGroups: + - aliecs.alice.cern + resources: + - environments + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - aliecs.alice.cern + resources: + - environments/status + verbs: + - get diff --git a/control-operator/config/rbac/environment_viewer_role.yaml b/control-operator/config/rbac/environment_viewer_role.yaml new file mode 100644 index 000000000..3d5084c85 --- /dev/null +++ b/control-operator/config/rbac/environment_viewer_role.yaml @@ -0,0 +1,29 @@ +# This rule is not used by the project operator itself. +# It is provided to allow the cluster admin to help manage permissions for users. +# +# Grants read-only access to aliecs.alice.cern resources. +# This role is intended for users who need visibility into these resources +# without permissions to modify them. It is ideal for monitoring purposes and limited-access viewing. + +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: operator + app.kubernetes.io/managed-by: kustomize + name: environment-viewer-role +rules: +- apiGroups: + - aliecs.alice.cern + resources: + - environments + verbs: + - get + - list + - watch +- apiGroups: + - aliecs.alice.cern + resources: + - environments/status + verbs: + - get diff --git a/control-operator/config/rbac/kustomization.yaml b/control-operator/config/rbac/kustomization.yaml new file mode 100644 index 000000000..426bfd7d4 --- /dev/null +++ b/control-operator/config/rbac/kustomization.yaml @@ -0,0 +1,29 @@ +resources: +# All RBAC will be applied under this service account in +# the deployment namespace. You may comment out this resource +# if your manager will use a service account that exists at +# runtime. Be sure to update RoleBinding and ClusterRoleBinding +# subjects if changing service account names. +- service_account.yaml +- role.yaml +- role_binding.yaml +- leader_election_role.yaml +- leader_election_role_binding.yaml +# Comment the following 4 lines if you want to disable +# the auth proxy (https://github.com/brancz/kube-rbac-proxy) +# which protects your /metrics endpoint. +- auth_proxy_service.yaml +- auth_proxy_role.yaml +- auth_proxy_role_binding.yaml +- auth_proxy_client_clusterrole.yaml +# For each CRD, "Admin", "Editor" and "Viewer" roles are scaffolded by +# default, aiding admins in cluster management. Those roles are +# not used by the operator itself. You can comment the following lines +# if you do not want those helpers be installed with your Project. +- tasktemplate_admin_role.yaml +- tasktemplate_editor_role.yaml +- tasktemplate_viewer_role.yaml +- environment_admin_role.yaml +- environment_editor_role.yaml +- environment_viewer_role.yaml + diff --git a/control-operator/config/rbac/leader_election_role.yaml b/control-operator/config/rbac/leader_election_role.yaml new file mode 100644 index 000000000..6a5caf3f6 --- /dev/null +++ b/control-operator/config/rbac/leader_election_role.yaml @@ -0,0 +1,44 @@ +# permissions to do leader election. +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + labels: + app.kubernetes.io/name: role + app.kubernetes.io/instance: leader-election-role + app.kubernetes.io/component: rbac + app.kubernetes.io/created-by: operator + app.kubernetes.io/part-of: operator + app.kubernetes.io/managed-by: kustomize + name: leader-election-role +rules: +- apiGroups: + - "" + resources: + - configmaps + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +- apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +- apiGroups: + - "" + resources: + - events + verbs: + - create + - patch diff --git a/control-operator/config/rbac/leader_election_role_binding.yaml b/control-operator/config/rbac/leader_election_role_binding.yaml new file mode 100644 index 000000000..f07fa72af --- /dev/null +++ b/control-operator/config/rbac/leader_election_role_binding.yaml @@ -0,0 +1,19 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + labels: + app.kubernetes.io/name: rolebinding + app.kubernetes.io/instance: leader-election-rolebinding + app.kubernetes.io/component: rbac + app.kubernetes.io/created-by: operator + app.kubernetes.io/part-of: operator + app.kubernetes.io/managed-by: kustomize + name: leader-election-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: leader-election-role +subjects: +- kind: ServiceAccount + name: controller-manager + namespace: system diff --git a/control-operator/config/rbac/role.yaml b/control-operator/config/rbac/role.yaml new file mode 100644 index 000000000..3b38b9048 --- /dev/null +++ b/control-operator/config/rbac/role.yaml @@ -0,0 +1,70 @@ +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: manager-role +rules: +- apiGroups: + - "" + resources: + - events + verbs: + - create + - patch +- apiGroups: + - "" + resources: + - nodes + verbs: + - get + - list + - watch +- apiGroups: + - "" + resources: + - pods + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - aliecs.alice.cern + resources: + - environments + - tasks + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - aliecs.alice.cern + resources: + - environments/finalizers + - tasks/finalizers + verbs: + - update +- apiGroups: + - aliecs.alice.cern + resources: + - environments/status + - tasks/status + verbs: + - get + - patch + - update +- apiGroups: + - aliecs.alice.cern + resources: + - tasktemplates + verbs: + - get + - list + - watch diff --git a/control-operator/config/rbac/role_binding.yaml b/control-operator/config/rbac/role_binding.yaml new file mode 100644 index 000000000..b8b41d60d --- /dev/null +++ b/control-operator/config/rbac/role_binding.yaml @@ -0,0 +1,19 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: + app.kubernetes.io/name: clusterrolebinding + app.kubernetes.io/instance: manager-rolebinding + app.kubernetes.io/component: rbac + app.kubernetes.io/created-by: operator + app.kubernetes.io/part-of: operator + app.kubernetes.io/managed-by: kustomize + name: manager-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: manager-role +subjects: +- kind: ServiceAccount + name: controller-manager + namespace: system diff --git a/control-operator/config/rbac/service_account.yaml b/control-operator/config/rbac/service_account.yaml new file mode 100644 index 000000000..00526cc6b --- /dev/null +++ b/control-operator/config/rbac/service_account.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + labels: + app.kubernetes.io/name: serviceaccount + app.kubernetes.io/instance: controller-manager-sa + app.kubernetes.io/component: rbac + app.kubernetes.io/created-by: operator + app.kubernetes.io/part-of: operator + app.kubernetes.io/managed-by: kustomize + name: controller-manager + namespace: system diff --git a/control-operator/config/rbac/task_editor_role.yaml b/control-operator/config/rbac/task_editor_role.yaml new file mode 100644 index 000000000..77de69cdc --- /dev/null +++ b/control-operator/config/rbac/task_editor_role.yaml @@ -0,0 +1,31 @@ +# permissions for end users to edit tasks. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: clusterrole + app.kubernetes.io/instance: task-editor-role + app.kubernetes.io/component: rbac + app.kubernetes.io/created-by: operator + app.kubernetes.io/part-of: operator + app.kubernetes.io/managed-by: kustomize + name: task-editor-role +rules: +- apiGroups: + - aliecs.alice.cern + resources: + - tasks + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - aliecs.alice.cern + resources: + - tasks/status + verbs: + - get diff --git a/control-operator/config/rbac/task_viewer_role.yaml b/control-operator/config/rbac/task_viewer_role.yaml new file mode 100644 index 000000000..8eaecaa1c --- /dev/null +++ b/control-operator/config/rbac/task_viewer_role.yaml @@ -0,0 +1,27 @@ +# permissions for end users to view tasks. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: clusterrole + app.kubernetes.io/instance: task-viewer-role + app.kubernetes.io/component: rbac + app.kubernetes.io/created-by: operator + app.kubernetes.io/part-of: operator + app.kubernetes.io/managed-by: kustomize + name: task-viewer-role +rules: +- apiGroups: + - aliecs.alice.cern + resources: + - tasks + verbs: + - get + - list + - watch +- apiGroups: + - aliecs.alice.cern + resources: + - tasks/status + verbs: + - get diff --git a/control-operator/config/rbac/tasktemplate_admin_role.yaml b/control-operator/config/rbac/tasktemplate_admin_role.yaml new file mode 100644 index 000000000..c6a5bbd25 --- /dev/null +++ b/control-operator/config/rbac/tasktemplate_admin_role.yaml @@ -0,0 +1,27 @@ +# This rule is not used by the project operator itself. +# It is provided to allow the cluster admin to help manage permissions for users. +# +# Grants full permissions ('*') over aliecs.alice.cern. +# This role is intended for users authorized to modify roles and bindings within the cluster, +# enabling them to delegate specific permissions to other users or groups as needed. + +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: operator + app.kubernetes.io/managed-by: kustomize + name: tasktemplate-admin-role +rules: +- apiGroups: + - aliecs.alice.cern + resources: + - tasktemplates + verbs: + - '*' +- apiGroups: + - aliecs.alice.cern + resources: + - tasktemplates/status + verbs: + - get diff --git a/control-operator/config/rbac/tasktemplate_editor_role.yaml b/control-operator/config/rbac/tasktemplate_editor_role.yaml new file mode 100644 index 000000000..448283e73 --- /dev/null +++ b/control-operator/config/rbac/tasktemplate_editor_role.yaml @@ -0,0 +1,33 @@ +# This rule is not used by the project operator itself. +# It is provided to allow the cluster admin to help manage permissions for users. +# +# Grants permissions to create, update, and delete resources within the aliecs.alice.cern. +# This role is intended for users who need to manage these resources +# but should not control RBAC or manage permissions for others. + +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: operator + app.kubernetes.io/managed-by: kustomize + name: tasktemplate-editor-role +rules: +- apiGroups: + - aliecs.alice.cern + resources: + - tasktemplates + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - aliecs.alice.cern + resources: + - tasktemplates/status + verbs: + - get diff --git a/control-operator/config/rbac/tasktemplate_viewer_role.yaml b/control-operator/config/rbac/tasktemplate_viewer_role.yaml new file mode 100644 index 000000000..96127f5db --- /dev/null +++ b/control-operator/config/rbac/tasktemplate_viewer_role.yaml @@ -0,0 +1,29 @@ +# This rule is not used by the project operator itself. +# It is provided to allow the cluster admin to help manage permissions for users. +# +# Grants read-only access to aliecs.alice.cern resources. +# This role is intended for users who need visibility into these resources +# without permissions to modify them. It is ideal for monitoring purposes and limited-access viewing. + +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: operator + app.kubernetes.io/managed-by: kustomize + name: tasktemplate-viewer-role +rules: +- apiGroups: + - aliecs.alice.cern + resources: + - tasktemplates + verbs: + - get + - list + - watch +- apiGroups: + - aliecs.alice.cern + resources: + - tasktemplates/status + verbs: + - get diff --git a/control-operator/config/samples/aliecs_v1alpha1_environment.yaml b/control-operator/config/samples/aliecs_v1alpha1_environment.yaml new file mode 100644 index 000000000..164344db5 --- /dev/null +++ b/control-operator/config/samples/aliecs_v1alpha1_environment.yaml @@ -0,0 +1,9 @@ +apiVersion: aliecs.alice.cern/v1alpha1 +kind: Environment +metadata: + labels: + app.kubernetes.io/name: operator + app.kubernetes.io/managed-by: kustomize + name: environment-sample +spec: + # TODO(user): Add fields here diff --git a/control-operator/config/samples/aliecs_v1alpha1_task.yaml b/control-operator/config/samples/aliecs_v1alpha1_task.yaml new file mode 100644 index 000000000..b5c853bae --- /dev/null +++ b/control-operator/config/samples/aliecs_v1alpha1_task.yaml @@ -0,0 +1,12 @@ +apiVersion: aliecs.alice.cern/v1alpha1 +kind: Task +metadata: + labels: + app.kubernetes.io/name: task + app.kubernetes.io/instance: task-sample + app.kubernetes.io/part-of: operator + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/created-by: operator + name: task-sample +spec: + # TODO(user): Add fields here diff --git a/control-operator/config/samples/aliecs_v1alpha1_tasktemplate.yaml b/control-operator/config/samples/aliecs_v1alpha1_tasktemplate.yaml new file mode 100644 index 000000000..82635c3d1 --- /dev/null +++ b/control-operator/config/samples/aliecs_v1alpha1_tasktemplate.yaml @@ -0,0 +1,9 @@ +apiVersion: aliecs.alice.cern/v1alpha1 +kind: TaskTemplate +metadata: + labels: + app.kubernetes.io/name: operator + app.kubernetes.io/managed-by: kustomize + name: tasktemplate-sample +spec: + # TODO(user): Add fields here diff --git a/control-operator/config/samples/kustomization.yaml b/control-operator/config/samples/kustomization.yaml new file mode 100644 index 000000000..81298dd00 --- /dev/null +++ b/control-operator/config/samples/kustomization.yaml @@ -0,0 +1,6 @@ +## Append samples of your project ## +resources: +- aliecs_v1alpha1_task.yaml +- aliecs_v1alpha1_environment.yaml +- aliecs_v1alpha1_tasktemplate.yaml +#+kubebuilder:scaffold:manifestskustomizesamples diff --git a/control-operator/config/scorecard/bases/config.yaml b/control-operator/config/scorecard/bases/config.yaml new file mode 100644 index 000000000..c77047841 --- /dev/null +++ b/control-operator/config/scorecard/bases/config.yaml @@ -0,0 +1,7 @@ +apiVersion: scorecard.operatorframework.io/v1alpha3 +kind: Configuration +metadata: + name: config +stages: +- parallel: true + tests: [] diff --git a/control-operator/config/scorecard/kustomization.yaml b/control-operator/config/scorecard/kustomization.yaml new file mode 100644 index 000000000..50cd2d084 --- /dev/null +++ b/control-operator/config/scorecard/kustomization.yaml @@ -0,0 +1,16 @@ +resources: +- bases/config.yaml +patchesJson6902: +- path: patches/basic.config.yaml + target: + group: scorecard.operatorframework.io + version: v1alpha3 + kind: Configuration + name: config +- path: patches/olm.config.yaml + target: + group: scorecard.operatorframework.io + version: v1alpha3 + kind: Configuration + name: config +#+kubebuilder:scaffold:patchesJson6902 diff --git a/control-operator/config/scorecard/patches/basic.config.yaml b/control-operator/config/scorecard/patches/basic.config.yaml new file mode 100644 index 000000000..8aa415864 --- /dev/null +++ b/control-operator/config/scorecard/patches/basic.config.yaml @@ -0,0 +1,10 @@ +- op: add + path: /stages/0/tests/- + value: + entrypoint: + - scorecard-test + - basic-check-spec + image: quay.io/operator-framework/scorecard-test:unknown + labels: + suite: basic + test: basic-check-spec-test diff --git a/control-operator/config/scorecard/patches/olm.config.yaml b/control-operator/config/scorecard/patches/olm.config.yaml new file mode 100644 index 000000000..47153a8b7 --- /dev/null +++ b/control-operator/config/scorecard/patches/olm.config.yaml @@ -0,0 +1,50 @@ +- op: add + path: /stages/0/tests/- + value: + entrypoint: + - scorecard-test + - olm-bundle-validation + image: quay.io/operator-framework/scorecard-test:unknown + labels: + suite: olm + test: olm-bundle-validation-test +- op: add + path: /stages/0/tests/- + value: + entrypoint: + - scorecard-test + - olm-crds-have-validation + image: quay.io/operator-framework/scorecard-test:unknown + labels: + suite: olm + test: olm-crds-have-validation-test +- op: add + path: /stages/0/tests/- + value: + entrypoint: + - scorecard-test + - olm-crds-have-resources + image: quay.io/operator-framework/scorecard-test:unknown + labels: + suite: olm + test: olm-crds-have-resources-test +- op: add + path: /stages/0/tests/- + value: + entrypoint: + - scorecard-test + - olm-spec-descriptors + image: quay.io/operator-framework/scorecard-test:unknown + labels: + suite: olm + test: olm-spec-descriptors-test +- op: add + path: /stages/0/tests/- + value: + entrypoint: + - scorecard-test + - olm-status-descriptors + image: quay.io/operator-framework/scorecard-test:unknown + labels: + suite: olm + test: olm-status-descriptors-test diff --git a/control-operator/config/task/kustomization.yaml b/control-operator/config/task/kustomization.yaml new file mode 100644 index 000000000..02b078f52 --- /dev/null +++ b/control-operator/config/task/kustomization.yaml @@ -0,0 +1,14 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +namespace: operator-system +namePrefix: operator- + +resources: +- ../base +- ../manager/task-manager + +images: +- name: task-manager + newName: gitlab-registry.cern.ch/aliceo2group/dockerfiles/aliecs/task-manager + newTag: latest diff --git a/control-operator/ecs-manifests/control-workflows/readout-docker.yaml b/control-operator/ecs-manifests/control-workflows/readout-docker.yaml new file mode 100644 index 000000000..4c7ee2cfc --- /dev/null +++ b/control-operator/ecs-manifests/control-workflows/readout-docker.yaml @@ -0,0 +1,33 @@ +name: readout # read by workflow +defaults: + readout_cfg_uri: "consul-ini://{{ consul_endpoint }}/o2/components/readout/ANY/any/readout-standalone-{{ task_hostname }}" + user: flp + log_task_stdout: none + log_task_stderr: none + _module_cmdline: >- + source /etc/profile.d/modules.sh && MODULEPATH={{ modulepath }} module load Readout Control-OCCPlugin && + o2-readout-exe + _plain_cmdline: "sudo -E docker run --name readout --replace --privileged --user flp -v /etc/group:/etc/group:ro -v /etc/passwd:/etc/passwd:ro -v /tmp:/tmp -v /lib/modules/$(uname -r):/lib/modules/$(uname -r) --network=host --ipc=host -e O2_DETECTOR -e O2_PARTITION -e OCC_CONTROL_PORT -e O2_SYSTEM -e O2_ROLE gitlab-registry.cern.ch/aliceo2group/dockerfiles/alma9-flp-node/readout:1 /opt/o2/bin/o2-readout-exe" +control: + mode: direct +wants: + cpu: 0.15 + memory: 128 +bind: + - name: readout + type: push + rateLogging: "{{ fmq_rate_logging }}" + addressing: ipc + transport: shmem +properties: {} +command: + stdout: "{{ log_task_stdout }}" + stderr: "{{ log_task_stderr }}" + shell: true + env: + - O2_DETECTOR={{ detector }} + - O2_PARTITION={{ environment_id }} + user: "{{ user }}" + arguments: + - "{{ readout_cfg_uri }}" + value: "{{ len(modulepath)>0 ? _module_cmdline : _plain_cmdline }}" diff --git a/control-operator/ecs-manifests/control-workflows/readout-kube-direct.yaml b/control-operator/ecs-manifests/control-workflows/readout-kube-direct.yaml new file mode 100644 index 000000000..ca81df06d --- /dev/null +++ b/control-operator/ecs-manifests/control-workflows/readout-kube-direct.yaml @@ -0,0 +1,33 @@ +name: readout # read by workflow +defaults: + readout_cfg_uri: "consul-ini://{{ consul_endpoint }}/o2/components/readout/ANY/any/readout-standalone-{{ task_hostname }}" + user: flp + log_task_stdout: none + log_task_stderr: none + _module_cmdline: >- + source /etc/profile.d/modules.sh && MODULEPATH={{ modulepath }} module load Readout Control-OCCPlugin && + o2-readout-exe + _plain_cmdline: "{{ o2_install_path }}/bin/o2-readout-exe" +control: + mode: kubernetes_direct +wants: + cpu: 0.15 + memory: 128 +bind: + - name: readout + type: push + rateLogging: "{{ fmq_rate_logging }}" + addressing: ipc + transport: shmem +properties: {} +command: + stdout: "{{ log_task_stdout }}" + stderr: "{{ log_task_stderr }}" + shell: true + env: + - O2_DETECTOR={{ detector }} + - O2_PARTITION={{ environment_id }} + user: "{{ user }}" + arguments: + - "{{ readout_cfg_uri }}" + value: "{{ len(modulepath)>0 ? _module_cmdline : _plain_cmdline }}" diff --git a/control-operator/ecs-manifests/control-workflows/readout-kube.yaml b/control-operator/ecs-manifests/control-workflows/readout-kube.yaml new file mode 100644 index 000000000..549aebd14 --- /dev/null +++ b/control-operator/ecs-manifests/control-workflows/readout-kube.yaml @@ -0,0 +1,35 @@ +name: readout # read by workflow +defaults: + readout_cfg_uri: "consul-ini://{{ consul_endpoint }}/o2/components/readout/ANY/any/readout-standalone-{{ task_hostname }}" + user: flp + log_task_stdout: none + log_task_stderr: none + _module_cmdline: >- + source /etc/profile.d/modules.sh && MODULEPATH={{ modulepath }} module load Readout Control-OCCPlugin && + o2-readout-exe + _plain_cmdline: "{{ o2_install_path }}/bin/o2-readout-exe" +control: + mode: kubectl_direct +wants: + cpu: 0.15 + memory: 128 +bind: + - name: readout + type: push + rateLogging: "{{ fmq_rate_logging }}" + addressing: ipc + transport: shmem +properties: {} +command: + stdout: "{{ log_task_stdout }}" + stderr: "{{ log_task_stderr }}" + shell: true + env: + - O2_DETECTOR={{ detector }} + - O2_PARTITION={{ environment_id }} + user: "{{ user }}" + arguments: + # the first argument is the location of yaml readout manifest to be loaded, the one to be used is located in this repo inside control-operator/ecs-manifests/kubernetes-manifests/readout.yaml + - "/root/readout.yaml" + - "{{ readout_cfg_uri }}" + value: "{{ len(modulepath)>0 ? _module_cmdline : _plain_cmdline }}" diff --git a/control-operator/ecs-manifests/control-workflows/readout-orig.yaml b/control-operator/ecs-manifests/control-workflows/readout-orig.yaml new file mode 100644 index 000000000..5d6130024 --- /dev/null +++ b/control-operator/ecs-manifests/control-workflows/readout-orig.yaml @@ -0,0 +1,33 @@ +name: readout # read by workflow +defaults: + readout_cfg_uri: "consul-ini://{{ consul_endpoint }}/o2/components/readout/ANY/any/readout-standalone-{{ task_hostname }}" + user: flp + log_task_stdout: none + log_task_stderr: none + _module_cmdline: >- + source /etc/profile.d/modules.sh && MODULEPATH={{ modulepath }} module load Readout Control-OCCPlugin && + o2-readout-exe + _plain_cmdline: "{{ o2_install_path }}/bin/o2-readout-exe" +control: + mode: direct +wants: + cpu: 0.15 + memory: 128 +bind: + - name: readout + type: push + rateLogging: "{{ fmq_rate_logging }}" + addressing: ipc + transport: shmem +properties: {} +command: + stdout: "{{ log_task_stdout }}" + stderr: "{{ log_task_stderr }}" + shell: true + env: + - O2_DETECTOR={{ detector }} + - O2_PARTITION={{ environment_id }} + user: "{{ user }}" + arguments: + - "{{ readout_cfg_uri }}" + value: "{{ len(modulepath)>0 ? _module_cmdline : _plain_cmdline }}" diff --git a/control-operator/ecs-manifests/control-workflows/stfbuilder-kube-direct.yaml b/control-operator/ecs-manifests/control-workflows/stfbuilder-kube-direct.yaml new file mode 100644 index 000000000..a50ac549b --- /dev/null +++ b/control-operator/ecs-manifests/control-workflows/stfbuilder-kube-direct.yaml @@ -0,0 +1,65 @@ +name: stfbuilder +control: + mode: kubernetes_fairmq +defaults: + detector: TST + rdh_version: "6" + user: flp + fmq_severity: info + dd_detector_subspec: feeid + dd_discovery_endpoint: "no-op://" # location of consul docker instance + log_task_stdout: none + log_task_stderr: none + stfb_dd_mode: "physics" + stfb_fee_mask: "0xffff" + stfb_enable_datasink: "false" + stfb_datasink_dir: "/tmp" + stfb_datasink_filesize: "2048" + monitoring_dd_interval: "5" + _module_cmdline: >- + source /etc/profile.d/modules.sh && MODULEPATH={{ modulepath }} module load DataDistribution Control-OCCPlugin && + StfBuilder + _plain_cmdline: "{{ o2_install_path }}/bin/StfBuilder" +wants: + cpu: 0.15 + memory: 128 +bind: + - name: dpl-chan + type: push + rateLogging: "{{ fmq_rate_logging }}" + transport: shmem + addressing: ipc + sndBufSize: "4" + global: "readout-proxy-{{ it }}" +command: + env: + - O2_INFOLOGGER_MODE={{infologger_mode}} + - O2_DETECTOR={{ detector }} + - DATADIST_FEE_MASK={{ stfb_fee_mask }} + - O2_PARTITION={{ environment_id }} + stdout: "{{ log_task_stdout }}" + stderr: "{{ log_task_stderr }}" + shell: true + user: "{{ user }}" + value: "{{ len(modulepath)>0 ? _module_cmdline : _plain_cmdline }}" + arguments: + - "--session=default" + - "--shm-segment-id=2" + - "--shm-segment-size=33554432" + - "--transport=shmem" + - "--monitoring-backend={{ monitoring_dd_url }}" + - "--monitoring-process-interval={{ monitoring_dd_interval }}" + - "--discovery-partition={{ environment_id }}" + - "--discovery-endpoint={{ dd_discovery_endpoint }}" + - "--detector-rdh={{ rdh_version }}" + - "--detector-subspec={{ dd_detector_subspec }}" + - "--severity={{ fmq_severity }}" + - "--severity-infologger={{ infologger_severity }}" + - "--dpl-channel-name=dpl-chan" + - "--shm-monitor=false" + - "--detector={{ detector }}" + - "--run-type={{ stfb_dd_mode }}" + - "{{ stfb_enable_datasink == 'true' ? '--data-sink-enable' : ' ' }}" + - "{{ stfb_enable_datasink == 'true' ? '--data-sink-dir=' + stfb_datasink_dir : ' ' }}" + - "{{ stfb_enable_datasink == 'true' ? '--data-sink-max-stfs-per-file=0' : ' ' }}" + - "{{ stfb_enable_datasink == 'true' ? '--data-sink-max-file-size=' + stfb_datasink_filesize : ' ' }}" diff --git a/control-operator/ecs-manifests/control-workflows/stfbuilder-senderoutput-kube-direct.yaml b/control-operator/ecs-manifests/control-workflows/stfbuilder-senderoutput-kube-direct.yaml new file mode 100644 index 000000000..d409de28a --- /dev/null +++ b/control-operator/ecs-manifests/control-workflows/stfbuilder-senderoutput-kube-direct.yaml @@ -0,0 +1,64 @@ +name: stfbuilder-senderoutput +control: + mode: kubernetes_fairmq +defaults: + detector: TST + rdh_version: "6" + user: flp + fmq_severity: info + dd_detector_subspec: feeid + dd_discovery_endpoint: "no-op://" # location of consul docker instance + log_task_stdout: none + log_task_stderr: none + stfb_dd_mode: "physics" + stfb_fee_mask: "0xffff" + stfb_enable_datasink: "false" + stfb_datasink_dir: "/tmp" + stfb_datasink_filesize: "2048" + monitoring_dd_interval: "5" + _module_cmdline: >- + source /etc/profile.d/modules.sh && MODULEPATH={{ modulepath }} module load DataDistribution Control-OCCPlugin && + StfBuilder + _plain_cmdline: "{{ o2_install_path }}/bin/StfBuilder" +wants: + cpu: 0.15 + memory: 128 +bind: + - name: buildertosender + type: push + rateLogging: "{{ fmq_rate_logging }}" + transport: shmem + addressing: ipc + sndBufSize: "4" +command: + env: + - O2_INFOLOGGER_MODE={{infologger_mode}} + - O2_DETECTOR={{ detector }} + - DATADIST_FEE_MASK={{ stfb_fee_mask }} + - O2_PARTITION={{ environment_id }} + stdout: "{{ log_task_stdout }}" + stderr: "{{ log_task_stderr }}" + shell: true + user: "{{ user }}" + value: "{{ len(modulepath)>0 ? _module_cmdline : _plain_cmdline }}" + arguments: + - "--session=default" + - "--transport=shmem" + - "--shm-segment-id=2" + - "--shm-segment-size=33554432" + - "--monitoring-backend={{ monitoring_dd_url }}" + - "--monitoring-process-interval={{ monitoring_dd_interval }}" + - "--discovery-partition={{ environment_id }}" + - "--discovery-endpoint={{ dd_discovery_endpoint }}" + - "--detector-rdh={{ rdh_version }}" + - "--detector-subspec={{ dd_detector_subspec }}" + - "--severity={{ fmq_severity }}" + - "--severity-infologger={{ infologger_severity }}" + - "--output-channel-name=buildertosender" + - "--shm-monitor=false" + - "--detector={{ detector }}" + - "--run-type={{ stfb_dd_mode }}" + - "{{ stfb_enable_datasink == 'true' ? '--data-sink-enable' : ' ' }}" + - "{{ stfb_enable_datasink == 'true' ? '--data-sink-dir=' + stfb_datasink_dir : ' ' }}" + - "{{ stfb_enable_datasink == 'true' ? '--data-sink-max-stfs-per-file=0' : ' ' }}" + - "{{ stfb_enable_datasink == 'true' ? '--data-sink-max-file-size=' + stfb_datasink_filesize : ' ' }}" diff --git a/control-operator/ecs-manifests/control-workflows/stfbuilder-senderoutput-kube.yaml b/control-operator/ecs-manifests/control-workflows/stfbuilder-senderoutput-kube.yaml new file mode 100644 index 000000000..9c84f0ed7 --- /dev/null +++ b/control-operator/ecs-manifests/control-workflows/stfbuilder-senderoutput-kube.yaml @@ -0,0 +1,66 @@ +name: stfbuilder-senderoutput +control: + mode: kubectl_fairmq +defaults: + detector: TST + rdh_version: "6" + user: flp + fmq_severity: info + dd_detector_subspec: feeid + dd_discovery_endpoint: "no-op://" # location of consul docker instance + log_task_stdout: none + log_task_stderr: none + stfb_dd_mode: "physics" + stfb_fee_mask: "0xffff" + stfb_enable_datasink: "false" + stfb_datasink_dir: "/tmp" + stfb_datasink_filesize: "2048" + monitoring_dd_interval: "5" + _module_cmdline: >- + source /etc/profile.d/modules.sh && MODULEPATH={{ modulepath }} module load DataDistribution Control-OCCPlugin && + StfBuilder + _plain_cmdline: "{{ o2_install_path }}/bin/StfBuilder" +wants: + cpu: 0.15 + memory: 128 +bind: + - name: buildertosender + type: push + rateLogging: "{{ fmq_rate_logging }}" + transport: shmem + addressing: ipc + sndBufSize: "4" +command: + env: + - O2_INFOLOGGER_MODE={{infologger_mode}} + - O2_DETECTOR={{ detector }} + - DATADIST_FEE_MASK={{ stfb_fee_mask }} + - O2_PARTITION={{ environment_id }} + stdout: "{{ log_task_stdout }}" + stderr: "{{ log_task_stderr }}" + shell: true + user: "{{ user }}" + value: "{{ len(modulepath)>0 ? _module_cmdline : _plain_cmdline }}" + arguments: + # the first argument is the location of yaml readout manifest to be loaded, the one to be used is located in this repo inside control-operator/ecs-manifests/kubernetes-manifests/stfbuilder-senderoutput.yaml + - "/root/stfbuilder-senderoutput.yaml" + - "--session=default" + - "--transport=shmem" + - "--shm-segment-id=2" + - "--shm-segment-size=33554432" + - "--monitoring-backend='{{ monitoring_dd_url }}'" + - "--monitoring-process-interval='{{ monitoring_dd_interval }}'" + - "--discovery-partition={{ environment_id }}" + - "--discovery-endpoint={{ dd_discovery_endpoint }}" + - "--detector-rdh={{ rdh_version }}" + - "--detector-subspec={{ dd_detector_subspec }}" + - "--severity={{ fmq_severity }}" + - "--severity-infologger={{ infologger_severity }}" + - "--output-channel-name=buildertosender" + - "--shm-monitor=false" + - "--detector={{ detector }}" + - "--run-type={{ stfb_dd_mode }}" + - "{{ stfb_enable_datasink == 'true' ? '--data-sink-enable' : ' ' }}" + - "{{ stfb_enable_datasink == 'true' ? '--data-sink-dir=' + stfb_datasink_dir : ' ' }}" + - "{{ stfb_enable_datasink == 'true' ? '--data-sink-max-stfs-per-file=0' : ' ' }}" + - "{{ stfb_enable_datasink == 'true' ? '--data-sink-max-file-size=' + stfb_datasink_filesize : ' ' }}" diff --git a/control-operator/ecs-manifests/control-workflows/stfbuilder-senderoutput-orig.yaml b/control-operator/ecs-manifests/control-workflows/stfbuilder-senderoutput-orig.yaml new file mode 100644 index 000000000..3d33ac31d --- /dev/null +++ b/control-operator/ecs-manifests/control-workflows/stfbuilder-senderoutput-orig.yaml @@ -0,0 +1,64 @@ +name: stfbuilder-senderoutput +control: + mode: fairmq +defaults: + detector: TST + rdh_version: "6" + user: flp + fmq_severity: info + dd_detector_subspec: feeid + dd_discovery_endpoint: "no-op://" # location of consul docker instance + log_task_stdout: none + log_task_stderr: none + stfb_dd_mode: "physics" + stfb_fee_mask: "0xffff" + stfb_enable_datasink: "false" + stfb_datasink_dir: "/tmp" + stfb_datasink_filesize: "2048" + monitoring_dd_interval: "5" + _module_cmdline: >- + source /etc/profile.d/modules.sh && MODULEPATH={{ modulepath }} module load DataDistribution Control-OCCPlugin && + StfBuilder + _plain_cmdline: "{{ o2_install_path }}/bin/StfBuilder" +wants: + cpu: 0.15 + memory: 128 +bind: + - name: buildertosender + type: push + rateLogging: "{{ fmq_rate_logging }}" + transport: shmem + addressing: ipc + sndBufSize: "4" +command: + env: + - O2_INFOLOGGER_MODE={{infologger_mode}} + - O2_DETECTOR={{ detector }} + - DATADIST_FEE_MASK={{ stfb_fee_mask }} + - O2_PARTITION={{ environment_id }} + stdout: "{{ log_task_stdout }}" + stderr: "{{ log_task_stderr }}" + shell: true + user: "{{ user }}" + value: "{{ len(modulepath)>0 ? _module_cmdline : _plain_cmdline }}" + arguments: + - "--session=default" + - "--transport=shmem" + - "--shm-segment-id=2" + - "--shm-segment-size=33554432" + - "--monitoring-backend='{{ monitoring_dd_url }}'" + - "--monitoring-process-interval='{{ monitoring_dd_interval }}'" + - "--discovery-partition={{ environment_id }}" + - "--discovery-endpoint={{ dd_discovery_endpoint }}" + - "--detector-rdh={{ rdh_version }}" + - "--detector-subspec={{ dd_detector_subspec }}" + - "--severity={{ fmq_severity }}" + - "--severity-infologger={{ infologger_severity }}" + - "--output-channel-name=buildertosender" + - "--shm-monitor=false" + - "--detector={{ detector }}" + - "--run-type={{ stfb_dd_mode }}" + - "{{ stfb_enable_datasink == 'true' ? '--data-sink-enable' : ' ' }}" + - "{{ stfb_enable_datasink == 'true' ? '--data-sink-dir=' + stfb_datasink_dir : ' ' }}" + - "{{ stfb_enable_datasink == 'true' ? '--data-sink-max-stfs-per-file=0' : ' ' }}" + - "{{ stfb_enable_datasink == 'true' ? '--data-sink-max-file-size=' + stfb_datasink_filesize : ' ' }}" diff --git a/control-operator/ecs-manifests/control-workflows/stfsender-docker.yaml b/control-operator/ecs-manifests/control-workflows/stfsender-docker.yaml new file mode 100644 index 000000000..a8f67ba33 --- /dev/null +++ b/control-operator/ecs-manifests/control-workflows/stfsender-docker.yaml @@ -0,0 +1,65 @@ +name: stfsender +control: + mode: fairmq +wants: + cpu: 0.15 + memory: 128 +defaults: + max_buffered_stfs: "-1" + dd_discovery_net_if: "ib0" #the loopback intf + dd_discovery_endpoint: "no-op://" # location of consul docker instance + user: flp + fmq_severity: info + log_task_stdout: none + log_task_stderr: none + stfs_dd_region_size: 4096 + stfs_shm_segment_size: 33554432 + stfs_enable_datasink: "false" + stfs_datasink_dir: "/tmp" + stfs_datasink_filesize: "2048" + monitoring_dd_interval: "5" + _module_cmdline: >- + source /etc/profile.d/modules.sh && MODULEPATH={{ modulepath }} module load DataDistribution Control-OCCPlugin && + numactl --cpunodebind=0 --preferred=0 -- StfSender + #_plain_cmdline: "numactl --cpunodebind=0 --preferred=0 -- {{ o2_install_path }}/bin/StfSender" + _plain_cmdline: "sudo -E docker run --name stfsender --replace --privileged --user flp -v /etc/group:/etc/group:ro -v /etc/passwd:/etc/passwd:ro -v /tmp:/tmp -v /lib/modules/$(uname -r):/lib/modules/$(uname -r) --network=host --ipc=host -e CONTROL_OCCPLUGIN_ROOT=/opt/o2 -e O2_INFOLOGGER_MODE -e UCX_TLS -e UCX_IB_SL -e UCX_DC_MAX_NUM_EPS -e UCX_RC_MAX_NUM_EPS -e http_proxy -e https_proxy -e UCX_NET_DEVICES -e O2_DETECTOR -e O2_PARTITION -e OCC_CONTROL_PORT -e O2_SYSTEM -e O2_ROLE gitlab-registry.cern.ch/aliceo2group/dockerfiles/alma9-flp-node/dd:1 numactl --cpunodebind=0 --preferred=0 -- {{ o2_install_path }}/bin/StfSender" +command: + env: + - O2_INFOLOGGER_MODE={{infologger_mode}} + - http_proxy="" + - https_proxy="" + - no_proxy=-ib,.internal + - O2_DETECTOR={{ detector }} + - UCX_NET_DEVICES=mlx5_0:1 # This limits StfSender to IB interface (used as of DD v1.3.0) + - UCX_TLS=sm,self,dc,rc # Force dc/rc connection (used as of DD v1.4.0) + - UCX_IB_SL=1 # Force IB SL1 with Adaptive Routing (AR) + - UCX_DC_MAX_NUM_EPS=512 # Allow 512 peers for DC transport + - UCX_RC_MAX_NUM_EPS=512 # Allow 512 peers for RC transport + - O2_PARTITION={{ environment_id }} + stdout: "{{ log_task_stdout }}" + stderr: "{{ log_task_stderr }}" + shell: true + user: "{{ user }}" + value: "{{ len(modulepath)>0 ? _module_cmdline : _plain_cmdline }}" + arguments: + - "--session=default" + - "--shm-segment-id=2" + - "--shm-segment-size={{ stfs_shm_segment_size }}" + - "--dd-region-size={{ stfs_dd_region_size }}" + - "--dd-region-id=3536" + - "--transport=shmem" + - "--input-channel-name={{ stfs_input_channel_name }}" + - "--severity={{ fmq_severity }}" + - "--severity-infologger={{ infologger_severity }}" + - "--monitoring-backend='{{ monitoring_dd_url }}'" + - "--monitoring-process-interval='{{ monitoring_dd_interval }}'" + - "--discovery-partition={{ environment_id }}" + - "--discovery-id={{ dd_discovery_stfs_id }}" + - "--discovery-endpoint={{ dd_discovery_endpoint }}" + - "--discovery-net-if={{ dd_discovery_net_if }}" + - '{{ ddsched_enabled == "true" ? "" : "--stand-alone" }}' + - "--shm-monitor=false" + - "{{ stfs_enable_datasink == 'true' ? '--data-sink-enable' : ' ' }}" + - "{{ stfs_enable_datasink == 'true' ? '--data-sink-dir=' + stfs_datasink_dir : ' ' }}" + - "{{ stfs_enable_datasink == 'true' ? '--data-sink-max-stfs-per-file=0' : ' ' }}" + - "{{ stfs_enable_datasink == 'true' ? '--data-sink-max-file-size=' + stfs_datasink_filesize : ' ' }}" diff --git a/control-operator/ecs-manifests/control-workflows/stfsender-kube-direct.yaml b/control-operator/ecs-manifests/control-workflows/stfsender-kube-direct.yaml new file mode 100644 index 000000000..6ed26731c --- /dev/null +++ b/control-operator/ecs-manifests/control-workflows/stfsender-kube-direct.yaml @@ -0,0 +1,68 @@ +name: stfsender +control: + mode: kubernetes_fairmq +wants: + cpu: 0.15 + memory: 128 +defaults: + max_buffered_stfs: "-1" + dd_discovery_net_if: "ib0" #the loopback intf + dd_discovery_endpoint: "no-op://" # location of consul docker instance + user: flp + fmq_severity: info + log_task_stdout: none + log_task_stderr: none + stfs_dd_region_size: 4096 + stfs_shm_segment_size: 33554432 + stfs_enable_datasink: "false" + stfs_datasink_dir: "/tmp" + stfs_datasink_filesize: "2048" + monitoring_dd_interval: "5" + _module_cmdline: >- + source /etc/profile.d/modules.sh && MODULEPATH={{ modulepath }} module load DataDistribution Control-OCCPlugin && + numactl --cpunodebind=0 --preferred=0 -- StfSender + _plain_cmdline: "numactl" +command: + env: + - O2_INFOLOGGER_MODE={{infologger_mode}} + - http_proxy="" + - https_proxy="" + - no_proxy=-ib,.internal + - O2_DETECTOR={{ detector }} + - UCX_NET_DEVICES=mlx5_0:1 # This limits StfSender to IB interface (used as of DD v1.3.0) + - UCX_TLS=sm,self,dc,rc # Force dc/rc connection (used as of DD v1.4.0) + - UCX_IB_SL=1 # Force IB SL1 with Adaptive Routing (AR) + - UCX_DC_MAX_NUM_EPS=512 # Allow 512 peers for DC transport + - UCX_RC_MAX_NUM_EPS=512 # Allow 512 peers for RC transport + - O2_PARTITION={{ environment_id }} + stdout: "{{ log_task_stdout }}" + stderr: "{{ log_task_stderr }}" + shell: true + user: "{{ user }}" + value: "{{ len(modulepath)>0 ? _module_cmdline : _plain_cmdline }}" + arguments: + - "--cpunodebind=0" + - "--preferred=0" + - "--" + - "{{ o2_install_path }}/bin/StfSender" + - "--session=default" + - "--shm-segment-id=2" + - "--shm-segment-size={{ stfs_shm_segment_size }}" + - "--dd-region-size={{ stfs_dd_region_size }}" + - "--dd-region-id=3536" + - "--transport=shmem" + - "--input-channel-name={{ stfs_input_channel_name }}" + - "--severity={{ fmq_severity }}" + - "--severity-infologger={{ infologger_severity }}" + - "--monitoring-backend={{ monitoring_dd_url }}" + - "--monitoring-process-interval={{ monitoring_dd_interval }}" + - "--discovery-partition={{ environment_id }}" + - "--discovery-id={{ dd_discovery_stfs_id }}" + - "--discovery-endpoint={{ dd_discovery_endpoint }}" + - "--discovery-net-if={{ dd_discovery_net_if }}" + - '{{ ddsched_enabled == "true" ? "" : "--stand-alone" }}' + - "--shm-monitor=false" + - "{{ stfs_enable_datasink == 'true' ? '--data-sink-enable' : ' ' }}" + - "{{ stfs_enable_datasink == 'true' ? '--data-sink-dir=' + stfs_datasink_dir : ' ' }}" + - "{{ stfs_enable_datasink == 'true' ? '--data-sink-max-stfs-per-file=0' : ' ' }}" + - "{{ stfs_enable_datasink == 'true' ? '--data-sink-max-file-size=' + stfs_datasink_filesize : ' ' }}" diff --git a/control-operator/ecs-manifests/control-workflows/stfsender-kube.yaml b/control-operator/ecs-manifests/control-workflows/stfsender-kube.yaml new file mode 100644 index 000000000..58a6106b6 --- /dev/null +++ b/control-operator/ecs-manifests/control-workflows/stfsender-kube.yaml @@ -0,0 +1,66 @@ +name: stfsender +control: + mode: kubectl_fairmq +wants: + cpu: 0.15 + memory: 128 +defaults: + max_buffered_stfs: "-1" + dd_discovery_net_if: "ib0" #the loopback intf + dd_discovery_endpoint: "no-op://" # location of consul docker instance + user: flp + fmq_severity: info + log_task_stdout: none + log_task_stderr: none + stfs_dd_region_size: 4096 + stfs_shm_segment_size: 33554432 + stfs_enable_datasink: "false" + stfs_datasink_dir: "/tmp" + stfs_datasink_filesize: "2048" + monitoring_dd_interval: "5" + _module_cmdline: >- + source /etc/profile.d/modules.sh && MODULEPATH={{ modulepath }} module load DataDistribution Control-OCCPlugin && + numactl --cpunodebind=0 --preferred=0 -- StfSender + _plain_cmdline: "numactl --cpunodebind=0 --preferred=0 -- {{ o2_install_path }}/bin/StfSender" +command: + env: + - O2_INFOLOGGER_MODE={{infologger_mode}} + - http_proxy= + - https_proxy= + - no_proxy=-ib,.internal + - O2_DETECTOR={{ detector }} + - UCX_NET_DEVICES=mlx5_0:1 # This limits StfSender to IB interface (used as of DD v1.3.0) + - UCX_TLS=sm,self,dc,rc # Force dc/rc connection (used as of DD v1.4.0) + - UCX_IB_SL=1 # Force IB SL1 with Adaptive Routing (AR) + - UCX_DC_MAX_NUM_EPS=512 # Allow 512 peers for DC transport + - UCX_RC_MAX_NUM_EPS=512 # Allow 512 peers for RC transport + - O2_PARTITION={{ environment_id }} + stdout: "{{ log_task_stdout }}" + stderr: "{{ log_task_stderr }}" + shell: true + user: "{{ user }}" + value: "{{ len(modulepath)>0 ? _module_cmdline : _plain_cmdline }}" + arguments: + # the first argument is the location of yaml readout manifest to be loaded, the one to be used is located in this repo inside control-operator/ecs-manifests/kubernetes-manifests/stfsender.yaml + - "/root/stfsender.yaml" + - "--session=default" + - "--shm-segment-id=2" + - "--shm-segment-size={{ stfs_shm_segment_size }}" + - "--dd-region-size={{ stfs_dd_region_size }}" + - "--dd-region-id=3536" + - "--transport=shmem" + - "--input-channel-name={{ stfs_input_channel_name }}" + - "--severity={{ fmq_severity }}" + - "--severity-infologger={{ infologger_severity }}" + - "--monitoring-backend='{{ monitoring_dd_url }}'" + - "--monitoring-process-interval='{{ monitoring_dd_interval }}'" + - "--discovery-partition={{ environment_id }}" + - "--discovery-id={{ dd_discovery_stfs_id }}" + - "--discovery-endpoint={{ dd_discovery_endpoint }}" + - "--discovery-net-if={{ dd_discovery_net_if }}" + - '{{ ddsched_enabled == "true" ? "" : "--stand-alone" }}' + - "--shm-monitor=false" + - "{{ stfs_enable_datasink == 'true' ? '--data-sink-enable' : ' ' }}" + - "{{ stfs_enable_datasink == 'true' ? '--data-sink-dir=' + stfs_datasink_dir : ' ' }}" + - "{{ stfs_enable_datasink == 'true' ? '--data-sink-max-stfs-per-file=0' : ' ' }}" + - "{{ stfs_enable_datasink == 'true' ? '--data-sink-max-file-size=' + stfs_datasink_filesize : ' ' }}" diff --git a/control-operator/ecs-manifests/control-workflows/stfsender-orig.yaml b/control-operator/ecs-manifests/control-workflows/stfsender-orig.yaml new file mode 100644 index 000000000..7bb9ecd23 --- /dev/null +++ b/control-operator/ecs-manifests/control-workflows/stfsender-orig.yaml @@ -0,0 +1,64 @@ +name: stfsender +control: + mode: fairmq +wants: + cpu: 0.15 + memory: 128 +defaults: + max_buffered_stfs: "-1" + dd_discovery_net_if: "ib0" #the loopback intf + dd_discovery_endpoint: "no-op://" # location of consul docker instance + user: flp + fmq_severity: info + log_task_stdout: none + log_task_stderr: none + stfs_dd_region_size: 4096 + stfs_shm_segment_size: 33554432 + stfs_enable_datasink: "false" + stfs_datasink_dir: "/tmp" + stfs_datasink_filesize: "2048" + monitoring_dd_interval: "5" + _module_cmdline: >- + source /etc/profile.d/modules.sh && MODULEPATH={{ modulepath }} module load DataDistribution Control-OCCPlugin && + numactl --cpunodebind=0 --preferred=0 -- StfSender + _plain_cmdline: "numactl --cpunodebind=0 --preferred=0 -- {{ o2_install_path }}/bin/StfSender" +command: + env: + - O2_INFOLOGGER_MODE={{infologger_mode}} + - http_proxy="" + - https_proxy="" + - no_proxy=-ib,.internal + - O2_DETECTOR={{ detector }} + - UCX_NET_DEVICES=mlx5_0:1 # This limits StfSender to IB interface (used as of DD v1.3.0) + - UCX_TLS=sm,self,dc,rc # Force dc/rc connection (used as of DD v1.4.0) + - UCX_IB_SL=1 # Force IB SL1 with Adaptive Routing (AR) + - UCX_DC_MAX_NUM_EPS=512 # Allow 512 peers for DC transport + - UCX_RC_MAX_NUM_EPS=512 # Allow 512 peers for RC transport + - O2_PARTITION={{ environment_id }} + stdout: "{{ log_task_stdout }}" + stderr: "{{ log_task_stderr }}" + shell: true + user: "{{ user }}" + value: "{{ len(modulepath)>0 ? _module_cmdline : _plain_cmdline }}" + arguments: + - "--session=default" + - "--shm-segment-id=2" + - "--shm-segment-size={{ stfs_shm_segment_size }}" + - "--dd-region-size={{ stfs_dd_region_size }}" + - "--dd-region-id=3536" + - "--transport=shmem" + - "--input-channel-name={{ stfs_input_channel_name }}" + - "--severity={{ fmq_severity }}" + - "--severity-infologger={{ infologger_severity }}" + - "--monitoring-backend='{{ monitoring_dd_url }}'" + - "--monitoring-process-interval='{{ monitoring_dd_interval }}'" + - "--discovery-partition={{ environment_id }}" + - "--discovery-id={{ dd_discovery_stfs_id }}" + - "--discovery-endpoint={{ dd_discovery_endpoint }}" + - "--discovery-net-if={{ dd_discovery_net_if }}" + - '{{ ddsched_enabled == "true" ? "" : "--stand-alone" }}' + - "--shm-monitor=false" + - "{{ stfs_enable_datasink == 'true' ? '--data-sink-enable' : ' ' }}" + - "{{ stfs_enable_datasink == 'true' ? '--data-sink-dir=' + stfs_datasink_dir : ' ' }}" + - "{{ stfs_enable_datasink == 'true' ? '--data-sink-max-stfs-per-file=0' : ' ' }}" + - "{{ stfs_enable_datasink == 'true' ? '--data-sink-max-file-size=' + stfs_datasink_filesize : ' ' }}" diff --git a/control-operator/ecs-manifests/environment/test_env.yaml b/control-operator/ecs-manifests/environment/test_env.yaml new file mode 100644 index 000000000..8e0f94821 --- /dev/null +++ b/control-operator/ecs-manifests/environment/test_env.yaml @@ -0,0 +1,266 @@ +apiVersion: aliecs.alice.cern/v1alpha1 +kind: Environment +metadata: + name: 31jdwll4xl6 + namespace: alice-tasks + +taskTemplates: + tasks: + mtichak-ost: + # mtichak-xps-cern: + # - name: readout + # argsCLI: + # - consul-ini://mtichak-ost.cern.ch:8500/o2/components/readout/ANY/any/readout-stfb-mtichak-ost + # argsTransition: + # chans.readout.0.address: ipc://@o2ipc-d73tvau8ndm8ddkd94og + # chans.readout.0.autoBind: "0" + # chans.readout.0.method: bind + # chans.readout.0.rateLogging: "0" + # chans.readout.0.rcvBufSize: "1000" + # chans.readout.0.rcvKernelSize: "0" + # chans.readout.0.sndBufSize: "1000" + # chans.readout.0.sndKernelSize: "0" + # chans.readout.0.transport: shmem + # chans.readout.0.type: push + # chans.readout.numSockets: "1" + # environment_id: 31jdWLL4XL6 + # Orbit-Reset-Time: "" + # env: + # - name: O2_DETECTOR + # value: TST + # - name: O2_PARTITION + # value: 31jdWLL4XL6 + # - name: OCC_CONTROL_PORT + # value: "31000" + # - name: O2_SYSTEM + # value: FLP + # - name: O2_ROLE + # value: mtichak-ost + + - name: stfbuilder-senderoutput + argsCLI: + - --session=default + - --transport=shmem + - --shm-segment-id=2 + - --shm-segment-size=33554432 + - --monitoring-backend=influxdb-unix:///tmp/telegraf.sock + - --monitoring-process-interval=5 + - --discovery-partition=31jdWLL4XL6 + - --discovery-endpoint=no-op:// + - --detector-rdh=6 + - --detector-subspec=feeid + - --severity=info + - --severity-infologger=info + - --output-channel-name=buildertosender + - --shm-monitor=false + - --detector=TST + - --run-type=physics + - --id + - 31jdWLqicSv + - -S + - $(CONTROL_OCCPLUGIN_ROOT)/lib/ + - -P + - OCClite + - --color + - "false" + - --control-port + - "31001" + argsTransition: + chans.buildertosender.0.address: ipc://@o2ipc-d73tvau8ndm8ddkd94p0 + chans.buildertosender.0.autoBind: "0" + chans.buildertosender.0.method: bind + chans.buildertosender.0.rateLogging: "0" + chans.buildertosender.0.rcvBufSize: "1000" + chans.buildertosender.0.rcvKernelSize: "0" + chans.buildertosender.0.sndBufSize: "4" + chans.buildertosender.0.sndKernelSize: "0" + chans.buildertosender.0.transport: shmem + chans.buildertosender.0.type: push + chans.buildertosender.numSockets: "1" + chans.readout.0.address: ipc://@o2ipc-d73tvau8ndm8ddkd94og + chans.readout.0.method: connect + chans.readout.0.rateLogging: "0" + chans.readout.0.rcvBufSize: "1000" + chans.readout.0.rcvKernelSize: "0" + chans.readout.0.sndBufSize: "1000" + chans.readout.0.sndKernelSize: "0" + chans.readout.0.transport: shmem + chans.readout.0.type: pull + chans.readout.numSockets: "1" + environment_id: 31jdWLL4XL6 + Orbit-Reset-Time: "" + env: + - name: O2_DETECTOR + value: TST + - name: O2_PARTITION + value: 31jdWLL4XL6 + - name: OCC_CONTROL_PORT + value: "31001" + - name: O2_SYSTEM + value: FLP + - name: O2_ROLE + value: mtichak-ost + - name: O2_INFOLOGGER_MODE + value: infoLoggerD + - name: DATADIST_FEE_MASK + value: "0xffff" + - name: CONTROL_OCCPLUGIN_ROOT + value: /opt/o2 + + - name: stfsender + argsCLI: + - --cpunodebind=0 + - --preferred=0 + - -- + - /opt/o2/bin/StfSender + - --session=default + - --shm-segment-id=2 + - --shm-segment-size=33554432 + - --dd-region-size=4096 + - --dd-region-id=3536 + - --transport=shmem + - --input-channel-name=buildertosender + - --severity=info + - --severity-infologger=info + - --monitoring-backend=influxdb-unix:///tmp/telegraf.sock + - --monitoring-process-interval=5 + - --discovery-partition=31jdWLL4XL6 + - --discovery-id=stfs-mtichak-ost-31jdWLTUTjp + - --discovery-endpoint=no-op:// + - --discovery-net-if=ib0 + - --stand-alone + - --shm-monitor=false + - --id + - 31jdWLsCGFL + - -S + - $(CONTROL_OCCPLUGIN_ROOT)/lib/ + - -P + - OCClite + - --color + - "false" + - --control-port + - "31002" + argsTransition: + chans.buildertosender.0.address: ipc://@o2ipc-d73tvau8ndm8ddkd94p0 + chans.buildertosender.0.method: connect + chans.buildertosender.0.rateLogging: "0" + chans.buildertosender.0.rcvBufSize: "1000" + chans.buildertosender.0.rcvKernelSize: "0" + chans.buildertosender.0.sndBufSize: "1000" + chans.buildertosender.0.sndKernelSize: "0" + chans.buildertosender.0.transport: shmem + chans.buildertosender.0.type: pull + chans.buildertosender.numSockets: "1" + environment_id: 31jdWLL4XL6 + Orbit-Reset-Time: "" + env: + - name: O2_DETECTOR + value: TST + - name: O2_PARTITION + value: 31jdWLL4XL6 + - name: OCC_CONTROL_PORT + value: "31002" + - name: O2_SYSTEM + value: FLP + - name: O2_ROLE + value: mtichak-ost + - name: O2_INFOLOGGER_MODE + value: infoLoggerD + - name: http_proxy + value: "" + - name: https_proxy + value: "" + - name: no_proxy + value: -ib,.internal + - name: UCX_NET_DEVICES + value: mlx5_0:1 + - name: UCX_TLS + value: sm,self,dc,rc + - name: UCX_IB_SL + value: "1" + - name: UCX_DC_MAX_NUM_EPS + value: "512" + - name: UCX_RC_MAX_NUM_EPS + value: "512" + - name: CONTROL_OCCPLUGIN_ROOT + value: /opt/o2 + +spec: + state: standby + tasks: + # mtichak-ost.cern.ch: + mtichak-ost: + - name: readout + spec: + pod: + hostNetwork: true + hostIPC: true + containers: + - name: readout + image: gitlab-registry.cern.ch/aliceo2group/dockerfiles/alma9-flp-node/readout:1 + command: ["/opt/o2/bin/o2-readout-exe"] + args: + - consul-ini://mtichak-ost.cern.ch:8500/o2/components/readout/ANY/any/readout-stfb-mtichak-ost + env: + - name: O2_DETECTOR + value: TST + - name: O2_PARTITION + value: 31jdWLL4XL6 + - name: OCC_CONTROL_PORT + value: "31000" + - name: O2_SYSTEM + value: FLP + - name: O2_ROLE + value: mtichak-ost + securityContext: + privileged: true + volumeMounts: + - name: host-shm + mountPath: /dev/shm + - name: group + mountPath: /etc/group + readOnly: true + - name: passwd + mountPath: /etc/passwd + readOnly: true + - name: tmp + mountPath: /tmp + - name: modules + mountPath: /lib/modules + imagePullPolicy: IfNotPresent + volumes: + - name: host-shm + hostPath: + path: /dev/shm + type: Directory + - name: group + hostPath: + path: /etc/group + - name: passwd + hostPath: + path: /etc/passwd + - name: tmp + hostPath: + path: /tmp + - name: modules + hostPath: + path: /lib/modules + imagePullSecrets: + - name: gitlab-registry-secret + control: + mode: "direct" + port: 31000 + arguments: + chans.readout.0.address: ipc://@o2ipc-d73tvau8ndm8ddkd94og + chans.readout.0.autoBind: "0" + chans.readout.0.method: bind + chans.readout.0.rateLogging: "0" + chans.readout.0.rcvBufSize: "1000" + chans.readout.0.rcvKernelSize: "0" + chans.readout.0.sndBufSize: "1000" + chans.readout.0.sndKernelSize: "0" + chans.readout.0.transport: shmem + chans.readout.0.type: push + chans.readout.numSockets: "1" + environment_id: 31jdWLL4XL6 + Orbit-Reset-Time: "" diff --git a/control-operator/ecs-manifests/kubernetes-manifests/dpl-qccheck-test.yaml b/control-operator/ecs-manifests/kubernetes-manifests/dpl-qccheck-test.yaml new file mode 100644 index 000000000..69a16845d --- /dev/null +++ b/control-operator/ecs-manifests/kubernetes-manifests/dpl-qccheck-test.yaml @@ -0,0 +1,110 @@ +apiVersion: aliecs.alice.cern/v1alpha1 +kind: Task +metadata: + labels: + environment: 34xmvqdqcsu + node: mtichak-ost + taskID: 34XmVrU4GeE + name: mtichak-ost-dpl-qc-check-daq-qccheck + namespace: alice-tasks +spec: + control: + mode: fairmq + port: 31000 + nodeName: mtichak-ost.cern.ch + pod: + containers: + - args: + - source /etc/profile.d/o2.sh && o2-dpl-raw-proxy -b --session default --dataspec + 'x:TST/RAWDATA;dd:FLP/DISTSUBTIMEFRAME/0' --inject-missing-data --readout-proxy + '--channel-config "name=readout-proxy,type=pull,method=connect,address=ipc:///tmp/stf-builder-dpl-pipe-0,transport=shmem,rateLogging=10"' + | o2-qc -b --config apricot://mtichak-ost.cern.ch:32188/o2/components/qc/ANY/any/stfb_to_daqtask-mtichak-ost + | o2-dpl-output-proxy --environment DPL_OUTPUT_PROXY_ORDERED=1 -b --session + default --dataspec 'x:TST/RAWDATA;dd:FLP/DISTSUBTIMEFRAME/0' --dpl-output-proxy + '--channel-config "name=downstream,type=push,method=bind,address=ipc:///tmp/stf-pipe-0,rateLogging=10,transport=shmem"' + | o2-qc -b --exit-transition-timeout '40' --data-processing-timeout '20' --monitoring-backend + 'influxdb-unix:///tmp/telegraf-dpl.sock' --session 'default' --infologger-severity + 'info' --infologger-mode 'infoLoggerD' --driver-client-backend 'stdout://' + --shm-segment-size '10000000000' --shm-throw-bad-alloc 'false' --resources-monitoring + '15' --id 'qc-check-DAQ-QcCheck' --shm-monitor 'false' --log-color 'false' + --batch --aod-file '' --aod-memory-rate-limit '0' --aod-writer-json '' --aod-writer-keep + '' --aod-writer-maxfilesize '0' --aod-writer-ntfmerge -1 --aod-writer-resdir + '' --aod-writer-resfile '' --aod-writer-resmode 'RECREATE' --bad-alloc-attempt-interval + '50' --bad-alloc-max-attempts '1' --channel-prefix '' --clone '' --config + 'apricot://mtichak-ost.cern.ch:32188/o2/components/qc/ANY/any/stfb_to_daqtask-mtichak-ost' + --configKeyValues '' --early-forward-policy 'never' --fairmq-ipc-prefix '@' + --fairmq-rate-logging '0' --fairmq-recv-buffer-size '4' --fairmq-send-buffer-size + '4' --forwarding-destination 'drop' --forwarding-policy 'dangling' --host + '' --io-threads '1' --labels '' --local-batch '' --override-values '' --pipeline + '' --readers '1' --remote-batch '' --severity 'info' --shm-allocation 'rbtree_best_fit' + --shm-metadata-msg-size '0' --shm-mlock-segment 'false' --shm-mlock-segment-on-creation + 'false' --shm-no-cleanup 'false' --shm-segment-id '0' --shm-zero-segment 'false' + --signposts '' --spawners '1' --stacktrace-on-signal 'simple' --timeframes-rate-limit + '0' --timeframes-rate-limit-ipcid -1 --workflow-suffix '' --qcConfiguration + '{}' --runNumber '' -S $CONTROL_OCCPLUGIN_ROOT/lib/ -P OCClite --color false + --control-port 31000 + command: + - bash + - -c + env: + - name: O2_DETECTOR + value: TST + - name: O2_PARTITION + value: 34XmVqDQCsU + - name: HOME + value: /tmp + - name: O2_SYSTEM + value: FLP + - name: O2_ROLE + value: mtichak-ost + - name: OCC_CONTROL_PORT + value: "31000" + image: gitlab-registry.cern.ch/pkonopka/dockerfiles/flp-sw:20260729-1 + imagePullPolicy: IfNotPresent + name: dpl + resources: {} + securityContext: + privileged: true + runAsGroup: 1100 + runAsUser: 1100 + volumeMounts: + - mountPath: /dev/shm + name: host-shm + - mountPath: /etc/group + name: group + readOnly: true + - mountPath: /etc/passwd + name: passwd + readOnly: true + - mountPath: /tmp + name: tmp + - mountPath: /lib/modules + name: modules + hostIPC: true + hostNetwork: true + imagePullSecrets: + - name: gitlab-registry-secret + nodeName: mtichak-ost.cern.ch + securityContext: + fsGroup: 1100 + supplementalGroups: + - 10 + - 1105 + volumes: + - hostPath: + path: /dev/shm + type: Directory + name: host-shm + - hostPath: + path: /etc/group + name: group + - hostPath: + path: /etc/passwd + name: passwd + - hostPath: + path: /tmp + name: tmp + - hostPath: + path: /lib/modules + name: modules + state: standby diff --git a/control-operator/ecs-manifests/kubernetes-manifests/readout-test.yaml b/control-operator/ecs-manifests/kubernetes-manifests/readout-test.yaml new file mode 100644 index 000000000..d77933ff1 --- /dev/null +++ b/control-operator/ecs-manifests/kubernetes-manifests/readout-test.yaml @@ -0,0 +1,182 @@ +apiVersion: aliecs.alice.cern/v1alpha1 +kind: Task +metadata: + name: readout + namespace: alice-tasks +spec: + nodeName: mtichak-ost.cern.ch + arguments: + chans.readout.0.address: ipc://@o2ipc-d7ef3du8ndmd7n8j7l2g + chans.readout.0.autoBind: "0" + chans.readout.0.method: bind + chans.readout.0.rateLogging: "0" + chans.readout.0.rcvBufSize: "1000" + chans.readout.0.rcvKernelSize: "0" + chans.readout.0.sndBufSize: "1000" + chans.readout.0.sndKernelSize: "0" + chans.readout.0.transport: shmem + chans.readout.0.type: push + chans.readout.numSockets: "1" + environment_id: 323ikoYJ88i + orbit-reset-time: "" + pod: + hostNetwork: true + hostIPC: true + securityContext: + fsGroup: 1100 + supplementalGroups: [10, 1105] + containers: + - name: readout + image: gitlab-registry.cern.ch/aliceo2group/dockerfiles/alma9-flp-node/readout:1 + command: ["/opt/o2/bin/o2-readout-exe"] + args: ["/etc/readout/readout-cfg.ini"] + securityContext: + privileged: true + runAsUser: 1100 + runAsGroup: 1100 + env: + - name: O2_DETECTOR + value: "TST" + - name: O2_PARTITION + value: "323ikoYJ88i" + - name: OCC_CONTROL_PORT + value: "31000" + - name: O2_SYSTEM + value: "flp" + # it might be necessary to change the role for your use case + - name: O2_ROLE + value: "mtichak" + volumeMounts: + - name: group + mountPath: /etc/group + readOnly: true + - name: passwd + mountPath: /etc/passwd + readOnly: true + - name: tmp + mountPath: /tmp + - name: modules + mountPath: /lib/modules + - name: readout-config + mountPath: /etc/readout + readOnly: true + imagePullPolicy: IfNotPresent + volumes: + - name: group + hostPath: + path: /etc/group + - name: passwd + hostPath: + path: /etc/passwd + - name: tmp + hostPath: + path: /tmp + - name: modules + hostPath: + path: /lib/modules + - name: readout-config + configMap: + name: readout-config + imagePullSecrets: + - name: gitlab-registry-secret + control: + mode: "direct" + port: 31000 + bind: + - name: readout + type: push + rateLogging: "0" + addressing: ipc + transport: shmem + global: "" + target: "" + connect: + properties: + var1: value1 + var2: value2 + state: standby + +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: readout-config + namespace: alice-tasks +data: + readout-cfg.ini: | + # Example configuration file for o2-readout-exe + # Standalone operation, with emulated input, and output to Data Distribution + # to run readout-stbf out of the box with data emulator + + [readout] + aggregatorSliceTimeout=0.5 + aggregatorStfTimeout=1 + + # dummy data source + [equipment-emulator-1] + enabled=1 + equipmentType=cruEmulator + memoryPoolNumberOfPages=1000 + memoryPoolPageSize=1M + numberOfLinks=1 + PayloadSize=8000 + rdhUseFirstInPageEnabled=1 + #systemId=19 + #feeId=0 + #cruId=0 + #dpwId=0 + id=1 + + # define a (disabled) CRU equipment for CRU end point #0 + [equipment-rorc-1] + enabled=0 + equipmentType=rorc + cardId=#0 + dataSource=Ddg + memoryPoolNumberOfPages=1800 + memoryPoolPageSize=1M + rdhUseFirstInPageEnabled=1 + + + # monitor counters + [consumer-stats] + consumerType=stats + monitoringEnabled=1 + monitoringUpdatePeriod=5 + processMonitoringInterval=15 + monitoringURI=influxdb-unix:///tmp/telegraf.sock + + + # record data to file (disabled) + [consumer-rec] + enabled=0 + consumerType=fileRecorder + fileName=/tmp/data.raw + + + # allow data sampling to take data + [consumer-data-sampling] + enabled=0 + consumerType=DataSampling + + + # send data to stfb + [consumer-StfBuilder] + enabled = 1 + consumerType = FairMQChannel + sessionName = default + fmq-transport = shmem + fmq-name = readout + fmq-type = push + fmq-address = ipc:///tmp/flp-readout-pipe-0 + unmanagedMemorySize = 2G + memoryPoolNumberOfPages = 200 + memoryPoolPageSize = 1M + disableSending=0 + + + # matching config for the test receiver + [receiver-fmq] + decodingMode=stfHbf + channelAddress=ipc:///tmp/flp-readout-pipe-0 + channelType=pull diff --git a/control-operator/ecs-manifests/kubernetes-manifests/readout.yaml b/control-operator/ecs-manifests/kubernetes-manifests/readout.yaml new file mode 100644 index 000000000..90d7600fe --- /dev/null +++ b/control-operator/ecs-manifests/kubernetes-manifests/readout.yaml @@ -0,0 +1,82 @@ +apiVersion: aliecs.alice.cern/v1alpha1 +kind: Task +metadata: + name: readout +spec: + pod: + hostNetwork: true + hostIPC: true + securityContext: + fsGroup: ${FLP_GID} + supplementalGroups: ${FLP_SUPPLEMENTAL_GROUPS} + containers: + - name: readout + image: gitlab-registry.cern.ch/aliceo2group/dockerfiles/alma9-flp-node/readout:1 + command: ["/bin/bash"] + args: + - "-c" + - "${KUBE_COMMAND} ${KUBE_ARGUMENTS}" + securityContext: + privileged: true + runAsUser: ${FLP_UID} + runAsGroup: ${FLP_GID} + env: + - name: O2_DETECTOR + value: "${O2_DETECTOR}" + - name: O2_PARTITION + value: "${O2_PARTITION}" + - name: OCC_CONTROL_PORT + value: "${OCC_CONTROL_PORT}" + - name: O2_SYSTEM + value: "${O2_SYSTEM}" + - name: O2_ROLE + value: "${O2_ROLE}" + volumeMounts: + - name: host-shm + mountPath: /dev/shm + - name: group + mountPath: /etc/group + readOnly: true + - name: passwd + mountPath: /etc/passwd + readOnly: true + - name: tmp + mountPath: /tmp + - name: modules + mountPath: /lib/modules + imagePullPolicy: IfNotPresent + volumes: + - name: host-shm + hostPath: + path: /dev/shm + type: Directory + - name: group + hostPath: + path: /etc/group + - name: passwd + hostPath: + path: /etc/passwd + - name: tmp + hostPath: + path: /tmp + - name: modules + hostPath: + path: /lib/modules + imagePullSecrets: + - name: gitlab-registry-secret + control: + mode: "direct" + port: ${OCC_CONTROL_PORT} + bind: + - name: readout + type: push + rateLogging: "0" + addressing: ipc + transport: shmem + global: "" + target: "" + connect: + properties: + var1: value1 + var2: value2 + state: standby diff --git a/control-operator/ecs-manifests/kubernetes-manifests/stfbuilder-senderoutput-test.yaml b/control-operator/ecs-manifests/kubernetes-manifests/stfbuilder-senderoutput-test.yaml new file mode 100644 index 000000000..982d0ea43 --- /dev/null +++ b/control-operator/ecs-manifests/kubernetes-manifests/stfbuilder-senderoutput-test.yaml @@ -0,0 +1,116 @@ +apiVersion: aliecs.alice.cern/v1alpha1 +kind: Task +metadata: + name: stfbuilder-senderoutput + namespace: alice-tasks +spec: + nodeName: mtichak-ost.cern.ch + arguments: + chans.buildertosender.0.address: ipc://@o2ipc-d7ef3du8ndmd7n8j7l30 + chans.buildertosender.0.autoBind: "0" + chans.buildertosender.0.method: bind + chans.buildertosender.0.rateLogging: "0" + chans.buildertosender.0.rcvBufSize: "1000" + chans.buildertosender.0.rcvKernelSize: "0" + chans.buildertosender.0.sndBufSize: "4" + chans.buildertosender.0.sndKernelSize: "0" + chans.buildertosender.0.transport: shmem + chans.buildertosender.0.type: push + chans.buildertosender.numSockets: "1" + chans.readout.0.address: ipc://@o2ipc-d7ef3du8ndmd7n8j7l2g + chans.readout.0.method: connect + chans.readout.0.rateLogging: "0" + chans.readout.0.rcvBufSize: "1000" + chans.readout.0.rcvKernelSize: "0" + chans.readout.0.sndBufSize: "1000" + chans.readout.0.sndKernelSize: "0" + chans.readout.0.transport: shmem + chans.readout.0.type: pull + chans.readout.numSockets: "1" + environment_id: 323ikoYJ88i + orbit-reset-time: "" + pod: + hostNetwork: true + hostIPC: true + securityContext: + fsGroup: 1100 + supplementalGroups: [10, 1105] + containers: + - name: stfbuilder-senderoutput + image: gitlab-registry.cern.ch/aliceo2group/dockerfiles/alma9-flp-node/dd:1 + command: ["/bin/bash"] + args: + - "-lc" + - "/opt/o2/bin/StfBuilder --session=default --transport=shmem --shm-segment-id=2 --shm-segment-size=33554432 --monitoring-backend='influxdb-unix:///tmp/telegraf.sock' --monitoring-process-interval='5' --discovery-partition=323ikoYJ88i --discovery-endpoint=no-op:// --detector-rdh=6 --detector-subspec=feeid --severity=info --severity-infologger=info --output-channel-name=buildertosender --shm-monitor=false --detector=TST --run-type=physics --id 323ikp9sptz -S $CONTROL_OCCPLUGIN_ROOT/lib/ -P OCClite --color false --control-port 31001" + securityContext: + privileged: true + runAsUser: 1100 + runAsGroup: 1100 + capabilities: + add: ["IPC_LOCK"] + env: + - name: O2_DETECTOR + value: "TST" + - name: O2_PARTITION + value: "323ikoYJ88i" + - name: OCC_CONTROL_PORT + value: "31001" + - name: O2_SYSTEM + value: "FLP" + - name: O2_ROLE + value: "mtichak-ost" + - name: O2_INFOLOGGER_MODE + value: "infoLoggerD" + - name: DATADIST_FEE_MASK + value: "0xffff" + - name: CONTROL_OCCPLUGIN_ROOT + value: "/opt/o2" + volumeMounts: + - name: host-shm + mountPath: /dev/shm + - name: group + mountPath: /etc/group + readOnly: true + - name: passwd + mountPath: /etc/passwd + readOnly: true + - name: tmp + mountPath: /tmp + - name: modules + mountPath: /lib/modules + imagePullPolicy: IfNotPresent + volumes: + - name: host-shm + hostPath: + path: /dev/shm + type: Directory + - name: group + hostPath: + path: /etc/group + - name: passwd + hostPath: + path: /etc/passwd + - name: tmp + hostPath: + path: /tmp + - name: modules + hostPath: + path: /lib/modules + imagePullSecrets: + - name: gitlab-registry-secret + control: + mode: "fairmq" + port: 31001 + bind: + - name: readout + type: push + rateLogging: "0" + addressing: ipc + transport: shmem + global: "" + target: "" + connect: + properties: + var1: value1 + var2: value2 + state: standby diff --git a/control-operator/ecs-manifests/kubernetes-manifests/stfbuilder-senderoutput.yaml b/control-operator/ecs-manifests/kubernetes-manifests/stfbuilder-senderoutput.yaml new file mode 100644 index 000000000..ae2bdc0be --- /dev/null +++ b/control-operator/ecs-manifests/kubernetes-manifests/stfbuilder-senderoutput.yaml @@ -0,0 +1,91 @@ +apiVersion: aliecs.alice.cern/v1alpha1 +kind: Task +metadata: + name: stfbuilder-senderoutput +spec: + pod: + hostNetwork: true + hostIPC: true + securityContext: + fsGroup: ${FLP_GID} + supplementalGroups: ${FLP_SUPPLEMENTAL_GROUPS} + containers: + - name: stfbuilder-senderoutput + image: gitlab-registry.cern.ch/aliceo2group/dockerfiles/alma9-flp-node/dd:1 + command: ["/bin/bash"] + args: + - "-lc" + - "${KUBE_COMMAND} ${KUBE_ARGUMENTS}" + securityContext: + privileged: true + runAsUser: ${FLP_UID} + runAsGroup: ${FLP_GID} + capabilities: + add: ["IPC_LOCK"] + + env: + - name: O2_DETECTOR + value: "${O2_DETECTOR}" + - name: O2_PARTITION + value: "${O2_PARTITION}" + - name: OCC_CONTROL_PORT + value: "${OCC_CONTROL_PORT}" + - name: O2_SYSTEM + value: "${O2_SYSTEM}" + - name: O2_ROLE + value: "${O2_ROLE}" + - name: O2_INFOLOGGER_MODE + value: "${O2_INFOLOGGER_MODE}" + - name: DATADIST_FEE_MASK + value: "${DATADIST_FEE_MASK}" + - name: CONTROL_OCCPLUGIN_ROOT + value: "/opt/o2" + volumeMounts: + - name: host-shm + mountPath: /dev/shm + - name: group + mountPath: /etc/group + readOnly: true + - name: passwd + mountPath: /etc/passwd + readOnly: true + - name: tmp + mountPath: /tmp + - name: modules + mountPath: /lib/modules + imagePullPolicy: IfNotPresent + volumes: + - name: host-shm + hostPath: + path: /dev/shm + type: Directory + - name: group + hostPath: + path: /etc/group + - name: passwd + hostPath: + path: /etc/passwd + - name: tmp + hostPath: + path: /tmp + - name: modules + hostPath: + path: /lib/modules + imagePullSecrets: + - name: gitlab-registry-secret + control: + mode: "fairmq" + port: ${OCC_CONTROL_PORT} + bind: + - name: readout + type: push + rateLogging: "0" + addressing: ipc + transport: shmem + global: "" + target: "" + connect: + properties: + var1: value1 + var2: value2 + state: standby diff --git a/control-operator/ecs-manifests/kubernetes-manifests/stfsender-test.yaml b/control-operator/ecs-manifests/kubernetes-manifests/stfsender-test.yaml new file mode 100644 index 000000000..fa8589c15 --- /dev/null +++ b/control-operator/ecs-manifests/kubernetes-manifests/stfsender-test.yaml @@ -0,0 +1,94 @@ +apiVersion: aliecs.alice.cern/v1alpha1 +kind: Task +metadata: + name: stfsender + namespace: alice-tasks +spec: + nodeName: mtichak-ost.cern.ch + arguments: + chans.buildertosender.0.address: ipc://@o2ipc-d7ef3du8ndmd7n8j7l30 + chans.buildertosender.0.method: connect + chans.buildertosender.0.rateLogging: "0" + chans.buildertosender.0.rcvBufSize: "1000" + chans.buildertosender.0.rcvKernelSize: "0" + chans.buildertosender.0.sndBufSize: "1000" + chans.buildertosender.0.sndKernelSize: "0" + chans.buildertosender.0.transport: shmem + chans.buildertosender.0.type: pull + chans.buildertosender.numSockets: "1" + environment_id: 323ikoYJ88i + orbit-reset-time: "" + + pod: + hostNetwork: true + hostIPC: true + securityContext: + fsGroup: 1100 + supplementalGroups: [10, 1105] + containers: + - name: readout + image: gitlab-registry.cern.ch/aliceo2group/dockerfiles/alma9-flp-node/dd:1 + command: ["/bin/bash"] + args: + - "-c" + # you might need to change mtichak from args to your usecase change this part + - "numactl --cpunodebind=0 --preferred=0 -- /opt/o2/bin/StfSender --session=default --shm-segment-id=2 --shm-segment-size=33554432 --dd-region-size=4096 --dd-region-id=3536 --transport=shmem --input-channel-name=buildertosender --severity=info --severity-infologger=info --monitoring-backend='influxdb-unix:///tmp/telegraf.sock' --monitoring-process-interval='5' --discovery-partition=323ikoYJ88i --discovery-id=stfs-mtichak-ost-323ikoig378 --discovery-endpoint=no-op:// --discovery-net-if=ib0 --stand-alone --shm-monitor=false --id 323ikp9tANv -S $CONTROL_OCCPLUGIN_ROOT/lib/ -P OCClite --color false --control-port 31002" + securityContext: + privileged: true + runAsUser: 1100 + runAsGroup: 1100 + env: + - name: O2_DETECTOR + value: "TST" + - name: O2_PARTITION + value: "xxxxxxxx" + - name: OCC_CONTROL_PORT + value: "31002" + - name: O2_SYSTEM + value: "flp" + # it might be necessary to change the role for your use case + - name: O2_ROLE + value: "mtichak" + volumeMounts: + - name: group + mountPath: /etc/group + readOnly: true + - name: passwd + mountPath: /etc/passwd + readOnly: true + - name: tmp + mountPath: /tmp + - name: modules + mountPath: /lib/modules + imagePullPolicy: IfNotPresent + volumes: + - name: group + hostPath: + path: /etc/group + - name: passwd + hostPath: + path: /etc/passwd + - name: tmp + hostPath: + path: /tmp + - name: modules + hostPath: + path: /lib/modules + imagePullSecrets: + - name: gitlab-registry-secret + control: + mode: "fairmq" + port: 31002 + bind: + - name: readout + type: push + rateLogging: "0" + addressing: ipc + transport: shmem + global: "" + target: "" + connect: + properties: + var1: value1 + var2: value2 + state: standby diff --git a/control-operator/ecs-manifests/kubernetes-manifests/stfsender.yaml b/control-operator/ecs-manifests/kubernetes-manifests/stfsender.yaml new file mode 100644 index 000000000..94f5478df --- /dev/null +++ b/control-operator/ecs-manifests/kubernetes-manifests/stfsender.yaml @@ -0,0 +1,105 @@ +apiVersion: aliecs.alice.cern/v1alpha1 +kind: Task +metadata: + name: stfsender +spec: + pod: + hostNetwork: true + hostIPC: true + securityContext: + fsGroup: ${FLP_GID} + supplementalGroups: ${FLP_SUPPLEMENTAL_GROUPS} + containers: + - name: stfsender + image: gitlab-registry.cern.ch/aliceo2group/dockerfiles/alma9-flp-node/dd:1 + command: ["/bin/bash"] + args: + - "-lc" + - "${KUBE_COMMAND} ${KUBE_ARGUMENTS}" + securityContext: + privileged: true + runAsUser: ${FLP_UID} + runAsGroup: ${FLP_GID} + capabilities: + add: ["IPC_LOCK"] + + env: + - name: O2_DETECTOR + value: "${O2_DETECTOR}" + - name: O2_PARTITION + value: "${O2_PARTITION}" + - name: OCC_CONTROL_PORT + value: "${OCC_CONTROL_PORT}" + - name: O2_SYSTEM + value: "${O2_SYSTEM}" + - name: O2_ROLE + value: "${O2_ROLE}" + - name: O2_INFOLOGGER_MODE + value: "${O2_INFOLOGGER_MODE}" + - name: http_proxy + value: "${http_proxy}" + - name: https_proxy + value: "${https_proxy}" + - name: no_proxy + value: "${no_proxy}" + - name: UCX_NET_DEVICES + value: "${UCX_NET_DEVICES}" + - name: UCX_TLS + value: "${UCX_TLS}" + - name: UCX_IB_SL + value: "${UCX_IB_SL}" + - name: UCX_DC_MAX_NUM_EPS + value: "${UCX_DC_MAX_NUM_EPS}" + - name: UCX_RC_MAX_NUM_EPS + value: "${UCX_RC_MAX_NUM_EPS}" + - name: CONTROL_OCCPLUGIN_ROOT + value: "/opt/o2" + volumeMounts: + - name: host-shm + mountPath: /dev/shm + - name: group + mountPath: /etc/group + readOnly: true + - name: passwd + mountPath: /etc/passwd + readOnly: true + - name: tmp + mountPath: /tmp + - name: modules + mountPath: /lib/modules + imagePullPolicy: IfNotPresent + volumes: + - name: host-shm + hostPath: + path: /dev/shm + type: Directory + - name: group + hostPath: + path: /etc/group + - name: passwd + hostPath: + path: /etc/passwd + - name: tmp + hostPath: + path: /tmp + - name: modules + hostPath: + path: /lib/modules + imagePullSecrets: + - name: gitlab-registry-secret + control: + mode: "fairmq" + port: ${OCC_CONTROL_PORT} + bind: + - name: readout + type: push + rateLogging: "0" + addressing: ipc + transport: shmem + global: "" + target: "" + connect: + properties: + var1: value1 + var2: value2 + state: standby diff --git a/control-operator/ecs-manifests/occ-configure-arguments/readout-configure-args.yaml b/control-operator/ecs-manifests/occ-configure-arguments/readout-configure-args.yaml new file mode 100644 index 000000000..2216e58b6 --- /dev/null +++ b/control-operator/ecs-manifests/occ-configure-arguments/readout-configure-args.yaml @@ -0,0 +1,16 @@ +chans: + readout: + numSockets: 1 + 0: + address: ipc://@o2ipc-d6qto8m8ndm409ltqngg + autoBind: 0 + method: bind + rateLogging: 0 + rcvBufSize: 1000 + rcvKernelSize: 0 + sndBufSize: 1000 + sndKernelSize: 0 + transport: shmem + type: push + +environment_id: 31UAUL4cwuk diff --git a/control-operator/ecs-manifests/occ-configure-arguments/stfbuilder-senderoutput-configure-args.yaml b/control-operator/ecs-manifests/occ-configure-arguments/stfbuilder-senderoutput-configure-args.yaml new file mode 100644 index 000000000..9d76ca13d --- /dev/null +++ b/control-operator/ecs-manifests/occ-configure-arguments/stfbuilder-senderoutput-configure-args.yaml @@ -0,0 +1,28 @@ +chans: + buildertosender: + numSockets: 1 + 0: + address: ipc://@o2ipc-d6qto8m8ndm409ltqnh0 + autoBind: 0 + method: bind + rateLogging: 0 + rcvBufSize: 1000 + rcvKernelSize: 0 + sndBufSize: 4 + sndKernelSize: 0 + transport: shmem + type: push + readout: + numSockets: 1 + 0: + address: ipc://@o2ipc-d6qto8m8ndm409ltqngg + method: connect + rateLogging: 0 + rcvBufSize: 1000 + rcvKernelSize: 0 + sndBufSize: 1000 + sndKernelSize: 0 + transport: shmem + type: pull + +environment_id: 31UAUL4cwuk diff --git a/control-operator/ecs-manifests/occ-configure-arguments/stfsender-configure-args.yaml b/control-operator/ecs-manifests/occ-configure-arguments/stfsender-configure-args.yaml new file mode 100644 index 000000000..fabc47d3f --- /dev/null +++ b/control-operator/ecs-manifests/occ-configure-arguments/stfsender-configure-args.yaml @@ -0,0 +1,15 @@ +chans: + buildertosender: + numSockets: 1 + 0: + address: ipc://@o2ipc-d6qto8m8ndm409ltqnh0 + method: connect + rateLogging: 0 + rcvBufSize: 1000 + rcvKernelSize: 0 + sndBufSize: 1000 + sndKernelSize: 0 + transport: shmem + type: pull + +environment_id: 31UAUL4cwuk diff --git a/control-operator/ecs-manifests/task-templates/dpl-tasktemplate.yaml b/control-operator/ecs-manifests/task-templates/dpl-tasktemplate.yaml new file mode 100644 index 000000000..a45a82c17 --- /dev/null +++ b/control-operator/ecs-manifests/task-templates/dpl-tasktemplate.yaml @@ -0,0 +1,62 @@ +apiVersion: aliecs.alice.cern/v1alpha1 +kind: TaskTemplate +metadata: + name: dpl + namespace: alice-tasks +spec: + envVars: + - O2_DETECTOR + - O2_PARTITION + - OCC_CONTROL_PORT + - O2_SYSTEM + - O2_ROLE + pod: + hostNetwork: true + hostIPC: true + securityContext: + fsGroup: 1100 + supplementalGroups: [10, 1105] + containers: + - name: dpl + image: gitlab-registry.cern.ch/aliceo2group/dockerfiles/alma9-flp-node/o2suite:20261001-1 + command: ["bash", "-c"] + securityContext: + privileged: true + runAsUser: 1100 + runAsGroup: 1100 + volumeMounts: + - name: host-shm + mountPath: /dev/shm + - name: group + mountPath: /etc/group + readOnly: true + - name: passwd + mountPath: /etc/passwd + readOnly: true + - name: tmp + mountPath: /tmp + - name: modules + mountPath: /lib/modules + imagePullPolicy: IfNotPresent + volumes: + - name: host-shm + hostPath: + path: /dev/shm + type: Directory + - name: group + hostPath: + path: /etc/group + - name: passwd + hostPath: + path: /etc/passwd + - name: tmp + hostPath: + path: /tmp + - name: modules + hostPath: + path: /lib/modules + imagePullSecrets: + - name: gitlab-registry-secret + control: + mode: "fairmq" + # port: ${OCC_CONTROL_PORT} # to be filled in automatically by TaskController diff --git a/control-operator/ecs-manifests/task-templates/readout-tasktemplate.yaml b/control-operator/ecs-manifests/task-templates/readout-tasktemplate.yaml new file mode 100644 index 000000000..5aa480497 --- /dev/null +++ b/control-operator/ecs-manifests/task-templates/readout-tasktemplate.yaml @@ -0,0 +1,64 @@ +apiVersion: aliecs.alice.cern/v1alpha1 +kind: TaskTemplate +metadata: + name: readout + namespace: alice-tasks +spec: + envVars: + - O2_DETECTOR + - O2_PARTITION + - O2_SYSTEM + - O2_ROLE + pod: + hostNetwork: true + hostIPC: true + securityContext: + fsGroup: 1100 + supplementalGroups: [10, 1105] + containers: + - name: readout + image: gitlab-registry.cern.ch/aliceo2group/dockerfiles/alma9-flp-node/o2suite:20261001-1 + command: ["/opt/o2/bin/o2-readout-exe"] + env: + - name: OCC_CONTROL_PORT + value: "47101" + securityContext: + privileged: true + runAsUser: 1100 + runAsGroup: 1100 + volumeMounts: + - name: host-shm + mountPath: /dev/shm + - name: group + mountPath: /etc/group + readOnly: true + - name: passwd + mountPath: /etc/passwd + readOnly: true + - name: tmp + mountPath: /tmp + - name: modules + mountPath: /lib/modules + imagePullPolicy: IfNotPresent + volumes: + - name: host-shm + hostPath: + path: /dev/shm + type: Directory + - name: group + hostPath: + path: /etc/group + - name: passwd + hostPath: + path: /etc/passwd + - name: tmp + hostPath: + path: /tmp + - name: modules + hostPath: + path: /lib/modules + imagePullSecrets: + - name: gitlab-registry-secret + control: + mode: "direct" + # port: ${OCC_CONTROL_PORT} # to be filled in automatically by TaskController diff --git a/control-operator/ecs-manifests/task-templates/stfbuilder-senderoutput-tasktemplate.yaml b/control-operator/ecs-manifests/task-templates/stfbuilder-senderoutput-tasktemplate.yaml new file mode 100644 index 000000000..978389bad --- /dev/null +++ b/control-operator/ecs-manifests/task-templates/stfbuilder-senderoutput-tasktemplate.yaml @@ -0,0 +1,77 @@ +apiVersion: aliecs.alice.cern/v1alpha1 +kind: TaskTemplate +metadata: + name: stfbuilder-senderoutput + namespace: alice-tasks +spec: + envVars: + - O2_DETECTOR + - O2_PARTITION + - O2_SYSTEM + - O2_ROLE + - O2_INFOLOGGER_MODE + - DATADIST_FEE_MASK + pod: + hostNetwork: true + hostIPC: true + securityContext: + fsGroup: 1100 + supplementalGroups: [10, 1105] + containers: + - name: stfbuilder + image: gitlab-registry.cern.ch/aliceo2group/dockerfiles/alma9-flp-node/o2suite:20261001-1 + command: [/opt/o2/bin/StfBuilder] + env: + - name: OCC_CONTROL_PORT + value: "47102" + securityContext: + privileged: true + runAsUser: 1100 + runAsGroup: 1100 + volumeMounts: + - name: host-shm + mountPath: /dev/shm + - name: group + mountPath: /etc/group + readOnly: true + - name: passwd + mountPath: /etc/passwd + readOnly: true + - name: tmp + mountPath: /tmp + - name: modules + mountPath: /lib/modules + imagePullPolicy: IfNotPresent + volumes: + - name: host-shm + hostPath: + path: /dev/shm + type: Directory + - name: group + hostPath: + path: /etc/group + - name: passwd + hostPath: + path: /etc/passwd + - name: tmp + hostPath: + path: /tmp + - name: modules + hostPath: + path: /lib/modules + imagePullSecrets: + - name: gitlab-registry-secret + control: + mode: "fairmq" + # port: ${OCC_CONTROL_PORT} # to be filled in automatically by TaskController + bind: + - name: buildertosender + type: push + rateLogging: "0" + addressing: ipc + transport: shmem + sndBufSize: 4 + global: "" + target: "" + connect: [] + properties: {} diff --git a/control-operator/ecs-manifests/task-templates/stfbuilder-tasktemplate.yaml b/control-operator/ecs-manifests/task-templates/stfbuilder-tasktemplate.yaml new file mode 100644 index 000000000..42d297624 --- /dev/null +++ b/control-operator/ecs-manifests/task-templates/stfbuilder-tasktemplate.yaml @@ -0,0 +1,77 @@ +apiVersion: aliecs.alice.cern/v1alpha1 +kind: TaskTemplate +metadata: + name: stfbuilder + namespace: alice-tasks +spec: + envVars: + - O2_DETECTOR + - O2_PARTITION + - O2_SYSTEM + - O2_ROLE + - O2_INFOLOGGER_MODE + - DATADIST_FEE_MASK + pod: + hostNetwork: true + hostIPC: true + securityContext: + fsGroup: 1100 + supplementalGroups: [10, 1105] + containers: + - name: stfbuilder + image: gitlab-registry.cern.ch/aliceo2group/dockerfiles/alma9-flp-node/o2suite:20261001-1 + command: [/opt/o2/bin/StfBuilder] + env: + - name: OCC_CONTROL_PORT + value: "47102" + securityContext: + privileged: true + runAsUser: 1100 + runAsGroup: 1100 + volumeMounts: + - name: host-shm + mountPath: /dev/shm + - name: group + mountPath: /etc/group + readOnly: true + - name: passwd + mountPath: /etc/passwd + readOnly: true + - name: tmp + mountPath: /tmp + - name: modules + mountPath: /lib/modules + imagePullPolicy: IfNotPresent + volumes: + - name: host-shm + hostPath: + path: /dev/shm + type: Directory + - name: group + hostPath: + path: /etc/group + - name: passwd + hostPath: + path: /etc/passwd + - name: tmp + hostPath: + path: /tmp + - name: modules + hostPath: + path: /lib/modules + imagePullSecrets: + - name: gitlab-registry-secret + control: + mode: "fairmq" + # port: ${OCC_CONTROL_PORT} # to be filled in automatically by TaskController + bind: + - name: dpl-chan + type: push + rateLogging: "0" + addressing: ipc + transport: shmem + sndBufSize: 4 + global: "" + target: "" + connect: [] + properties: {} diff --git a/control-operator/ecs-manifests/task-templates/stfsender-tasktemplate.yaml b/control-operator/ecs-manifests/task-templates/stfsender-tasktemplate.yaml new file mode 100644 index 000000000..62efc2c7c --- /dev/null +++ b/control-operator/ecs-manifests/task-templates/stfsender-tasktemplate.yaml @@ -0,0 +1,73 @@ +apiVersion: aliecs.alice.cern/v1alpha1 +kind: TaskTemplate +metadata: + name: stfsender + namespace: alice-tasks +spec: + envVars: + - O2_DETECTOR + - O2_PARTITION + - O2_SYSTEM + - O2_ROLE + - O2_INFOLOGGER_MODE + - http_proxy + - https_proxy + - no_proxy + - UCX_NET_DEVICES + - UCX_TLS + - UCX_IB_SL + - UCX_DC_MAX_NUM_EPS + - UCX_RC_MAX_NUM_EPS + pod: + hostNetwork: true + hostIPC: true + securityContext: + fsGroup: 1100 + supplementalGroups: [10, 1105] + containers: + - name: stfsender + image: gitlab-registry.cern.ch/aliceo2group/dockerfiles/alma9-flp-node/o2suite:20261001-1 + command: ["numactl"] + env: + - name: OCC_CONTROL_PORT + value: "47103" + securityContext: + privileged: true + runAsUser: 1100 + runAsGroup: 1100 + volumeMounts: + - name: host-shm + mountPath: /dev/shm + - name: group + mountPath: /etc/group + readOnly: true + - name: passwd + mountPath: /etc/passwd + readOnly: true + - name: tmp + mountPath: /tmp + - name: modules + mountPath: /lib/modules + imagePullPolicy: IfNotPresent + volumes: + - name: host-shm + hostPath: + path: /dev/shm + type: Directory + - name: group + hostPath: + path: /etc/group + - name: passwd + hostPath: + path: /etc/passwd + - name: tmp + hostPath: + path: /tmp + - name: modules + hostPath: + path: /lib/modules + imagePullSecrets: + - name: gitlab-registry-secret + control: + mode: "fairmq" + # port: ${OCC_CONTROL_PORT} # to be filled in automatically by TaskController diff --git a/control-operator/go.mod b/control-operator/go.mod new file mode 100644 index 000000000..0754656af --- /dev/null +++ b/control-operator/go.mod @@ -0,0 +1,85 @@ +module github.com/AliceO2Group/Control/control-operator + +go 1.26.8 + +require ( + github.com/go-logr/logr v1.4.3 + github.com/onsi/ginkgo/v2 v2.27.2 + github.com/onsi/gomega v1.38.2 + google.golang.org/grpc v1.78.0 + google.golang.org/protobuf v1.36.11 + k8s.io/api v0.35.0 + k8s.io/apimachinery v0.35.0 + k8s.io/client-go v0.35.0 + sigs.k8s.io/controller-runtime v0.23.0 +) + +require ( + github.com/Masterminds/semver/v3 v3.4.0 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/emicklei/go-restful/v3 v3.13.0 // indirect + github.com/evanphx/json-patch/v5 v5.9.11 // indirect + github.com/fsnotify/fsnotify v1.9.0 // indirect + github.com/fxamacker/cbor/v2 v2.9.0 // indirect + github.com/go-logr/zapr v1.3.0 // indirect + github.com/go-openapi/jsonpointer v0.22.4 // indirect + github.com/go-openapi/jsonreference v0.21.4 // indirect + github.com/go-openapi/swag v0.25.4 // indirect + github.com/go-openapi/swag/cmdutils v0.25.4 // indirect + github.com/go-openapi/swag/conv v0.25.4 // indirect + github.com/go-openapi/swag/fileutils v0.25.4 // indirect + github.com/go-openapi/swag/jsonname v0.25.4 // indirect + github.com/go-openapi/swag/jsonutils v0.25.4 // indirect + github.com/go-openapi/swag/loading v0.25.4 // indirect + github.com/go-openapi/swag/mangling v0.25.4 // indirect + github.com/go-openapi/swag/netutils v0.25.4 // indirect + github.com/go-openapi/swag/stringutils v0.25.4 // indirect + github.com/go-openapi/swag/typeutils v0.25.4 // indirect + github.com/go-openapi/swag/yamlutils v0.25.4 // indirect + github.com/go-task/slim-sprig/v3 v3.0.0 // indirect + github.com/google/btree v1.1.3 // indirect + github.com/google/gnostic-models v0.7.1 // indirect + github.com/google/go-cmp v0.7.0 // indirect + github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/prometheus/client_golang v1.23.2 // indirect + github.com/prometheus/client_model v0.6.2 // indirect + github.com/prometheus/common v0.67.5 // indirect + github.com/prometheus/procfs v0.19.2 // indirect + github.com/spf13/pflag v1.0.10 // indirect + github.com/x448/float16 v0.8.4 // indirect + go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.27.1 // indirect + go.yaml.in/yaml/v2 v2.4.3 // indirect + go.yaml.in/yaml/v3 v3.0.4 // indirect + golang.org/x/mod v0.31.0 // indirect + golang.org/x/net v0.49.0 // indirect + golang.org/x/oauth2 v0.34.0 // indirect + golang.org/x/sync v0.19.0 // indirect + golang.org/x/sys v0.40.0 // indirect + golang.org/x/term v0.39.0 // indirect + golang.org/x/text v0.33.0 // indirect + golang.org/x/time v0.14.0 // indirect + golang.org/x/tools v0.40.0 // indirect + gomodules.xyz/jsonpatch/v2 v2.5.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260122232226-8e98ce8d340d // indirect + gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect + gopkg.in/inf.v0 v0.9.1 // indirect + k8s.io/apiextensions-apiserver v0.35.0 // indirect + k8s.io/klog/v2 v2.130.1 // indirect + k8s.io/kube-openapi v0.0.0-20251125145642-4e65d59e963e // indirect + k8s.io/utils v0.0.0-20260108192941-914a6e750570 // indirect + sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect + sigs.k8s.io/randfill v1.0.0 // indirect + sigs.k8s.io/structured-merge-diff/v6 v6.3.1 // indirect + sigs.k8s.io/yaml v1.6.0 // indirect +) + +replace github.com/AliceO2Group/Control/control-operator/internal/controller/protos/generated => ./internal/controller/protos/generated diff --git a/control-operator/go.sum b/control-operator/go.sum new file mode 100644 index 000000000..3c8f95fef --- /dev/null +++ b/control-operator/go.sum @@ -0,0 +1,229 @@ +github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0= +github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/emicklei/go-restful/v3 v3.13.0 h1:C4Bl2xDndpU6nJ4bc1jXd+uTmYPVUwkD6bFY/oTyCes= +github.com/emicklei/go-restful/v3 v3.13.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/evanphx/json-patch v0.5.2 h1:xVCHIVMUu1wtM/VkR9jVZ45N3FhZfYMMYGorLCR8P3k= +github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ= +github.com/evanphx/json-patch/v5 v5.9.11 h1:/8HVnzMq13/3x9TPvjG08wUGqBTmZBsCWzjTM0wiaDU= +github.com/evanphx/json-patch/v5 v5.9.11/go.mod h1:3j+LviiESTElxA4p3EMKAB9HXj3/XEtnUf6OZxqIQTM= +github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= +github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= +github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM= +github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= +github.com/gkampitakis/ciinfo v0.3.2 h1:JcuOPk8ZU7nZQjdUhctuhQofk7BGHuIy0c9Ez8BNhXs= +github.com/gkampitakis/ciinfo v0.3.2/go.mod h1:1NIwaOcFChN4fa/B0hEBdAb6npDlFL8Bwx4dfRLRqAo= +github.com/gkampitakis/go-diff v1.3.2 h1:Qyn0J9XJSDTgnsgHRdz9Zp24RaJeKMUHg2+PDZZdC4M= +github.com/gkampitakis/go-diff v1.3.2/go.mod h1:LLgOrpqleQe26cte8s36HTWcTmMEur6OPYerdAAS9tk= +github.com/gkampitakis/go-snaps v0.5.15 h1:amyJrvM1D33cPHwVrjo9jQxX8g/7E2wYdZ+01KS3zGE= +github.com/gkampitakis/go-snaps v0.5.15/go.mod h1:HNpx/9GoKisdhw9AFOBT1N7DBs9DiHo/hGheFGBZ+mc= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= +github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg= +github.com/go-openapi/jsonpointer v0.22.4 h1:dZtK82WlNpVLDW2jlA1YCiVJFVqkED1MegOUy9kR5T4= +github.com/go-openapi/jsonpointer v0.22.4/go.mod h1:elX9+UgznpFhgBuaMQ7iu4lvvX1nvNsesQ3oxmYTw80= +github.com/go-openapi/jsonreference v0.21.4 h1:24qaE2y9bx/q3uRK/qN+TDwbok1NhbSmGjjySRCHtC8= +github.com/go-openapi/jsonreference v0.21.4/go.mod h1:rIENPTjDbLpzQmQWCj5kKj3ZlmEh+EFVbz3RTUh30/4= +github.com/go-openapi/swag v0.25.4 h1:OyUPUFYDPDBMkqyxOTkqDYFnrhuhi9NR6QVUvIochMU= +github.com/go-openapi/swag v0.25.4/go.mod h1:zNfJ9WZABGHCFg2RnY0S4IOkAcVTzJ6z2Bi+Q4i6qFQ= +github.com/go-openapi/swag/cmdutils v0.25.4 h1:8rYhB5n6WawR192/BfUu2iVlxqVR9aRgGJP6WaBoW+4= +github.com/go-openapi/swag/cmdutils v0.25.4/go.mod h1:pdae/AFo6WxLl5L0rq87eRzVPm/XRHM3MoYgRMvG4A0= +github.com/go-openapi/swag/conv v0.25.4 h1:/Dd7p0LZXczgUcC/Ikm1+YqVzkEeCc9LnOWjfkpkfe4= +github.com/go-openapi/swag/conv v0.25.4/go.mod h1:3LXfie/lwoAv0NHoEuY1hjoFAYkvlqI/Bn5EQDD3PPU= +github.com/go-openapi/swag/fileutils v0.25.4 h1:2oI0XNW5y6UWZTC7vAxC8hmsK/tOkWXHJQH4lKjqw+Y= +github.com/go-openapi/swag/fileutils v0.25.4/go.mod h1:cdOT/PKbwcysVQ9Tpr0q20lQKH7MGhOEb6EwmHOirUk= +github.com/go-openapi/swag/jsonname v0.25.4 h1:bZH0+MsS03MbnwBXYhuTttMOqk+5KcQ9869Vye1bNHI= +github.com/go-openapi/swag/jsonname v0.25.4/go.mod h1:GPVEk9CWVhNvWhZgrnvRA6utbAltopbKwDu8mXNUMag= +github.com/go-openapi/swag/jsonutils v0.25.4 h1:VSchfbGhD4UTf4vCdR2F4TLBdLwHyUDTd1/q4i+jGZA= +github.com/go-openapi/swag/jsonutils v0.25.4/go.mod h1:7OYGXpvVFPn4PpaSdPHJBtF0iGnbEaTk8AvBkoWnaAY= +github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.4 h1:IACsSvBhiNJwlDix7wq39SS2Fh7lUOCJRmx/4SN4sVo= +github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.4/go.mod h1:Mt0Ost9l3cUzVv4OEZG+WSeoHwjWLnarzMePNDAOBiM= +github.com/go-openapi/swag/loading v0.25.4 h1:jN4MvLj0X6yhCDduRsxDDw1aHe+ZWoLjW+9ZQWIKn2s= +github.com/go-openapi/swag/loading v0.25.4/go.mod h1:rpUM1ZiyEP9+mNLIQUdMiD7dCETXvkkC30z53i+ftTE= +github.com/go-openapi/swag/mangling v0.25.4 h1:2b9kBJk9JvPgxr36V23FxJLdwBrpijI26Bx5JH4Hp48= +github.com/go-openapi/swag/mangling v0.25.4/go.mod h1:6dxwu6QyORHpIIApsdZgb6wBk/DPU15MdyYj/ikn0Hg= +github.com/go-openapi/swag/netutils v0.25.4 h1:Gqe6K71bGRb3ZQLusdI8p/y1KLgV4M/k+/HzVSqT8H0= +github.com/go-openapi/swag/netutils v0.25.4/go.mod h1:m2W8dtdaoX7oj9rEttLyTeEFFEBvnAx9qHd5nJEBzYg= +github.com/go-openapi/swag/stringutils v0.25.4 h1:O6dU1Rd8bej4HPA3/CLPciNBBDwZj9HiEpdVsb8B5A8= +github.com/go-openapi/swag/stringutils v0.25.4/go.mod h1:GTsRvhJW5xM5gkgiFe0fV3PUlFm0dr8vki6/VSRaZK0= +github.com/go-openapi/swag/typeutils v0.25.4 h1:1/fbZOUN472NTc39zpa+YGHn3jzHWhv42wAJSN91wRw= +github.com/go-openapi/swag/typeutils v0.25.4/go.mod h1:Ou7g//Wx8tTLS9vG0UmzfCsjZjKhpjxayRKTHXf2pTE= +github.com/go-openapi/swag/yamlutils v0.25.4 h1:6jdaeSItEUb7ioS9lFoCZ65Cne1/RZtPBZ9A56h92Sw= +github.com/go-openapi/swag/yamlutils v0.25.4/go.mod h1:MNzq1ulQu+yd8Kl7wPOut/YHAAU/H6hL91fF+E2RFwc= +github.com/go-openapi/testify/enable/yaml/v2 v2.0.2 h1:0+Y41Pz1NkbTHz8NngxTuAXxEodtNSI1WG1c/m5Akw4= +github.com/go-openapi/testify/enable/yaml/v2 v2.0.2/go.mod h1:kme83333GCtJQHXQ8UKX3IBZu6z8T5Dvy5+CW3NLUUg= +github.com/go-openapi/testify/v2 v2.0.2 h1:X999g3jeLcoY8qctY/c/Z8iBHTbwLz7R2WXd6Ub6wls= +github.com/go-openapi/testify/v2 v2.0.2/go.mod h1:HCPmvFFnheKK2BuwSA0TbbdxJ3I16pjwMkYkP4Ywn54= +github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= +github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= +github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw= +github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= +github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= +github.com/google/gnostic-models v0.7.1 h1:SisTfuFKJSKM5CPZkffwi6coztzzeYUhc3v4yxLWH8c= +github.com/google/gnostic-models v0.7.1/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8= +github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/joshdk/go-junit v1.0.0 h1:S86cUKIdwBHWwA6xCmFlf3RTLfVXYQfvanM5Uh+K6GE= +github.com/joshdk/go-junit v1.0.0/go.mod h1:TiiV0PqkaNfFXjEiyjWM3XXrhVyCa1K4Zfga6W52ung= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= +github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/maruel/natural v1.1.1 h1:Hja7XhhmvEFhcByqDoHz9QZbkWey+COd9xWfCfn1ioo= +github.com/maruel/natural v1.1.1/go.mod h1:v+Rfd79xlw1AgVBjbO0BEQmptqb5HvL/k9GRHB7ZKEg= +github.com/mfridman/tparse v0.18.0 h1:wh6dzOKaIwkUGyKgOntDW4liXSo37qg5AXbIhkMV3vE= +github.com/mfridman/tparse v0.18.0/go.mod h1:gEvqZTuCgEhPbYk/2lS3Kcxg1GmTxxU7kTC8DvP0i/A= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8= +github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/onsi/ginkgo/v2 v2.27.2 h1:LzwLj0b89qtIy6SSASkzlNvX6WktqurSHwkk2ipF/Ns= +github.com/onsi/ginkgo/v2 v2.27.2/go.mod h1:ArE1D/XhNXBXCBkKOLkbsb2c81dQHCRcF5zwn/ykDRo= +github.com/onsi/gomega v1.38.2 h1:eZCjf2xjZAqe+LeWvKb5weQ+NcPwX84kqJ0cZNxok2A= +github.com/onsi/gomega v1.38.2/go.mod h1:W2MJcYxRGV63b418Ai34Ud0hEdTVXq9NW9+Sx6uXf3k= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= +github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= +github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= +github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= +github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4= +github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw= +github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws= +github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= +github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= +github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= +github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= +github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= +github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY= +github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= +github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= +github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= +go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= +go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= +go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= +go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= +go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA= +go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI= +go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E= +go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg= +go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM= +go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA= +go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= +go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc= +go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= +go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= +go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= +go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +golang.org/x/mod v0.31.0 h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI= +golang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg= +golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o= +golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8= +golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw= +golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= +golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= +golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ= +golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/term v0.39.0 h1:RclSuaJf32jOqZz74CkPA9qFuVTX7vhLlpfj/IGWlqY= +golang.org/x/term v0.39.0/go.mod h1:yxzUCTP/U+FzoxfdKmLaA0RV1WgE0VY7hXBwKtY/4ww= +golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE= +golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8= +golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= +golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= +golang.org/x/tools v0.40.0 h1:yLkxfA+Qnul4cs9QA3KnlFu0lVmd8JJfoq+E41uSutA= +golang.org/x/tools v0.40.0/go.mod h1:Ik/tzLRlbscWpqqMRjyWYDisX8bG13FrdXp3o4Sr9lc= +gomodules.xyz/jsonpatch/v2 v2.5.0 h1:JELs8RLM12qJGXU4u/TO3V25KW8GreMKl9pdkk14RM0= +gomodules.xyz/jsonpatch/v2 v2.5.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= +gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= +gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260122232226-8e98ce8d340d h1:xXzuihhT3gL/ntduUZwHECzAn57E8dA6l8SOtYWdD8Q= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260122232226-8e98ce8d340d/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= +google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc= +google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/evanphx/json-patch.v4 v4.13.0 h1:czT3CmqEaQ1aanPc5SdlgQrrEIb8w/wwCvWWnfEbYzo= +gopkg.in/evanphx/json-patch.v4 v4.13.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +k8s.io/api v0.35.0 h1:iBAU5LTyBI9vw3L5glmat1njFK34srdLmktWwLTprlY= +k8s.io/api v0.35.0/go.mod h1:AQ0SNTzm4ZAczM03QH42c7l3bih1TbAXYo0DkF8ktnA= +k8s.io/apiextensions-apiserver v0.35.0 h1:3xHk2rTOdWXXJM+RDQZJvdx0yEOgC0FgQ1PlJatA5T4= +k8s.io/apiextensions-apiserver v0.35.0/go.mod h1:E1Ahk9SADaLQ4qtzYFkwUqusXTcaV2uw3l14aqpL2LU= +k8s.io/apimachinery v0.35.0 h1:Z2L3IHvPVv/MJ7xRxHEtk6GoJElaAqDCCU0S6ncYok8= +k8s.io/apimachinery v0.35.0/go.mod h1:jQCgFZFR1F4Ik7hvr2g84RTJSZegBc8yHgFWKn//hns= +k8s.io/client-go v0.35.0 h1:IAW0ifFbfQQwQmga0UdoH0yvdqrbwMdq9vIFEhRpxBE= +k8s.io/client-go v0.35.0/go.mod h1:q2E5AAyqcbeLGPdoRB+Nxe3KYTfPce1Dnu1myQdqz9o= +k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= +k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= +k8s.io/kube-openapi v0.0.0-20251125145642-4e65d59e963e h1:iW9ChlU0cU16w8MpVYjXk12dqQ4BPFBEgif+ap7/hqQ= +k8s.io/kube-openapi v0.0.0-20251125145642-4e65d59e963e/go.mod h1:kdmbQkyfwUagLfXIad1y2TdrjPFWp2Q89B3qkRwf/pQ= +k8s.io/utils v0.0.0-20260108192941-914a6e750570 h1:JT4W8lsdrGENg9W+YwwdLJxklIuKWdRm+BC+xt33FOY= +k8s.io/utils v0.0.0-20260108192941-914a6e750570/go.mod h1:xDxuJ0whA3d0I4mf/C4ppKHxXynQ+fxnkmQH0vTHnuk= +sigs.k8s.io/controller-runtime v0.23.0 h1:Ubi7klJWiwEWqDY+odSVZiFA0aDSevOCXpa38yCSYu8= +sigs.k8s.io/controller-runtime v0.23.0/go.mod h1:DBOIr9NsprUqCZ1ZhsuJ0wAnQSIxY/C6VjZbmLgw0j0= +sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5EXP7sU1kvOlxwZh5txg= +sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= +sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU= +sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= +sigs.k8s.io/structured-merge-diff/v6 v6.3.1 h1:JrhdFMqOd/+3ByqlP2I45kTOZmTRLBUm5pvRjeheg7E= +sigs.k8s.io/structured-merge-diff/v6 v6.3.1/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE= +sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs= +sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4= diff --git a/control-operator/hack/boilerplate.go.txt b/control-operator/hack/boilerplate.go.txt new file mode 100644 index 000000000..100584a9f --- /dev/null +++ b/control-operator/hack/boilerplate.go.txt @@ -0,0 +1,23 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2026 CERN and copyright holders of ALICE O². + * Author: author + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ diff --git a/control-operator/internal/controller/direct_transition.go b/control-operator/internal/controller/direct_transition.go new file mode 100644 index 000000000..4f73f1bc4 --- /dev/null +++ b/control-operator/internal/controller/direct_transition.go @@ -0,0 +1,145 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2026 CERN and copyright holders of ALICE O². + * Author: Michal Tichak + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package controller + +import ( + "fmt" +) + +type State int + +const ( + CONFIGURED = iota + RUNNING + STANDBY + ERROR +) + +type Transition int + +const ( + GO_ERROR = iota + RECOVER + CONFIGURE + RESET + STOP + START + EXIT +) + +type FromTo struct { + from, to State +} + +// How to handle EXIT? +var fromStatesToTransition = map[FromTo]Transition{ + {from: CONFIGURED, to: ERROR}: GO_ERROR, + {from: CONFIGURED, to: RUNNING}: START, + {from: CONFIGURED, to: STANDBY}: RESET, + {from: ERROR, to: STANDBY}: RECOVER, + {from: RUNNING, to: CONFIGURED}: STOP, + {from: RUNNING, to: ERROR}: GO_ERROR, + {from: STANDBY, to: CONFIGURED}: CONFIGURE, +} + +func FromStatesToTransition(from, to State) (Transition, error) { + transition, hasValue := fromStatesToTransition[FromTo{from: from, to: to}] + if !hasValue { + return 0, fmt.Errorf("failed to find transition from %s, to %s", from, to) + } + return transition, nil +} + +func (s State) String() string { + switch s { + case CONFIGURED: + return "configured" + case RUNNING: + return "running" + case STANDBY: + return "standby" + case ERROR: + return "error" + default: + return fmt.Sprintf("State(%d)", s) + } +} + +func StateFromString(s string) (State, error) { + switch s { + case "configured": + return CONFIGURED, nil + case "running": + return RUNNING, nil + case "standby": + return STANDBY, nil + case "error": + return ERROR, nil + default: + return 0, fmt.Errorf("invalid State: %s", s) + } +} + +func (t Transition) String() string { + switch t { + case GO_ERROR: + return "go_error" + case RECOVER: + return "recover" + case CONFIGURE: + return "configure" + case RESET: + return "reset" + case STOP: + return "stop" + case START: + return "start" + case EXIT: + return "exit" + default: + return fmt.Sprintf("Transition(%d)", t) + } +} + +func TransitionFromString(s string) (Transition, error) { + switch s { + case "go_error": + return GO_ERROR, nil + case "recover": + return RECOVER, nil + case "configure": + return CONFIGURE, nil + case "reset": + return RESET, nil + case "stop": + return STOP, nil + case "start": + return START, nil + case "exit": + return EXIT, nil + default: + return 0, fmt.Errorf("invalid Transition: %s", s) + } +} diff --git a/control-operator/internal/controller/environment_controller.go b/control-operator/internal/controller/environment_controller.go new file mode 100644 index 000000000..702b13d0c --- /dev/null +++ b/control-operator/internal/controller/environment_controller.go @@ -0,0 +1,421 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2026 CERN and copyright holders of ALICE O². + * Author: Michal Tichak + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package controller + +import ( + "context" + "fmt" + "maps" + "slices" + "strconv" + "strings" + + v1 "k8s.io/api/core/v1" + k8serrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + "sigs.k8s.io/controller-runtime/pkg/handler" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + + aliecsv1alpha1 "github.com/AliceO2Group/Control/control-operator/api/v1alpha1" + "github.com/go-logr/logr" +) + +// EnvironmentReconciler reconciles a Environment object +type EnvironmentReconciler struct { + client.Client + Scheme *runtime.Scheme +} + +func (r *EnvironmentReconciler) runTasksFromReferenceOnNode(ctx context.Context, taskReferences []aliecsv1alpha1.TaskReference, + nodename string, resolvedNodename string, req ctrl.Request, environment *aliecsv1alpha1.Environment, log logr.Logger, +) (*ctrl.Result, error) { + for _, taskReference := range taskReferences { + + log.Info("getting stored template for task", "task", taskReference.Name) + taskTemplateName := taskReference.Name + if strings.HasPrefix(taskTemplateName, "jit-") { + log.Info("getting dpl TaskTemplate for task", "task", taskReference.Name) + taskTemplateName = "dpl" + } + template := &aliecsv1alpha1.TaskTemplate{} + if err := r.Get(ctx, types.NamespacedName{Namespace: req.Namespace, Name: taskTemplateName}, template); err != nil { + log.Error(err, "failed to get template for task", "task", taskReference.Name) + return &ctrl.Result{}, nil + } + + task := &aliecsv1alpha1.Task{} + task.Namespace = req.Namespace + task.Name = fmt.Sprintf("%s-%s", nodename, template.Name) + if taskReference.NameSuffix != "" { + task.Name = fmt.Sprintf("%s-%s", task.Name, taskReference.NameSuffix) + } + task.Name = strings.ToLower(task.Name) + + if err := r.Get(ctx, types.NamespacedName{Name: task.Name, Namespace: task.Namespace}, task); err == nil { + continue + } + + // TODO: N^2 change + for _, env := range template.Spec.EnvVars { + if !slices.ContainsFunc(taskReference.Env, func(envVar v1.EnvVar) bool { + return envVar.Name == env + }) { + log.Error(fmt.Errorf("didn't find required env: %s", env), "failed to fill in env vars from template") + return &reconcile.Result{}, nil + } + } + + task.Spec.Pod = *template.Spec.Pod.DeepCopy() + task.Spec.Control = *template.Spec.Control.DeepCopy() + + if task.Spec.Arguments == nil { + task.Spec.Arguments = make(map[string]string) + } + + handleEnvVars(task, taskReference) + maps.Copy(task.Spec.Arguments, taskReference.ArgsTransition) + + task.Spec.Pod.NodeName = resolvedNodename + task.Spec.NodeName = resolvedNodename + task.Spec.State = environment.Spec.State + + task.Labels = labels(environment, nodename) + if taskReference.TaskID != "" { + task.Labels["taskID"] = taskReference.TaskID + } + + if err := controllerutil.SetControllerReference(environment, task, r.Scheme); err != nil { + log.Error(err, "failed to set controller reference", "task", task.Name) + return &ctrl.Result{}, err + } + + if err := r.Create(ctx, task); err != nil { + if err = client.IgnoreAlreadyExists(err); err != nil { + log.Error(err, "Failed to create task on node", "nodename", nodename, "task", task.Name) + return &ctrl.Result{}, err + // TODO: add error handling + } + } + } + return nil, nil +} + +func handleEnvVars(task *aliecsv1alpha1.Task, taskReference aliecsv1alpha1.TaskReference) { + // TODO: check for existence of containers + // TODO: adapt for Pods with multiple containers + // reference to existing Env Vars to overwrite them if found in taskReference + existing := make(map[string]int, len(task.Spec.Pod.Containers[0].Env)) + for i, e := range task.Spec.Pod.Containers[0].Env { + existing[e.Name] = i + } + for _, e := range taskReference.Env { + if i, ok := existing[e.Name]; ok { + task.Spec.Pod.Containers[0].Env[i] = e + } else { + task.Spec.Pod.Containers[0].Env = append(task.Spec.Pod.Containers[0].Env, e) + } + } + task.Spec.Pod.Containers[0].Args = append(task.Spec.Pod.Containers[0].Args, taskReference.ArgsCLI...) + + // TODO: should the OCC_CONTROL_PORT handled in task_controller.go and set only Ports and types in this controller? + for _, envVar := range task.Spec.Pod.Containers[0].Env { + if envVar.Name == "OCC_CONTROL_PORT" { + if port, err := strconv.Atoi(envVar.Value); err == nil { + task.Spec.Control.Port = port + } + break + } + } +} + +func (r *EnvironmentReconciler) runTaskFromDefinitionOnNode(ctx context.Context, taskDefs []aliecsv1alpha1.TaskDefinition, + nodename string, resolvedNodename string, req ctrl.Request, environment *aliecsv1alpha1.Environment, log logr.Logger, +) (*ctrl.Result, error) { + for _, taskDef := range taskDefs { + task := &aliecsv1alpha1.Task{} + task.Namespace = req.Namespace + task.Name = fmt.Sprintf("%s-%s", nodename, taskDef.Name) + if err := r.Get(ctx, types.NamespacedName{Name: task.Name, Namespace: task.Namespace}, task); err == nil { + continue + } + + task.Spec.Pod = *taskDef.Spec.Pod.DeepCopy() + task.Spec.Control = *taskDef.Spec.Control.DeepCopy() + + if len(taskDef.Spec.Arguments) > 0 { + if task.Spec.Arguments == nil { + task.Spec.Arguments = make(map[string]string) + } + maps.Copy(task.Spec.Arguments, taskDef.Spec.Arguments) + } + + task.Spec.Pod.NodeName = resolvedNodename + task.Spec.NodeName = resolvedNodename + task.Spec.State = environment.Spec.State + + task.Labels = labels(environment, nodename) + + if err := controllerutil.SetControllerReference(environment, task, r.Scheme); err != nil { + log.Error(err, "failed to set controller reference", "task", task.Name) + return &ctrl.Result{}, err + } + + if err := r.Create(ctx, task); err != nil { + if err = client.IgnoreAlreadyExists(err); err != nil { + log.Error(err, "Failed to create task on node", "nodename", nodename, "task", task.Name) + return &ctrl.Result{}, err + // TODO: add error handling + } + } + + } + + return nil, nil +} + +// create label to bunch tasks to the environment and node +func labels(environment *aliecsv1alpha1.Environment, nodename string) map[string]string { + return map[string]string{ + "environment": environment.Name, + "node": nodename, + } +} + +const environmentFinalizer = "aliecs.alice.cern/environment-finalizer" + +// +kubebuilder:rbac:groups=aliecs.alice.cern,resources=environments,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=aliecs.alice.cern,resources=environments/status,verbs=get;update;patch +// +kubebuilder:rbac:groups=aliecs.alice.cern,resources=environments/finalizers,verbs=update +// +kubebuilder:rbac:groups=aliecs.alice.cern,resources=tasktemplates,verbs=get;list;watch +// +kubebuilder:rbac:groups=core,resources=nodes,verbs=get;list;watch + +// Reconcile is part of the main kubernetes reconciliation loop which aims to +// move the current state of the cluster closer to the desired state. +// TODO(user): Modify the Reconcile function to compare the state specified by +// the Environment object against the actual cluster state, and then +// perform operations to make the cluster state reflect the state specified by +// the user. +// +// For more details, check Reconcile and its Result here: +// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.23.1/pkg/reconcile +func (r *EnvironmentReconciler) handleFinalizer(ctx context.Context, environment *aliecsv1alpha1.Environment, log logr.Logger) (ctrl.Result, bool, error) { + if environment.DeletionTimestamp.IsZero() { + if controllerutil.ContainsFinalizer(environment, environmentFinalizer) { + // finalizer already in place, nothing to do: continue reconciliation + return ctrl.Result{}, false, nil + } + controllerutil.AddFinalizer(environment, environmentFinalizer) + return ctrl.Result{}, true, r.Update(ctx, environment) + } + + // environment is being deleted + if !controllerutil.ContainsFinalizer(environment, environmentFinalizer) { + return ctrl.Result{}, true, nil + } + + tasks := &aliecsv1alpha1.TaskList{} + if err := r.List(ctx, tasks, client.InNamespace(environment.Namespace), client.MatchingLabels{"environment": environment.Name}); err != nil { + return ctrl.Result{}, true, err + } + + if len(tasks.Items) > 0 { + for i := range tasks.Items { + if !tasks.Items[i].DeletionTimestamp.IsZero() { + continue + } + if err := r.Delete(ctx, &tasks.Items[i]); err != nil && !k8serrors.IsNotFound(err) { + return ctrl.Result{}, true, err + } + } + log.Info("waiting for tasks to be deleted before removing environment", "remaining", len(tasks.Items)) + return ctrl.Result{}, true, nil + } + + controllerutil.RemoveFinalizer(environment, environmentFinalizer) + return ctrl.Result{}, true, r.Update(ctx, environment) +} + +func (r *EnvironmentReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + log := logf.FromContext(ctx) + + environment := &aliecsv1alpha1.Environment{} + if err := r.Get(ctx, req.NamespacedName, environment); err != nil { + return ctrl.Result{}, client.IgnoreNotFound(err) + } + + if res, stop, err := r.handleFinalizer(ctx, environment, log); err != nil || stop { + return res, err + } + + if environment.Status.State == "" { + nodeNames, err := r.listAndParseNodes(ctx) + if err != nil { + return ctrl.Result{}, err + } + + for nodename, tasksReferences := range environment.TaskTemplates.Tasks { + log.Info("creating tasks for hostname from references", "hostname", nodename, "number of tasks", len(tasksReferences)) + resolvedName, err := resolveNodeName(nodeNames, nodename) + if err != nil { + return ctrl.Result{}, err + } + + if res, err := r.runTasksFromReferenceOnNode(ctx, tasksReferences, nodename, resolvedName, req, environment, log); res != nil { + return *res, err + } + } + + for nodename, taskTemplates := range environment.Spec.Tasks { + log.Info("creating tasks for hostname from definitions", "hostname", nodename, "number of tasks", len(taskTemplates)) + resolvedName, err := resolveNodeName(nodeNames, nodename) + if err != nil { + return ctrl.Result{}, err + } + + if res, err := r.runTaskFromDefinitionOnNode(ctx, taskTemplates, nodename, resolvedName, req, environment, log); res != nil { + return *res, err + } + } + } + + tasks := &aliecsv1alpha1.TaskList{} + if err := r.List(ctx, tasks, client.InNamespace(environment.Namespace), client.MatchingLabels{"environment": environment.Name}); err != nil { + log.Error(err, "failed to get list of tasks for this environment") + return ctrl.Result{}, err + } + + if environment.Status.Tasks == nil { + environment.Status.Tasks = make(map[string]map[string]string) + } + + for _, task := range tasks.Items { + nodename := task.Spec.Pod.NodeName + if environment.Status.Tasks[nodename] == nil { + environment.Status.Tasks[nodename] = make(map[string]string) + } + + environment.Status.Tasks[nodename][task.Name] = task.Status.State + } + + environment.Status.State = aggregateState(tasks.Items, environment.Status.State) + + for _, task := range tasks.Items { + if task.Spec.State != environment.Spec.State { + patch := client.MergeFrom(task.DeepCopy()) + task.Spec.State = environment.Spec.State + if err := r.Patch(ctx, &task, patch); err != nil { + log.Error(err, "failed to patch task state", "task", task.Name) + } + } + } + + if err := r.Status().Update(ctx, environment); err != nil { + if k8serrors.IsConflict(err) { + return ctrl.Result{Requeue: true}, nil + } + log.Error(err, "failed to update status") + return ctrl.Result{}, err + } + + return ctrl.Result{}, nil +} + +var filterList = []string{"cern", "ch"} + +// listAndParseNodes asks k8s cluster for all nodes and it creates map out of them containing the node names +// and also partial node names while filtering those from `filterList`, so map will contain following for example: +// { "flp001.cern.ch": "flp001.cern.ch", "flp001": "flp001.cern.ch" } +func (r *EnvironmentReconciler) listAndParseNodes(ctx context.Context) (map[string]string, error) { + nodeList := &v1.NodeList{} + nodeNames := make(map[string]string) + if err := r.List(ctx, nodeList); err != nil { + return nodeNames, fmt.Errorf("listing nodes: %w", err) + } + + for _, node := range nodeList.Items { + if val, ok := nodeNames[node.Name]; ok { + return nil, fmt.Errorf("duplicate node names %s and %s ", node.Name, val) + } + nodeNames[node.Name] = node.Name + for _, nodePart := range strings.Split(node.Name, ".") { + if !slices.Contains(filterList, nodePart) { + if val, ok := nodeNames[nodePart]; ok { + return nil, fmt.Errorf("duplicate partial node names %s and %s ", nodePart, val) + } + nodeNames[nodePart] = node.Name + } + } + } + + return nodeNames, nil +} + +// resolveNodeNames checks created map in listAndParseNodes for any match +func resolveNodeName(nodes map[string]string, nodename string) (string, error) { + if res, ok := nodes[nodename]; !ok { + return "", fmt.Errorf("node not in cluster: %s", nodename) + } else { + return res, nil + } +} + +func aggregateState(tasks []aliecsv1alpha1.Task, previousState string) string { + if len(tasks) == 0 { + return previousState + } + + first := tasks[0].Status.State + allSame := true + for _, task := range tasks { + if task.Status.State == "error" { + return "error" + } + if task.Status.State != first { + allSame = false + } + } + + if allSame { + return first + } + return previousState +} + +// SetupWithManager sets up the controller with the Manager. +func (r *EnvironmentReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&aliecsv1alpha1.Environment{}). + Owns(&aliecsv1alpha1.Task{}). + Watches(&aliecsv1alpha1.TaskTemplate{}, handler.EnqueueRequestsFromMapFunc(func(ctx context.Context, obj client.Object) []reconcile.Request { + return nil + })). + Named("environment"). + Complete(r) +} diff --git a/control-operator/internal/controller/environment_controller_test.go b/control-operator/internal/controller/environment_controller_test.go new file mode 100644 index 000000000..8a7532f7f --- /dev/null +++ b/control-operator/internal/controller/environment_controller_test.go @@ -0,0 +1,98 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2026 CERN and copyright holders of ALICE O². + * Author: Michal Tichak + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package controller + +import ( + "context" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + aliecsv1alpha1 "github.com/AliceO2Group/Control/control-operator/api/v1alpha1" +) + +var _ = Describe("Environment Controller", func() { + Context("When reconciling a resource", func() { + const resourceName = "test-resource" + + ctx := context.Background() + + typeNamespacedName := types.NamespacedName{ + Name: resourceName, + Namespace: "default", // TODO(user):Modify as needed + } + environment := &aliecsv1alpha1.Environment{} + + BeforeEach(func() { + By("creating the custom resource for the Kind Environment") + err := k8sClient.Get(ctx, typeNamespacedName, environment) + if err != nil && errors.IsNotFound(err) { + resource := &aliecsv1alpha1.Environment{ + ObjectMeta: metav1.ObjectMeta{ + Name: resourceName, + Namespace: "default", + }, + Spec: aliecsv1alpha1.EnvironmentSpec{ + State: "standby", + Tasks: map[string][]aliecsv1alpha1.TaskDefinition{}, + }, + TaskTemplates: aliecsv1alpha1.TemplateSpecification{ + Tasks: map[string][]aliecsv1alpha1.TaskReference{}, + }, + } + Expect(k8sClient.Create(ctx, resource)).To(Succeed()) + } + }) + + AfterEach(func() { + // TODO(user): Cleanup logic after each test, like removing the resource instance. + resource := &aliecsv1alpha1.Environment{} + err := k8sClient.Get(ctx, typeNamespacedName, resource) + Expect(err).NotTo(HaveOccurred()) + + By("Cleanup the specific resource instance Environment") + Expect(k8sClient.Delete(ctx, resource)).To(Succeed()) + }) + It("should successfully reconcile the resource", func() { + By("Reconciling the created resource") + controllerReconciler := &EnvironmentReconciler{ + Client: k8sClient, + Scheme: k8sClient.Scheme(), + } + + _, err := controllerReconciler.Reconcile(ctx, reconcile.Request{ + NamespacedName: typeNamespacedName, + }) + Expect(err).NotTo(HaveOccurred()) + // TODO(user): Add more specific assertions depending on your controller's reconciliation logic. + // Example: If you expect a certain status condition after reconciliation, verify it here. + }) + }) +}) diff --git a/control-operator/internal/controller/fairmq_transition.go b/control-operator/internal/controller/fairmq_transition.go new file mode 100644 index 000000000..1fe9cabfd --- /dev/null +++ b/control-operator/internal/controller/fairmq_transition.go @@ -0,0 +1,223 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2024 CERN and copyright holders of ALICE O². + * Author: Michal Tichak + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package controller + +import ( + "context" + "fmt" + "strings" + + pb "github.com/AliceO2Group/Control/control-operator/internal/controller/protos/generated" +) + +// FairMQ internal state names as used by the OCC plugin +const ( + fmqOK = "OK" + fmqIdle = "IDLE" + fmqInitializingDevice = "INITIALIZING DEVICE" + fmqInitialized = "INITIALIZED" + fmqBound = "BOUND" + fmqDeviceReady = "DEVICE READY" + fmqReady = "READY" + fmqRunning = "RUNNING" + fmqError = "ERROR" + fmqExiting = "EXITING" +) + +// FairMQ transition event names as used by the OCC plugin +const ( + fmqEvtInitDevice = "INIT DEVICE" + fmqEvtCompleteInit = "COMPLETE INIT" + fmqEvtBind = "BIND" + fmqEvtConnect = "CONNECT" + fmqEvtInitTask = "INIT TASK" + fmqEvtRun = "RUN" + fmqEvtStop = "STOP" + fmqEvtResetTask = "RESET TASK" + fmqEvtResetDevice = "RESET DEVICE" + fmqEvtEnd = "END" + fmqEvtAuto = "Auto" +) + +// fmqToOCCState maps FairMQ internal states to lowercase OCC state names +var fmqToOCCState = map[string]string{ + fmqIdle: "standby", + fmqReady: "configured", + fmqRunning: "running", + fmqError: "error", + fmqExiting: "done", +} + +func occStateForFmqState(fmqState string) string { + if occ, ok := fmqToOCCState[strings.ToUpper(fmqState)]; ok { + return occ + } + // TODO: it might be better to do proper error handling for doFairMQStep when intermediate step fail + return "not-mapped" +} + +// doFairMQStep sends a single FairMQ-level gRPC Transition and returns the +// resulting FairMQ state as reported by the OCC plugin. +func (c *OccClient) doFairMQStep(ctx context.Context, srcFmqState, fmqEvent string, args map[string]string) (string, error) { + var configEntries []*pb.ConfigEntry + for k, v := range args { + configEntries = append(configEntries, &pb.ConfigEntry{Key: k, Value: v}) + } + + request := &pb.TransitionRequest{ + SrcState: srcFmqState, + TransitionEvent: fmqEvent, + Arguments: configEntries, + } + c.log.V(1).Info("FairMQ step", "event", fmqEvent, "src", srcFmqState) + + reply, err := c.client.Transition(ctx, request) + if err != nil { + return "", fmt.Errorf("FairMQ gRPC transition %q failed: %w", fmqEvent, err) + } + + resultState := reply.GetState() + if !reply.GetOk() { + return resultState, fmt.Errorf("FairMQ transition %q not ok, state: %s", fmqEvent, resultState) + } + c.log.V(1).Info("FairMQ step done", "event", fmqEvent, "result", resultState) + return resultState, nil +} + +// fairMQConfigure drives the FairMQ device through the 5-step CONFIGURE sequence: +// +// IDLE → (INIT DEVICE + args) → INITIALIZING DEVICE +// → (COMPLETE INIT) → INITIALIZED +// → (BIND) → BOUND +// → (CONNECT) → DEVICE READY +// → (INIT TASK) → READY (= CONFIGURED in OCC terms) +// +// On failure in any intermediate step, a RESET DEVICE rollback is attempted. +func (c *OccClient) fairMQConfigure(ctx context.Context, args map[string]string) (string, error) { + // Step 1: INIT DEVICE — channel config args go here + state, err := c.doFairMQStep(ctx, fmqIdle, fmqEvtInitDevice, args) + if state != fmqInitializingDevice { + return occStateForFmqState(state), fmt.Errorf("INIT DEVICE: expected %s, got %s: %w", fmqInitializingDevice, state, err) + } + + // Step 2: COMPLETE INIT + state, err = c.doFairMQStep(ctx, fmqInitializingDevice, fmqEvtCompleteInit, nil) + if state != fmqInitialized { + return occStateForFmqState(state), fmt.Errorf("COMPLETE INIT: expected %s, got %s: %w", fmqInitialized, state, err) + } + + // Step 3: BIND + state, err = c.doFairMQStep(ctx, fmqInitialized, fmqEvtBind, nil) + if state == fmqInitialized { + // Stuck — roll back to IDLE + rollback, _ := c.doFairMQStep(ctx, fmqInitialized, fmqEvtResetDevice, nil) + return occStateForFmqState(rollback), fmt.Errorf("BIND: stuck in %s, rolled back to %s", fmqInitialized, rollback) + } else if state != fmqBound { + return occStateForFmqState(state), fmt.Errorf("BIND: expected %s, got %s: %w", fmqBound, state, err) + } + + // Step 4: CONNECT + state, err = c.doFairMQStep(ctx, fmqBound, fmqEvtConnect, nil) + if state == fmqBound { + // Stuck — roll back to IDLE + rollback, _ := c.doFairMQStep(ctx, fmqBound, fmqEvtResetDevice, nil) + return occStateForFmqState(rollback), fmt.Errorf("CONNECT: stuck in %s, rolled back to %s", fmqBound, rollback) + } else if state != fmqDeviceReady { + return occStateForFmqState(state), fmt.Errorf("CONNECT: expected %s, got %s: %w", fmqDeviceReady, state, err) + } + + // Step 5: INIT TASK + state, err = c.doFairMQStep(ctx, fmqDeviceReady, fmqEvtInitTask, nil) + if state == fmqDeviceReady { + // Stuck — roll back to IDLE + rollback, _ := c.doFairMQStep(ctx, fmqDeviceReady, fmqEvtResetDevice, nil) + return occStateForFmqState(rollback), fmt.Errorf("INIT TASK: stuck in %s, rolled back to %s", fmqDeviceReady, rollback) + } + + return occStateForFmqState(state), err +} + +// fairMQReset drives the FairMQ device through the 2-step RESET sequence: +// +// READY → (RESET TASK) → DEVICE READY +// → (RESET DEVICE + args) → IDLE (= STANDBY in OCC terms) +func (c *OccClient) fairMQReset(ctx context.Context, args map[string]string) (string, error) { + // Step 1: RESET TASK + state, err := c.doFairMQStep(ctx, fmqReady, fmqEvtResetTask, nil) + if state != fmqDeviceReady { + return occStateForFmqState(state), fmt.Errorf("RESET TASK: expected %s, got %s: %w", fmqDeviceReady, state, err) + } + + // Step 2: RESET DEVICE — args go here, matching executor doReset behaviour + state, err = c.doFairMQStep(ctx, fmqDeviceReady, fmqEvtResetDevice, args) + if state == fmqDeviceReady { + // Stuck — roll back to READY + rollback, _ := c.doFairMQStep(ctx, fmqDeviceReady, fmqEvtInitTask, nil) + return occStateForFmqState(rollback), fmt.Errorf("RESET DEVICE: stuck in %s, rolled back to %s", fmqDeviceReady, rollback) + } + + return occStateForFmqState(state), err +} + +// FairMQTransitionRequest drives a FairMQ OCC device through the multi-step +// sequence that corresponds to a single OCC-level transition (CONFIGURE, RESET, +// START, STOP). Returns the resulting OCC state in lowercase ("configured", +// "running", "standby", "error"). +func (c *OccClient) FairMQTransitionRequest(ctx context.Context, fromState, toState string, args map[string]string) (string, error) { + if c == nil || c.client == nil { + return fromState, fmt.Errorf("nil client for FairMQTransitionRequest") + } + + from, err := StateFromString(fromState) + if err != nil { + return fromState, err + } + to, err := StateFromString(toState) + if err != nil { + return fromState, err + } + + transition, err := FromStatesToTransition(from, to) + if err != nil { + return fromState, err + } + + c.log.Info("FairMQTransitionRequest", "from", fromState, "to", toState, "transition", transition.String(), "args", args) + + switch transition { + case CONFIGURE: + return c.fairMQConfigure(ctx, args) + case RESET: + return c.fairMQReset(ctx, args) + case START: + state, err := c.doFairMQStep(ctx, fmqReady, fmqEvtRun, args) + return occStateForFmqState(state), err + case STOP: + state, err := c.doFairMQStep(ctx, fmqRunning, fmqEvtStop, args) + return occStateForFmqState(state), err + default: + return fromState, fmt.Errorf("FairMQ transition %s not implemented", transition.String()) + } +} diff --git a/control-operator/internal/controller/grpc_client.go b/control-operator/internal/controller/grpc_client.go new file mode 100644 index 000000000..1ae82df0c --- /dev/null +++ b/control-operator/internal/controller/grpc_client.go @@ -0,0 +1,310 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2026 CERN and copyright holders of ALICE O². + * Author: Michal Tichak + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package controller + +import ( + "context" + "errors" + "fmt" + "strings" + "time" + + "github.com/go-logr/logr" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/connectivity" + "google.golang.org/grpc/credentials/insecure" + "google.golang.org/grpc/status" + "k8s.io/apimachinery/pkg/types" + + aliecsv1alpha1 "github.com/AliceO2Group/Control/control-operator/api/v1alpha1" + pb "github.com/AliceO2Group/Control/control-operator/internal/controller/protos/generated" +) + +// jsonOccClient mirrors executor/executorcmd/nopb/occclient.go: uses short method +// names and JSON content subtype for OCC lite / FairMQ processes. +type jsonOccClient struct{ conn *grpc.ClientConn } + +type jsonEventStreamClient struct{ grpc.ClientStream } + +func (x *jsonEventStreamClient) Recv() (*pb.EventStreamReply, error) { + m := new(pb.EventStreamReply) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +type jsonStateStreamClient struct{ grpc.ClientStream } + +func (x *jsonStateStreamClient) Recv() (*pb.StateStreamReply, error) { + m := new(pb.StateStreamReply) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +func (c *jsonOccClient) EventStream(ctx context.Context, in *pb.EventStreamRequest, opts ...grpc.CallOption) (pb.Occ_EventStreamClient, error) { + opts = append(opts, grpc.CallContentSubtype("json")) + stream, err := c.conn.NewStream(ctx, &grpc.StreamDesc{StreamName: "EventStream", ServerStreams: true}, "EventStream", opts...) + if err != nil { + return nil, err + } + x := &jsonEventStreamClient{stream} + if err := x.SendMsg(in); err != nil { + return nil, err + } + if err := x.CloseSend(); err != nil { + return nil, err + } + return x, nil +} + +func (c *jsonOccClient) StateStream(ctx context.Context, in *pb.StateStreamRequest, opts ...grpc.CallOption) (pb.Occ_StateStreamClient, error) { + opts = append(opts, grpc.CallContentSubtype("json")) + stream, err := c.conn.NewStream(ctx, &grpc.StreamDesc{StreamName: "StateStream", ServerStreams: true}, "StateStream", opts...) + if err != nil { + return nil, err + } + x := &jsonStateStreamClient{stream} + if err := x.SendMsg(in); err != nil { + return nil, err + } + if err := x.CloseSend(); err != nil { + return nil, err + } + return x, nil +} + +func (c *jsonOccClient) GetState(ctx context.Context, in *pb.GetStateRequest, opts ...grpc.CallOption) (*pb.GetStateReply, error) { + out := new(pb.GetStateReply) + opts = append(opts, grpc.CallContentSubtype("json")) + if err := c.conn.Invoke(ctx, "GetState", in, out, opts...); err != nil { + return nil, err + } + return out, nil +} + +func (c *jsonOccClient) Transition(ctx context.Context, in *pb.TransitionRequest, opts ...grpc.CallOption) (*pb.TransitionReply, error) { + out := new(pb.TransitionReply) + opts = append(opts, grpc.CallContentSubtype("json")) + if err := c.conn.Invoke(ctx, "Transition", in, out, opts...); err != nil { + return nil, err + } + return out, nil +} + +type OccClient struct { + client pb.OccClient + conn *grpc.ClientConn + crdName types.NamespacedName + reconciler *TaskReconciler + cancel *context.CancelFunc + log logr.Logger + controlMode string +} + +// fromDeviceState translates a raw device state to an OCC state name, +// mirroring executor/executorcmd/transitioner FairMQ and Direct FromDeviceState logic. +func fromDeviceState(controlMode string, state string) string { + if controlMode == "fairmq" { + return occStateForFmqState(state) + } + return strings.ToLower(state) +} + +func NewOccClient(ctx context.Context, address string, controlMode string, reconciler *TaskReconciler, crdName types.NamespacedName, log logr.Logger) (*OccClient, error) { + // grpc.WithBlock() ensures that the dialer waits for the connection to be established. + // If the server isn't listening, this will return an error after the context timeout. + conn, err := grpc.NewClient(address, grpc.WithTransportCredentials(insecure.NewCredentials())) + if err != nil { + return nil, err + } + + var occClient pb.OccClient + if controlMode == "fairmq" { + occClient = &jsonOccClient{conn} + } else { + occClient = pb.NewOccClient(conn) + } + + client := &OccClient{client: occClient, conn: conn, crdName: crdName, reconciler: reconciler, log: log, controlMode: controlMode} + + client.conn.Connect() + + // ctxWithTimeout, cancel := context.WithTimeout(ctx, 1*time.Second) + // defer cancel() + // client.GetState(ctxWithTimeout) + + return client, nil +} + +func (c *OccClient) ConsumeIfReady(ctx context.Context) bool { + if c.cancel != nil { + return true + } + + if connState := c.conn.GetState(); connState != connectivity.Ready { + c.log.V(1).Info("connection is in different state than ready", "conn state", connState.String()) + return false + } + // clientCtx, clientCancel := context.WithCancel(context.Background()) + _, clientCancel := context.WithCancel(context.Background()) + c.cancel = &clientCancel + + // TODO: Streams are not used anywhere now so we turn them off until they are fixed/useful + // they are not useful for now because any Transition call imediately returns final + // status. So for now we don't need them. Of course the problem is that if something + // changes inside OCC container we have no way how to detect it now. + // go c.ConsumeEventStream(clientCtx) + // go c.ConsumeStateStream(clientCtx) + return true +} + +func (c *OccClient) WaitUntilConnected(ctx context.Context) error { + for { + state := c.conn.GetState() + if state == connectivity.Ready { + return nil + } + + if !c.conn.WaitForStateChange(ctx, state) { + return fmt.Errorf("connection failed: %w", ctx.Err()) + } + } +} + +func (c *OccClient) Close() error { + if c.cancel != nil { + (*c.cancel)() + } + if c.conn != nil { + return c.conn.Close() + } + return nil +} + +func (c *OccClient) GetState(ctx context.Context) (*pb.GetStateReply, error) { + if c == nil || c.client == nil { + return nil, errors.New("nil client for TransitionRequest") + } + c.log.V(1).Info("GetState") + result, err := c.client.GetState(ctx, &pb.GetStateRequest{}) + if err == nil { + result.State = fromDeviceState(c.controlMode, result.State) + } + return result, err +} + +func (c *OccClient) TransitionRequest(ctx context.Context, fromState string, toState string, args map[string]string) (*pb.TransitionReply, error) { + if c == nil || c.client == nil { + return nil, errors.New("nil client for TransitionRequest") + } + + from, err := StateFromString(fromState) + if err != nil { + return nil, err + } + to, err := StateFromString(toState) + if err != nil { + return nil, err + } + + transition, err := FromStatesToTransition(from, to) + if err != nil { + return nil, err + } + + var configEntries []*pb.ConfigEntry + for k, v := range args { + configEntries = append(configEntries, &pb.ConfigEntry{Key: k, Value: v}) + } + + request := &pb.TransitionRequest{ + SrcState: strings.ToUpper(fromState), + TransitionEvent: strings.ToUpper(transition.String()), + Arguments: configEntries, + } + c.log.V(1).Info("TransitionRequest", "req", request) + + return c.client.Transition(ctx, request) +} + +func (c *OccClient) ConsumeEventStream(ctx context.Context) { + c.log.Info("starting to consume EventStream") + stream, err := c.client.EventStream(ctx, &pb.EventStreamRequest{}) + if err != nil { + c.log.Error(err, "failed to start event stream") + return + } + for { + resp, err := stream.Recv() + if err != nil { + if st, ok := status.FromError(err); ok && st.Code() == codes.Canceled { + c.log.Info("EventStream stopped: context cancelled") + return + } + c.log.Error(err, "EventStream stopped with error") + return + } + c.log.Info("received event", "event", resp.GetEvent()) + } +} + +func (c *OccClient) ConsumeStateStream(ctx context.Context) { + c.log.Info("starting to consume StateStream") + stream, err := c.client.StateStream(ctx, &pb.StateStreamRequest{}) + if err != nil { + c.log.Error(err, "failed to start state stream") + return + } + + for { + resp, err := stream.Recv() + if err != nil { + if st, ok := status.FromError(err); ok && st.Code() == codes.Canceled { + c.log.Info("StateStream stopped: context cancelled") + return + } + c.log.Error(err, "StateStream stopped with error") + return + } + c.log.V(1).Info("received state update", "type", resp.GetType(), "state", resp.GetState()) + + task := &aliecsv1alpha1.Task{} + if err := c.reconciler.Get(ctx, c.crdName, task); err != nil { + c.log.V(1).Error(err, "state event could not find task") + continue + } + // TODO: add some checks?? + task.Status.State = fromDeviceState(c.controlMode, resp.GetState()) + updateCtx, cancel := context.WithTimeout(context.Background(), time.Second) + if err := c.reconciler.Status().Update(updateCtx, task); err != nil { + c.log.Error(err, "state event did not apply state change to task", "state", task.Status.State) + } + cancel() + } +} diff --git a/control-operator/internal/controller/jsoncodec.go b/control-operator/internal/controller/jsoncodec.go new file mode 100644 index 000000000..a4070f8e6 --- /dev/null +++ b/control-operator/internal/controller/jsoncodec.go @@ -0,0 +1,49 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2026 CERN and copyright holders of ALICE O². + * Author: Michal Tichak + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package controller + +import ( + "encoding/json" + + "google.golang.org/grpc/encoding" +) + +func init() { + encoding.RegisterCodec(&jsonCodec{}) +} + +type jsonCodec struct{} + +func (*jsonCodec) Marshal(v interface{}) ([]byte, error) { + return json.Marshal(v) +} + +func (*jsonCodec) Unmarshal(data []byte, v interface{}) error { + return json.Unmarshal(data, v) +} + +func (*jsonCodec) Name() string { + return "json" +} diff --git a/control-operator/internal/controller/occ_server_test.go b/control-operator/internal/controller/occ_server_test.go new file mode 100644 index 000000000..13a73e281 --- /dev/null +++ b/control-operator/internal/controller/occ_server_test.go @@ -0,0 +1,137 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2026 CERN and copyright holders of ALICE O². + * Author: Michal Tichak + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package controller + +import ( + "context" + "errors" + "net" + "strings" + "sync" + + "google.golang.org/grpc" + + pb "github.com/AliceO2Group/Control/control-operator/internal/controller/protos/generated" +) + +// occServerStub is an in-process implementation of the OCC gRPC service defined +// in occ/protos/occ.proto. Embedding pb.UnimplementedOccServer satisfies the +// generated interface and keeps this type valid if the service gains methods. +// The state is guarded because the gRPC server answers on its own goroutines while +// the test reads and writes it. +type occServerStub struct { + pb.UnimplementedOccServer + + returnError bool + mu sync.RWMutex + state string +} + +func (s *occServerStub) setState(state string) { + s.mu.Lock() + defer s.mu.Unlock() + s.state = state +} + +func (s *occServerStub) getState() string { + s.mu.RLock() + defer s.mu.RUnlock() + return s.state +} + +func (s *occServerStub) GetState(ctx context.Context, req *pb.GetStateRequest) (*pb.GetStateReply, error) { + if s.returnError == true { + return nil, errors.New("returning error as requested by user") + } + return &pb.GetStateReply{State: s.getState()}, nil +} + +// Transition answers with the state the requested event leads to, looked up in the +// same FSM table the controller used to pick the event, and moves the stub to that +// state so a following GetState reports it. +func (s *occServerStub) Transition(ctx context.Context, req *pb.TransitionRequest) (*pb.TransitionReply, error) { + if s.returnError == true { + return nil, errors.New("returning error as requested by user") + } + + src, err := StateFromString(strings.ToLower(req.GetSrcState())) + if err != nil { + return nil, err + } + event, err := TransitionFromString(strings.ToLower(req.GetTransitionEvent())) + if err != nil { + return nil, err + } + + for fromTo, transition := range fromStatesToTransition { + if fromTo.from == src && transition == event { + newState := fromTo.to.String() + s.setState(newState) + return &pb.TransitionReply{ + Trigger: pb.StateChangeTrigger_EXECUTOR, + State: newState, + TransitionEvent: req.GetTransitionEvent(), + Ok: true, + }, nil + } + } + + // No rule for this event in this state, so the device stays where it is. + return &pb.TransitionReply{ + Trigger: pb.StateChangeTrigger_EXECUTOR, + State: src.String(), + TransitionEvent: req.GetTransitionEvent(), + Ok: false, + }, nil +} + +func (s *occServerStub) StateStream(req *pb.StateStreamRequest, srv grpc.ServerStreamingServer[pb.StateStreamReply]) error { + <-srv.Context().Done() + return nil +} + +func (s *occServerStub) EventStream(req *pb.EventStreamRequest, srv grpc.ServerStreamingServer[pb.EventStreamReply]) error { + <-srv.Context().Done() + return nil +} + +// startOccServerStub serves a stub on a kernel-assigned loopback port and +// returns it together with that port and a stop function. +func startOccServerStub() (*occServerStub, int, func(), error) { + lis, err := net.Listen("tcp", "127.0.0.1:0") + if err != nil { + return nil, 0, nil, err + } + + stub := &occServerStub{} + srv := grpc.NewServer() + pb.RegisterOccServer(srv, stub) + + go func() { + _ = srv.Serve(lis) + }() + + return stub, lis.Addr().(*net.TCPAddr).Port, srv.Stop, nil +} diff --git a/control-operator/internal/controller/protos/occ.proto b/control-operator/internal/controller/protos/occ.proto new file mode 100644 index 000000000..ca00369c1 --- /dev/null +++ b/control-operator/internal/controller/protos/occ.proto @@ -0,0 +1,99 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2018 CERN and copyright holders of ALICE O². + * Author: Teo Mrnjavac + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +syntax = "proto3"; + +package occ_pb; +option go_package = "github.com/AliceO2Group/ControlOperator/internal/controller/protos/generated;pb"; + +////////////////////////////////////////////////////// + +service Occ { + // We have to have a notification stream because the FairMQDevice might transition + // on its own for whatever reason. + rpc EventStream (EventStreamRequest) returns (stream EventStreamReply) {} + rpc StateStream (StateStreamRequest) returns (stream StateStreamReply) {} + rpc GetState (GetStateRequest) returns (GetStateReply) {} + rpc Transition (TransitionRequest) returns (TransitionReply) {} +} + +enum StateChangeTrigger { + EXECUTOR = 0; + DEVICE_INTENTIONAL = 1; + DEVICE_ERROR = 2; +} + +enum StateType { + STATE_STABLE = 0; + STATE_INTERMEDIATE = 1; +} + +enum DeviceEventType { + NULL_DEVICE_EVENT = 0; + END_OF_STREAM = 1; + BASIC_TASK_TERMINATED = 2; + TASK_INTERNAL_ERROR = 3; +} + +message StateStreamRequest {} + +message StateStreamReply { + StateType type = 1; + string state = 2; +} + +message EventStreamRequest {} + +message DeviceEvent { + DeviceEventType type = 1; +} + +message EventStreamReply { + DeviceEvent event = 1; +} + +message GetStateRequest {} + +message GetStateReply { + string state = 1; + int32 pid = 2; +} + +message ConfigEntry { + string key = 1; + string value = 2; +} + +message TransitionRequest { + string srcState = 1; + string transitionEvent = 2; + repeated ConfigEntry arguments = 3; +} + +message TransitionReply { + StateChangeTrigger trigger = 1; + string state = 2; + string transitionEvent = 3; + bool ok = 4; +} diff --git a/control-operator/internal/controller/suite_test.go b/control-operator/internal/controller/suite_test.go new file mode 100644 index 000000000..f9d2f5c41 --- /dev/null +++ b/control-operator/internal/controller/suite_test.go @@ -0,0 +1,89 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2026 CERN and copyright holders of ALICE O². + * Author: Michal Tichak + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package controller + +import ( + "path/filepath" + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/rest" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/envtest" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/log/zap" + + aliecsv1alpha1 "github.com/AliceO2Group/Control/control-operator/api/v1alpha1" + //+kubebuilder:scaffold:imports +) + +// These tests use Ginkgo (BDD-style Go testing framework). Refer to +// http://onsi.github.io/ginkgo/ to learn more about Ginkgo. + +var ( + cfg *rest.Config + k8sClient client.Client + testEnv *envtest.Environment +) + +func TestControllers(t *testing.T) { + RegisterFailHandler(Fail) + + RunSpecs(t, "Controller Suite") +} + +var _ = BeforeSuite(func() { + logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true))) + + By("bootstrapping test environment") + testEnv = &envtest.Environment{ + CRDDirectoryPaths: []string{filepath.Join("..", "..", "config", "crd", "bases")}, + ErrorIfCRDPathMissing: true, + } + + var err error + // cfg is defined in this file globally. + cfg, err = testEnv.Start() + Expect(err).NotTo(HaveOccurred()) + Expect(cfg).NotTo(BeNil()) + + err = aliecsv1alpha1.AddToScheme(scheme.Scheme) + Expect(err).NotTo(HaveOccurred()) + + //+kubebuilder:scaffold:scheme + + k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}) + Expect(err).NotTo(HaveOccurred()) + Expect(k8sClient).NotTo(BeNil()) +}) + +var _ = AfterSuite(func() { + By("tearing down the test environment") + err := testEnv.Stop() + Expect(err).NotTo(HaveOccurred()) +}) diff --git a/control-operator/internal/controller/task_controller.go b/control-operator/internal/controller/task_controller.go new file mode 100644 index 000000000..e5a39dad5 --- /dev/null +++ b/control-operator/internal/controller/task_controller.go @@ -0,0 +1,456 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2026 CERN and copyright holders of ALICE O². + * Author: Michal Tichak + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package controller + +import ( + "context" + "fmt" + "reflect" + "strings" + "sync" + "time" + + v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/tools/record" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/builder" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/predicate" + + aliecsv1alpha1 "github.com/AliceO2Group/Control/control-operator/api/v1alpha1" + "github.com/go-logr/logr" +) + +// TaskReconciler reconciles a Task object +type TaskReconciler struct { + client.Client + Scheme *runtime.Scheme + Recorder record.EventRecorder + NodeName string + MaxConcurrentReconciles int +} + +var clientsForContainers sync.Map + +const taskFinalizer string = "aliecs.alice.cern/finalizer" + +//+kubebuilder:rbac:groups=aliecs.alice.cern,resources=tasks,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups=aliecs.alice.cern,resources=tasks/status,verbs=get;update;patch +//+kubebuilder:rbac:groups=aliecs.alice.cern,resources=tasks/finalizers,verbs=update +//+kubebuilder:rbac:groups=core,resources=events,verbs=create;patch +//+kubebuilder:rbac:groups=core,resources=pods,verbs=get;list;watch;create;update;patch;delete + +// Reconcile is part of the main kubernetes reconciliation loop which aims to +// move the current state of the cluster closer to the desired state. +// TODO(user): Modify the Reconcile function to compare the state specified by +// the Task object against the actual cluster state, and then +// perform operations to make the cluster state reflect the state specified by +// the user. +// +// For more details, check Reconcile and its Result here: +// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.15.0/pkg/reconcile +// TODO: right now if POD fails and stops sooner, reconciliation creates a new one... +func (r *TaskReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + log := logf.FromContext(ctx) + + t := &aliecsv1alpha1.Task{} + if err := r.Get(ctx, req.NamespacedName, t); err != nil { + return ctrl.Result{}, client.IgnoreNotFound(err) + } + + log.V(1).Info("new reconcile request on existing Task Kind", "request", req) + + // Handle finalizers for clean deletion + if res, stop, err := r.handleFinalizer(ctx, t, log); err != nil || stop { + return res, err + } + + // TODO: add this check to direct_transition.go + if t.Status.State == "error" && t.Spec.State != "standby" { + return ctrl.Result{}, nil + } + + // Get existing pod or create new one, Reconciler owns the pod, so all changes to the pod and task will trigger + // reconciliation + // TODO: make this function + existingPod := &v1.Pod{} + err := r.Get(ctx, types.NamespacedName{ + Name: podNameFromTask(t.Name), + Namespace: t.Namespace, + }, existingPod) + if err != nil { + if errors.IsNotFound(err) { + pod := r.podForTask(t) + log.Info("Creating a new Pod", "Pod.Namespace", pod.Namespace, "Pod.Name", pod.Name) + if err = r.Create(ctx, pod); err != nil { + return ctrl.Result{}, err + } + return ctrl.Result{}, nil + } + return ctrl.Result{}, err + } + + if failed, reason := isPodFailed(existingPod); failed { + if t.Status.State != "error" { + log.Info("Pod failure detected, setting Task state to error", "reason", reason) + t.Status.State = "error" + t.Status.Error = reason + t.Status.Pod = *existingPod.Status.DeepCopy() + if err := r.Status().Update(ctx, t); err != nil { + return ctrl.Result{}, err + } + if err := r.recordCondition(ctx, t, aliecsv1alpha1.ConditionPodReady, metav1.ConditionFalse, "PodFailed", reason); err != nil { + return ctrl.Result{}, err + } + } + // Always stop reconciliation if the Pod is in a failed state + return ctrl.Result{}, nil + } + + if _, exists := clientsForContainers.Load(t.Name); !exists { + if existingPod.Status.PodIP == "" { + log.Info("pod doesn't have IP yet, we wait for different event") + return ctrl.Result{}, nil + } + if err := r.recordCondition(ctx, t, aliecsv1alpha1.ConditionPodReady, metav1.ConditionTrue, "PodRunning", fmt.Sprintf("Pod %s has IP %s", existingPod.Name, existingPod.Status.PodIP)); err != nil { + return ctrl.Result{}, err + } + res, err := r.createGRPCConsumer(ctx, t, existingPod, req.NamespacedName, log) + if err != nil || !res.IsZero() { + return res, err + } + } + + // when this succesfully returns we should be able to communicate via gRPC + if res := r.consumeGRPCConsumerIfReady(ctx, t, log); !res.IsZero() { + return res, nil + } + + // As far as I understand this is necessary because even though we have gRPC streams, we cannot rely + // on them being implemented + if t.Status.State == "" { + log.V(1).Info("Status.State is empty, querying container") + client, exists := clientsForContainers.Load(t.Name) + if !exists { + return ctrl.Result{Requeue: true}, nil + } + + stateReply, err := client.(*OccClient).GetState(ctx) + if err != nil { + log.Error(err, "Failed to GetState") + return ctrl.Result{RequeueAfter: 5 * time.Second}, nil + } + + log.V(1).Info("Current State inside POD is: ", "state", stateReply.State) + t.Status.State = stateReply.State + log.Info("Updating empty Task status ", "state", t.Status.State) + if err := r.Status().Update(ctx, t); err != nil { + return ctrl.Result{}, err + } + if err := r.recordCondition(ctx, t, aliecsv1alpha1.ConditionStateAccessible, metav1.ConditionTrue, "StateQueried", fmt.Sprintf("Initial state: %s", t.Status.State)); err != nil { + return ctrl.Result{}, err + } + return ctrl.Result{}, nil + } + + oldStatus := t.Status.DeepCopy() + + t.Status.Pod = *existingPod.Status.DeepCopy() + + // Handle Spec -> gRPC State Sync + if t.Status.State != t.Spec.State { + clientAny, exists := clientsForContainers.Load(t.Name) + if !exists { + return ctrl.Result{Requeue: true}, nil + } + + client := clientAny.(*OccClient) + + stateReply, err := client.GetState(ctx) + if err != nil { + log.Info("Failed to get state for sync, retrying in 5s", "error", err.Error()) + client.Close() + clientsForContainers.Delete(t.Name) + return ctrl.Result{RequeueAfter: 5 * time.Second}, nil + } + + if stateReply.GetState() != t.Spec.State { + var ( + newState string + transErr error + ) + + if t.Spec.Control.Mode == "fairmq" { + newState, transErr = client.FairMQTransitionRequest(ctx, stateReply.GetState(), t.Spec.State, t.Spec.Arguments) + } else { + reply, err := client.TransitionRequest(ctx, stateReply.GetState(), t.Spec.State, t.Spec.Arguments) + transErr = err + if err == nil && reply.GetOk() { + newState = strings.ToLower(reply.GetState()) + } + } + + if transErr != nil { + log.Error(transErr, "failed to Transition") + t.Status.Error = transErr.Error() + log.Info("Updating Task status") + if err := r.Status().Update(ctx, t); err != nil { + return ctrl.Result{}, err + } + if err := r.recordCondition(ctx, t, aliecsv1alpha1.ConditionStateTransitionResult, metav1.ConditionFalse, "TransitionFailed", fmt.Sprintf("Transition from %s to %s failed: %s", stateReply.GetState(), t.Spec.State, transErr.Error())); err != nil { + return ctrl.Result{}, err + } + return ctrl.Result{}, nil + } + if newState != "" { + log.V(1).Info("succeeded in transition", "new state", newState) + t.Status.State = newState + } + } + + } + + // Status Update: Only call the API if the status actually changed + // TODO: should I also add some query for GetState in case some Update or whatever fails? + // TODO: is this the best way? shouldn't we do the Update the moment something changes? + if !reflect.DeepEqual(oldStatus, &t.Status) { + log.Info("Updating Task status") + if err := r.Status().Update(ctx, t); err != nil { + return ctrl.Result{}, err + } + if t.Status.State != oldStatus.State { + if err := r.recordCondition(ctx, t, aliecsv1alpha1.ConditionStateTransitionResult, metav1.ConditionTrue, "TransitionComplete", fmt.Sprintf("Transitioned from %s to %s", oldStatus.State, t.Status.State)); err != nil { + return ctrl.Result{}, err + } + } + } + + return ctrl.Result{}, nil +} + +func (r *TaskReconciler) createGRPCConsumer(ctx context.Context, t *aliecsv1alpha1.Task, found *v1.Pod, taskName types.NamespacedName, log logr.Logger) (ctrl.Result, error) { + addr := fmt.Sprintf("%s:%d", found.Status.PodIP, t.Spec.Control.Port) + ctx, cancel := context.WithTimeout(ctx, 3*time.Second) + defer cancel() + + log.Info("creating gRPC client for task to task: ", "address", addr) + client, err := NewOccClient(ctx, addr, t.Spec.Control.Mode, r, taskName, log) + if err != nil { + log.Error(err, "failed to create gRPC client, retrying in 5 seconds") + // TODO: what would be proper error handling here? + return ctrl.Result{RequeueAfter: 5 * time.Second}, nil + } + + clientsForContainers.Store(t.Name, client) + + if err := r.recordCondition(ctx, t, aliecsv1alpha1.ConditionGRPCConnected, metav1.ConditionTrue, "Connected", fmt.Sprintf("gRPC connection established to %s", addr)); err != nil { + return ctrl.Result{}, err + } + return ctrl.Result{}, nil +} + +func (r *TaskReconciler) consumeGRPCConsumerIfReady(ctx context.Context, t *aliecsv1alpha1.Task, log logr.Logger) ctrl.Result { + client, exists := clientsForContainers.Load(t.Name) + + if !exists { + log.Info("didn't found existing client, retrying ", "task", t.Name) + return ctrl.Result{RequeueAfter: time.Second} + } + + if !client.(*OccClient).ConsumeIfReady(ctx) { + log.Info("gRPC client is not ready, retrying in 5 seconds", "name", t.Name) + return ctrl.Result{RequeueAfter: 5 * time.Second} + } + return ctrl.Result{} +} + +// handleFinalizer sets the finalizer, when Task isn't marked for deletion, and when deletion reconcile is triggered +// it then clears the gRPC connection and deletes the underlying POD and doesn't allow Task to be deleted +// until pod is gone. +// Note: if you add finalizer to a task you cannot delete it unless you remove the finalizer, run: +// kubectl patch task --type=json -p='[{"op": "remove", "path": "/metadata/finalizers"}]' +func (r *TaskReconciler) handleFinalizer(ctx context.Context, t *aliecsv1alpha1.Task, log logr.Logger) (result ctrl.Result, stopReconciliation bool, err error) { + if t.DeletionTimestamp.IsZero() { + if controllerutil.AddFinalizer(t, taskFinalizer) { + return ctrl.Result{}, true, r.Update(ctx, t) + } + return ctrl.Result{}, false, nil + } + + if !controllerutil.ContainsFinalizer(t, taskFinalizer) { + return ctrl.Result{}, true, nil + } + + log.Info("Finalizer found, starting cleanup") + r.cleargRPC(t, log) + + if podDeleted, err := r.deletePod(ctx, t, log); err != nil || !podDeleted { + return ctrl.Result{}, true, err + } + + controllerutil.RemoveFinalizer(t, taskFinalizer) + return ctrl.Result{}, true, r.Update(ctx, t) +} + +// deletePod deletes the Task's Pod if it still exists, and reports whether it is +// fully gone yet so the caller can wait for termination before removing the finalizer. +func (r *TaskReconciler) deletePod(ctx context.Context, t *aliecsv1alpha1.Task, log logr.Logger) (podDeleted bool, err error) { + pod := &v1.Pod{} + err = r.Get(ctx, types.NamespacedName{Name: podNameFromTask(t.Name), Namespace: t.Namespace}, pod) + if errors.IsNotFound(err) { + log.Info("Pod deleted") + return true, nil + } else if err != nil { + return false, err + } + + if pod.DeletionTimestamp.IsZero() { + log.Info("Deleting pod before removing finalizer") + if err := r.Delete(ctx, pod); err != nil && !errors.IsNotFound(err) { + return false, err + } + } + log.Info("Waiting for pod to terminate before removing finalizer") + return false, nil +} + +func (*TaskReconciler) cleargRPC(t *aliecsv1alpha1.Task, log logr.Logger) { + if client, exists := clientsForContainers.Load(t.Name); exists { + log.Info("Cleaning up gRPC connection") + if err := client.(*OccClient).Close(); err != nil { + log.Error(err, "Failed to close gRPC client during deletion") + } + clientsForContainers.Delete(t.Name) + log.Info("gRPC cleaned") + } +} + +func podNameFromTask(name string) string { + return fmt.Sprintf("aliecs-task-pod-%s", name) +} + +func (r *TaskReconciler) podForTask(t *aliecsv1alpha1.Task) *v1.Pod { + lbls := labelsForTask(t) + + pod := &v1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: podNameFromTask(t.Name), + Namespace: t.Namespace, + Labels: lbls, + }, + Spec: t.Spec.Pod, + } + pod.Spec.RestartPolicy = v1.RestartPolicyNever + + _ = controllerutil.SetControllerReference(t, pod, r.Scheme) + return pod +} + +func labelsForTask(t *aliecsv1alpha1.Task) map[string]string { + return map[string]string{ + "task_name": t.Name, + "application": "ControlOperator", + } +} + +func isPodFailed(pod *v1.Pod) (bool, string) { + if pod.Status.Phase == v1.PodFailed { + return true, fmt.Sprintf("Pod failed: %s", pod.Status.Reason) + } + + // Helper to check container statuses + checkContainerStatus := func(statuses []v1.ContainerStatus) (bool, string) { + for _, cs := range statuses { + if cs.State.Waiting != nil { + reason := cs.State.Waiting.Reason + switch reason { + case "CrashLoopBackOff", "ImagePullBackOff", "ErrImagePull", "CreateContainerConfigError", "InvalidImageName", "CreateContainerError": + return true, fmt.Sprintf("Container %s is in %s: %s", cs.Name, reason, cs.State.Waiting.Message) + } + } + if cs.State.Terminated != nil && cs.State.Terminated.ExitCode != 0 { + return true, fmt.Sprintf("Container %s failed with ExitCode %d: %s", cs.Name, cs.State.Terminated.ExitCode, cs.State.Terminated.Message) + } + // If it has restarted multiple times and is currently not ready, it's likely failing + if cs.RestartCount > 3 && !cs.Ready { + return true, fmt.Sprintf("Container %s has crashed %d times", cs.Name, cs.RestartCount) + } + } + return false, "" + } + + // Check main containers + if failed, reason := checkContainerStatus(pod.Status.ContainerStatuses); failed { + return failed, reason + } + + // Check init containers + if failed, reason := checkContainerStatus(pod.Status.InitContainerStatuses); failed { + return failed, reason + } + + return false, "" +} + +// SetupWithManager sets up the controller with the Manager. +func (r *TaskReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&aliecsv1alpha1.Task{}, builder.WithPredicates( + predicate.NewPredicateFuncs(func(obj client.Object) bool { + task, ok := obj.(*aliecsv1alpha1.Task) + if !ok { + return false + } + return task.Spec.NodeName == r.NodeName + }), + )). + Owns(&v1.Pod{}). + WithOptions(controller.Options{MaxConcurrentReconciles: r.MaxConcurrentReconciles}). + Complete(r) +} + +func (r *TaskReconciler) recordCondition(ctx context.Context, t *aliecsv1alpha1.Task, condType string, condStatus metav1.ConditionStatus, reason, message string) error { + patch := client.MergeFrom(t.DeepCopy()) + eventType := v1.EventTypeNormal + if condStatus == metav1.ConditionFalse { + eventType = v1.EventTypeWarning + } + meta.SetStatusCondition(&t.Status.Conditions, metav1.Condition{ + Type: condType, + Status: condStatus, + Reason: reason, + Message: message, + }) + r.Recorder.Event(t, eventType, reason, message) + return r.Status().Patch(ctx, t, patch) +} diff --git a/control-operator/internal/controller/task_controller_helper_test.go b/control-operator/internal/controller/task_controller_helper_test.go new file mode 100644 index 000000000..7d37a8d68 --- /dev/null +++ b/control-operator/internal/controller/task_controller_helper_test.go @@ -0,0 +1,284 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2026 CERN and copyright holders of ALICE O². + * Author: Michal Tichak + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package controller + +import ( + "testing" + + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + + aliecsv1alpha1 "github.com/AliceO2Group/Control/control-operator/api/v1alpha1" +) + +// newTestScheme is used to register owner of pod properly in TestPodForTask +func newTestScheme(t *testing.T) *runtime.Scheme { + t.Helper() + scheme := runtime.NewScheme() + if err := aliecsv1alpha1.AddToScheme(scheme); err != nil { + t.Fatalf("failed to add aliecsv1alpha1 to scheme: %v", err) + } + return scheme +} + +func TestPodForTask(t *testing.T) { + r := &TaskReconciler{Scheme: newTestScheme(t)} + + task := &aliecsv1alpha1.Task{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-task", + Namespace: "my-namespace", + UID: types.UID("test-uid"), + }, + Spec: aliecsv1alpha1.TaskSpec{ + Pod: v1.PodSpec{ + RestartPolicy: v1.RestartPolicyAlways, + Containers: []v1.Container{ + {Name: "main", Image: "example.io/image:latest"}, + }, + }, + }, + } + + pod := r.podForTask(task) + + if pod.Name != "aliecs-task-pod-my-task" { + t.Errorf("pod.Name = %q, want %q", pod.Name, "aliecs-task-pod-my-task") + } + if pod.Namespace != task.Namespace { + t.Errorf("pod.Namespace = %q, want %q", pod.Namespace, task.Namespace) + } + + wantLabels := map[string]string{ + "task_name": "my-task", + "application": "ControlOperator", + } + for k, v := range wantLabels { + if got := pod.Labels[k]; got != v { + t.Errorf("pod.Labels[%q] = %q, want %q", k, got, v) + } + } + + if len(pod.Spec.Containers) != 1 || pod.Spec.Containers[0].Name != "main" { + t.Errorf("pod.Spec.Containers not copied from task.Spec.Pod, got %+v", pod.Spec.Containers) + } + + // podForTask always forces RestartPolicyNever, regardless of what's set on the Task spec. + if pod.Spec.RestartPolicy != v1.RestartPolicyNever { + t.Errorf("pod.Spec.RestartPolicy = %q, want %q", pod.Spec.RestartPolicy, v1.RestartPolicyNever) + } + + ownerRefs := pod.GetOwnerReferences() + if len(ownerRefs) != 1 { + t.Fatalf("expected exactly one owner reference, got %d: %+v", len(ownerRefs), ownerRefs) + } + owner := ownerRefs[0] + if owner.Kind != "Task" { + t.Errorf("owner.Kind = %q, want %q", owner.Kind, "Task") + } + if owner.Name != task.Name { + t.Errorf("owner.Name = %q, want %q", owner.Name, task.Name) + } + if owner.UID != task.UID { + t.Errorf("owner.UID = %q, want %q", owner.UID, task.UID) + } + if owner.Controller == nil || !*owner.Controller { + t.Errorf("owner.Controller = %v, want true", owner.Controller) + } +} + +func TestPodNameFromTask(t *testing.T) { + tests := []struct { + name string + taskName string + want string + }{ + {name: "simple task name", taskName: "my-task", want: "aliecs-task-pod-my-task"}, + {name: "empty task name", taskName: "", want: "aliecs-task-pod-"}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := podNameFromTask(tt.taskName); got != tt.want { + t.Errorf("podNameFromTask(%q) = %q, want %q", tt.taskName, got, tt.want) + } + }) + } +} + +func TestLabelsForTask(t *testing.T) { + task := &aliecsv1alpha1.Task{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-task", + }, + } + + want := map[string]string{ + "task_name": "my-task", + "application": "ControlOperator", + } + + got := labelsForTask(task) + + if len(got) != len(want) { + t.Fatalf("labelsForTask() = %v, want %v", got, want) + } + for k, v := range want { + if got[k] != v { + t.Errorf("labelsForTask()[%q] = %q, want %q", k, got[k], v) + } + } +} + +func TestIsPodFailed(t *testing.T) { + tests := []struct { + name string + pod *v1.Pod + wantFailed bool + }{ + { + name: "running pod with ready container is not failed", + pod: &v1.Pod{ + Status: v1.PodStatus{ + Phase: v1.PodRunning, + ContainerStatuses: []v1.ContainerStatus{{Name: "main", Ready: true}}, + }, + }, + wantFailed: false, + }, + { + name: "pod phase Failed is failed", + pod: &v1.Pod{ + Status: v1.PodStatus{ + Phase: v1.PodFailed, + Reason: "Evicted", + }, + }, + wantFailed: true, + }, + { + name: "container waiting CrashLoopBackOff is failed", + pod: &v1.Pod{ + Status: v1.PodStatus{ + Phase: v1.PodRunning, + ContainerStatuses: []v1.ContainerStatus{{ + Name: "main", + State: v1.ContainerState{Waiting: &v1.ContainerStateWaiting{Reason: "CrashLoopBackOff"}}, + }}, + }, + }, + wantFailed: true, + }, + { + name: "container waiting for a benign reason is not failed", + pod: &v1.Pod{ + Status: v1.PodStatus{ + Phase: v1.PodRunning, + ContainerStatuses: []v1.ContainerStatus{{ + Name: "main", + State: v1.ContainerState{Waiting: &v1.ContainerStateWaiting{Reason: "ContainerCreating"}}, + }}, + }, + }, + wantFailed: false, + }, + { + name: "container terminated with non-zero exit code is failed", + pod: &v1.Pod{ + Status: v1.PodStatus{ + Phase: v1.PodRunning, + ContainerStatuses: []v1.ContainerStatus{{ + Name: "main", + State: v1.ContainerState{Terminated: &v1.ContainerStateTerminated{ExitCode: 1}}, + }}, + }, + }, + wantFailed: true, + }, + { + name: "container terminated with exit code 0 is not failed", + pod: &v1.Pod{ + Status: v1.PodStatus{ + Phase: v1.PodRunning, + ContainerStatuses: []v1.ContainerStatus{{ + Name: "main", + State: v1.ContainerState{Terminated: &v1.ContainerStateTerminated{ExitCode: 0}}, + }}, + }, + }, + wantFailed: false, + }, + { + name: "container with more than 3 restarts and not ready is failed", + pod: &v1.Pod{ + Status: v1.PodStatus{ + Phase: v1.PodRunning, + ContainerStatuses: []v1.ContainerStatus{{Name: "main", RestartCount: 4, Ready: false}}, + }, + }, + wantFailed: true, + }, + { + name: "container with more than 3 restarts but ready is not failed", + pod: &v1.Pod{ + Status: v1.PodStatus{ + Phase: v1.PodRunning, + ContainerStatuses: []v1.ContainerStatus{{Name: "main", RestartCount: 4, Ready: true}}, + }, + }, + wantFailed: false, + }, + { + name: "init container failure is failed", + pod: &v1.Pod{ + Status: v1.PodStatus{ + Phase: v1.PodRunning, + InitContainerStatuses: []v1.ContainerStatus{{ + Name: "init", + State: v1.ContainerState{Waiting: &v1.ContainerStateWaiting{Reason: "InvalidImageName"}}, + }}, + }, + }, + wantFailed: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + gotFailed, gotReason := isPodFailed(tt.pod) + if gotFailed != tt.wantFailed { + t.Errorf("isPodFailed() failed = %v, want %v (reason: %q)", gotFailed, tt.wantFailed, gotReason) + } + if gotFailed && gotReason == "" { + t.Errorf("isPodFailed() returned failed=true with empty reason") + } + if !gotFailed && gotReason != "" { + t.Errorf("isPodFailed() returned failed=false with non-empty reason %q", gotReason) + } + }) + } +} diff --git a/control-operator/internal/controller/task_controller_test.go b/control-operator/internal/controller/task_controller_test.go new file mode 100644 index 000000000..16be626a3 --- /dev/null +++ b/control-operator/internal/controller/task_controller_test.go @@ -0,0 +1,244 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2026 CERN and copyright holders of ALICE O². + * Author: Michal Tichak + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package controller + +import ( + "context" + "time" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/tools/record" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + + aliecsv1alpha1 "github.com/AliceO2Group/Control/control-operator/api/v1alpha1" +) + +var _ = Describe("Task Controller", func() { + // Reconcile takes the Task one step further on every pass and then returns to wait + // for the next event: first the finalizer, then the Pod, then the gRPC connection, + // then the state of the container, and finally the transition. These specs run in + // order and each one drives the pass responsible for the next step. + Describe("When reconciling a Task step by step", Ordered, func() { + const ( + resourceName = "test-resource" + namespace = "default" + containerName = "placeholder-name" + containerImg = "placeholder-image:latest" + // The OCC stub listens on loopback, so the Pod IP the controller dials has + // to point back at the test process. + podIP = "127.0.0.1" + ) + + var ( + ctx context.Context + taskName types.NamespacedName + podName types.NamespacedName + occServer *occServerStub + reconciler *TaskReconciler + ) + + reconcileOnce := func() reconcile.Result { + GinkgoHelper() + res, err := reconciler.Reconcile(ctx, reconcile.Request{NamespacedName: taskName}) + Expect(err).NotTo(HaveOccurred()) + return res + } + + getTask := func() *aliecsv1alpha1.Task { + GinkgoHelper() + task := &aliecsv1alpha1.Task{} + Expect(k8sClient.Get(ctx, taskName, task)).To(Succeed()) + return task + } + + BeforeAll(func() { + ctx = context.Background() + taskName = types.NamespacedName{Name: resourceName, Namespace: namespace} + podName = types.NamespacedName{Name: podNameFromTask(resourceName), Namespace: namespace} + + By("starting the OCC server stub that stands in for the task container") + stub, port, stopOccServer, err := startOccServerStub() + Expect(err).NotTo(HaveOccurred()) + DeferCleanup(stopOccServer) + + occServer = stub + occServer.setState("standby") + + reconciler = &TaskReconciler{ + Client: k8sClient, + Scheme: k8sClient.Scheme(), + // A zero-value FakeRecorder discards events; NewFakeRecorder would block + // once its buffer filled up. + Recorder: &record.FakeRecorder{}, + } + + By("creating the custom resource for the Kind Task") + task := &aliecsv1alpha1.Task{ + ObjectMeta: metav1.ObjectMeta{ + Name: resourceName, + Namespace: namespace, + }, + Spec: aliecsv1alpha1.TaskSpec{ + Control: aliecsv1alpha1.TaskSpecControl{Mode: "direct", Port: port}, + State: "standby", + Pod: v1.PodSpec{ + Containers: []v1.Container{{Name: containerName, Image: containerImg}}, + }, + }, + } + Expect(k8sClient.Create(ctx, task)).To(Succeed()) + + // The last spec deletes the Task and its Pod through the controller, so this + // only clears what an earlier failure left behind. + DeferCleanup(func() { + if c, loaded := clientsForContainers.LoadAndDelete(resourceName); loaded { + Expect(c.(*OccClient).Close()).To(Succeed()) + } + + pod := &v1.Pod{} + if err := k8sClient.Get(ctx, podName, pod); err == nil { + Expect(client.IgnoreNotFound(k8sClient.Delete(ctx, pod, client.GracePeriodSeconds(0)))).To(Succeed()) + } + + leftover := &aliecsv1alpha1.Task{} + if err := k8sClient.Get(ctx, taskName, leftover); err == nil { + controllerutil.RemoveFinalizer(leftover, taskFinalizer) + Expect(k8sClient.Update(ctx, leftover)).To(Succeed()) + Expect(client.IgnoreNotFound(k8sClient.Delete(ctx, leftover))).To(Succeed()) + } + }) + }) + + It("sets the finalizer before touching anything else", func() { + reconcileOnce() + + Expect(controllerutil.ContainsFinalizer(getTask(), taskFinalizer)).To(BeTrue()) + Expect(k8sClient.Get(ctx, podName, &v1.Pod{})).To(Satisfy(errors.IsNotFound), + "the Pod should wait until the finalizer is persisted") + }) + + It("creates the Pod for the Task", func() { + reconcileOnce() + + pod := &v1.Pod{} + Expect(k8sClient.Get(ctx, podName, pod)).To(Succeed()) + + Expect(pod.Spec.Containers).To(HaveLen(1)) + Expect(pod.Spec.Containers[0].Image).To(Equal(containerImg)) + Expect(pod.Spec.RestartPolicy).To(Equal(v1.RestartPolicyNever)) + Expect(pod.Labels).To(HaveKeyWithValue("task_name", resourceName)) + + Expect(pod.OwnerReferences).To(HaveLen(1)) + Expect(pod.OwnerReferences[0].Name).To(Equal(resourceName)) + Expect(pod.OwnerReferences[0].Controller).To(HaveValue(BeTrue())) + }) + + It("waits for the Pod to report an IP before connecting", func() { + reconcileOnce() + + _, connected := clientsForContainers.Load(resourceName) + Expect(connected).To(BeFalse()) + Expect(meta.FindStatusCondition(getTask().Status.Conditions, aliecsv1alpha1.ConditionPodReady)).To(BeNil()) + }) + + It("connects to the container once the Pod is running", func() { + By("reporting the Pod status that a kubelet would report") + // envtest runs no kubelet, so nothing else moves the Pod to Running. + pod := &v1.Pod{} + Expect(k8sClient.Get(ctx, podName, pod)).To(Succeed()) + pod.Status.Phase = v1.PodRunning + pod.Status.PodIP = podIP + Expect(k8sClient.Status().Update(ctx, pod)).To(Succeed()) + + reconcileOnce() + + occClient, connected := clientsForContainers.Load(resourceName) + Expect(connected).To(BeTrue()) + + task := getTask() + Expect(meta.IsStatusConditionTrue(task.Status.Conditions, aliecsv1alpha1.ConditionPodReady)).To(BeTrue()) + Expect(meta.IsStatusConditionTrue(task.Status.Conditions, aliecsv1alpha1.ConditionGRPCConnected)).To(BeTrue()) + + // This pass asked to be requeued until the connection is Ready, which is what + // the next step needs. + connectCtx, cancel := context.WithTimeout(ctx, 5*time.Second) + defer cancel() + Expect(occClient.(*OccClient).WaitUntilConnected(connectCtx)).To(Succeed()) + }) + + It("reads the state from the container", func() { + reconcileOnce() + + task := getTask() + Expect(task.Status.State).To(Equal(occServer.getState())) + Expect(task.Status.Error).To(BeEmpty()) + Expect(meta.IsStatusConditionTrue(task.Status.Conditions, aliecsv1alpha1.ConditionStateAccessible)).To(BeTrue()) + }) + + It("transitions the container to a newly requested state", func() { + By("asking the Task for a state the container is not in") + task := getTask() + task.Spec.State = "configured" + Expect(k8sClient.Update(ctx, task)).To(Succeed()) + + reconcileOnce() + + task = getTask() + Expect(task.Status.State).To(Equal("configured")) + Expect(task.Status.Error).To(BeEmpty()) + Expect(meta.IsStatusConditionTrue(task.Status.Conditions, aliecsv1alpha1.ConditionStateTransitionResult)).To(BeTrue()) + + By("leaving the container in the requested state") + Expect(occServer.getState()).To(Equal("configured")) + }) + + It("clears the connection and the Pod when the Task is deleted", func() { + Expect(k8sClient.Delete(ctx, getTask())).To(Succeed()) + + reconcileOnce() + + _, connected := clientsForContainers.Load(resourceName) + Expect(connected).To(BeFalse()) + + // Nothing schedules the Pod in envtest, so the API server drops it right away + // instead of waiting for a kubelet to confirm termination. + Expect(k8sClient.Get(ctx, podName, &v1.Pod{})).To(Satisfy(errors.IsNotFound)) + Expect(controllerutil.ContainsFinalizer(getTask(), taskFinalizer)).To(BeTrue(), + "this pass only deletes the Pod, the finalizer goes on the next one") + + reconcileOnce() + + Expect(k8sClient.Get(ctx, taskName, &aliecsv1alpha1.Task{})).To(Satisfy(errors.IsNotFound)) + }) + }) +}) diff --git a/control-operator/k3s-observability/manifests/fluent-bit-audit.yml b/control-operator/k3s-observability/manifests/fluent-bit-audit.yml new file mode 100644 index 000000000..6e438dbe5 --- /dev/null +++ b/control-operator/k3s-observability/manifests/fluent-bit-audit.yml @@ -0,0 +1,98 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: fluent-bit-audit-config + namespace: kube-system +data: + fluent-bit-audit.yml: | + service: + flush: 5 + log_level: info + + parsers: + - name: audit-json + format: json + time_key: requestReceivedTimestamp + time_format: "%Y-%m-%dT%H:%M:%S.%LZ" + + pipeline: + inputs: + - name: tail + path: /var/log/k3s-audit.log + tag: k8s.audit + parser: audit-json + db: /db/fluent-bit-audit.db + buffer_max_size: 2MB + skip_long_lines: off + + filters: + - name: nest + match: k8s.audit + operation: lift + nested_under: objectRef + add_prefix: objectRef_ + + - name: nest + match: k8s.audit + operation: lift + nested_under: user + add_prefix: user_ + + outputs: + - name: forward + match: k8s.audit + host: ${OPENSEARCH_HOST} + port: ${OPENSEARCH_PORT} +--- +apiVersion: apps/v1 +kind: DaemonSet +metadata: + name: fluent-bit-audit + namespace: kube-system +spec: + selector: + matchLabels: + app: fluent-bit-audit + template: + metadata: + labels: + app: fluent-bit-audit + annotations: + reloader.stakater.com/auto: "true" + spec: + nodeSelector: + node-role.kubernetes.io/control-plane: "true" + tolerations: + - key: node-role.kubernetes.io/control-plane + operator: Exists + effect: NoSchedule + containers: + - name: fluent-bit + image: fluent/fluent-bit:5.0 + args: + - -c + - /etc/fluent-bit-audit/fluent-bit-audit.yml + envFrom: + - configMapRef: + name: opensearch-config + volumeMounts: + - name: config + mountPath: /etc/fluent-bit-audit/fluent-bit-audit.yml + subPath: fluent-bit-audit.yml + - name: auditlog + mountPath: /var/log/k3s-audit.log + readOnly: true + - name: db + mountPath: /db + volumes: + - name: config + configMap: + name: fluent-bit-audit-config + - name: auditlog + hostPath: + path: /var/log/k3s-audit.log + type: File + - name: db + hostPath: + path: /var/log/fluent-bit-audit + type: DirectoryOrCreate diff --git a/control-operator/k3s-observability/manifests/fluent-bit-events.yml b/control-operator/k3s-observability/manifests/fluent-bit-events.yml new file mode 100644 index 000000000..cdff9507c --- /dev/null +++ b/control-operator/k3s-observability/manifests/fluent-bit-events.yml @@ -0,0 +1,94 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: fluent-bit-events-config + namespace: kube-system +data: + fluent-bit.yml: | + service: + flush: 5 + log_level: info + + pipeline: + inputs: + - name: kubernetes_events + tag: kube.events + kube_url: https://kubernetes.default.svc:443 + kube_ca_file: /var/lib/rancher/k3s/server/tls/server-ca.crt + kube_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token + + outputs: + - name: forward + match: kube.events + host: ${OPENSEARCH_HOST} + port: ${OPENSEARCH_PORT} +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: fluent-bit-events + namespace: kube-system +spec: + replicas: 1 + selector: + matchLabels: + app: fluent-bit-events + template: + metadata: + labels: + app: fluent-bit-events + annotations: + reloader.stakater.com/auto: "true" + spec: + serviceAccountName: fluent-bit-events + containers: + - name: fluent-bit + image: fluent/fluent-bit:5.0 + args: + - -c + - /etc/fluent-bit/fluent-bit.yml + envFrom: + - configMapRef: + name: opensearch-config + volumeMounts: + - name: config + mountPath: /etc/fluent-bit/fluent-bit.yml + subPath: fluent-bit.yml + - name: k3s-tls + mountPath: /var/lib/rancher/k3s/server/tls + readOnly: true + volumes: + - name: config + configMap: + name: fluent-bit-events-config + - name: k3s-tls + hostPath: + path: /var/lib/rancher/k3s/server/tls +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: fluent-bit-events + namespace: kube-system +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: fluent-bit-events +rules: +- apiGroups: [""] + resources: [events] + verbs: [get, list, watch] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: fluent-bit-events +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: fluent-bit-events +subjects: +- kind: ServiceAccount + name: fluent-bit-events + namespace: kube-system diff --git a/control-operator/k3s-observability/manifests/fluent-bit-logs.yml b/control-operator/k3s-observability/manifests/fluent-bit-logs.yml new file mode 100644 index 000000000..cd852ea71 --- /dev/null +++ b/control-operator/k3s-observability/manifests/fluent-bit-logs.yml @@ -0,0 +1,123 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: fluent-bit-logs-config + namespace: kube-system +data: + fluent-bit-logs.yml: | + service: + flush: 5 + log_level: info + + pipeline: + inputs: + - name: tail + path: /var/log/containers/*.log + multiline.parser: cri + tag: kube.* + mem_buf_limit: 50MB + skip_long_lines: on + + + filters: + - name: kubernetes + match: kube.* + kube_url: https://kubernetes.default.svc:443 + kube_ca_file: /var/lib/rancher/k3s/server/tls/server-ca.crt + kube_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token + labels: on + annotations: off + merge_log: on + + - name: nest + match: kube.* + operation: lift + nested_under: kubernetes + add_prefix: kubernetes. + + - name: modify + match: kube.* + rename: msg log + + outputs: + - name: forward + match: kube.* + host: ${OPENSEARCH_HOST} + port: ${OPENSEARCH_PORT} +--- +apiVersion: apps/v1 +kind: DaemonSet +metadata: + name: fluent-bit-logs + namespace: kube-system +spec: + selector: + matchLabels: + app: fluent-bit-logs + template: + metadata: + labels: + app: fluent-bit-logs + annotations: + reloader.stakater.com/auto: "true" + spec: + serviceAccountName: fluent-bit-logs + tolerations: + - operator: Exists + containers: + - name: fluent-bit + image: fluent/fluent-bit:5.0 + args: + - -c + - /etc/fluent-bit-logs/fluent-bit-logs.yml + envFrom: + - configMapRef: + name: opensearch-config + volumeMounts: + - name: config + mountPath: /etc/fluent-bit-logs/fluent-bit-logs.yml + subPath: fluent-bit-logs.yml + - name: varlog + mountPath: /var/log + readOnly: true + - name: k3s-tls + mountPath: /var/lib/rancher/k3s/server/tls + readOnly: true + volumes: + - name: config + configMap: + name: fluent-bit-logs-config + - name: varlog + hostPath: + path: /var/log + - name: k3s-tls + hostPath: + path: /var/lib/rancher/k3s/server/tls +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: fluent-bit-logs + namespace: kube-system +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: fluent-bit-logs +rules: +- apiGroups: [""] + resources: [pods, namespaces] + verbs: [get, list, watch] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: fluent-bit-logs +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: fluent-bit-logs +subjects: +- kind: ServiceAccount + name: fluent-bit-logs + namespace: kube-system diff --git a/control-operator/k3s-observability/manifests/opensearch-config.yml b/control-operator/k3s-observability/manifests/opensearch-config.yml new file mode 100644 index 000000000..883499fc7 --- /dev/null +++ b/control-operator/k3s-observability/manifests/opensearch-config.yml @@ -0,0 +1,8 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: opensearch-config + namespace: kube-system +data: + OPENSEARCH_HOST: mtichak-opensearch.cern.ch + OPENSEARCH_PORT: "24224" diff --git a/control-operator/k3s-observability/other/audit-policy.yaml b/control-operator/k3s-observability/other/audit-policy.yaml new file mode 100644 index 000000000..b4e8cb461 --- /dev/null +++ b/control-operator/k3s-observability/other/audit-policy.yaml @@ -0,0 +1,27 @@ +apiVersion: audit.k8s.io/v1 +kind: Policy +rules: + # Full request+response for ALIECS custom resources + - level: RequestResponse + omitManagedFields: true + verbs: [create, update, patch, delete] + resources: + - group: aliecs.alice.cern + resources: [environments, tasks, tasktemplates] + + # Pod lifecycle — metadata only (no need for full spec, we have container logs) + - level: Metadata + verbs: [create, delete] + resources: + - group: "" + resources: [pods] + + # Drop noisy non-events + - level: None + resources: + - group: coordination.k8s.io + resources: [leases] + - level: None + users: [system:node:*] + - level: None + nonResourceURLs: [/healthz*, /readyz*, /livez*, /metrics] diff --git a/control-operator/pkg/client/client.go b/control-operator/pkg/client/client.go new file mode 100644 index 000000000..d81b9d77b --- /dev/null +++ b/control-operator/pkg/client/client.go @@ -0,0 +1,163 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2026 CERN and copyright holders of ALICE O². + * Author: Michal Tichak + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package client + +import ( + "context" + "fmt" + "maps" + + "github.com/AliceO2Group/Control/control-operator/api/v1alpha1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/watch" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/clientcmd" + crClient "sigs.k8s.io/controller-runtime/pkg/client" +) + +type Client struct { + client crClient.WithWatch + namespace string +} + +func New(kubeconfigPath, namespace string) (*Client, error) { + config, err := buildConfig(kubeconfigPath) + if err != nil { + return nil, fmt.Errorf("building kubeconfig: %w", err) + } + return NewFromConfig(config, namespace) +} + +func NewFromConfig(config *rest.Config, namespace string) (*Client, error) { + scheme := runtime.NewScheme() + if err := v1alpha1.AddToScheme(scheme); err != nil { + return nil, fmt.Errorf("registering v1alpha1 scheme: %w", err) + } + + c, err := crClient.NewWithWatch(config, crClient.Options{Scheme: scheme}) + if err != nil { + return nil, fmt.Errorf("creating kubernetes client: %w", err) + } + + return &Client{client: c, namespace: namespace}, nil +} + +func buildConfig(kubeconfigPath string) (*rest.Config, error) { + if kubeconfigPath != "" { + return clientcmd.BuildConfigFromFlags("", kubeconfigPath) + } + config, err := rest.InClusterConfig() + if err == nil { + return config, nil + } + + return nil, fmt.Errorf("ECS isn't running in a container and you didn't pass any kubernetes config: %v", err) +} + +func (c *Client) CreateTask(ctx context.Context, task *v1alpha1.Task) error { + task.Namespace = c.namespace + return c.client.Create(ctx, task) +} + +func (c *Client) GetTask(ctx context.Context, name string) (*v1alpha1.Task, error) { + task := &v1alpha1.Task{} + err := c.client.Get(ctx, types.NamespacedName{Name: name, Namespace: c.namespace}, task) + return task, err +} + +func (c *Client) UpdateTask(ctx context.Context, task *v1alpha1.Task) error { + task.Namespace = c.namespace + return c.client.Update(ctx, task) +} + +// SetTaskArguments replaces the Task's spec.arguments with the given transition arguments +func (c *Client) SetTaskArguments(ctx context.Context, task *v1alpha1.Task, args map[string]string) error { + patch := crClient.MergeFrom(task.DeepCopy()) + task.Spec.Arguments = maps.Clone(args) + return c.client.Patch(ctx, task, patch) +} + +func (c *Client) DeleteTask(ctx context.Context, name string) error { + return c.client.Delete(ctx, &v1alpha1.Task{ + ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: c.namespace}, + }) +} + +// WatchTasks returns a watcher for all Task resources in the namespace. +// Each event on ResultChan() carries a *v1alpha1.Task as event.Object. +func (c *Client) WatchTasks(ctx context.Context) (watch.Interface, error) { + return c.client.Watch(ctx, &v1alpha1.TaskList{}, crClient.InNamespace(c.namespace)) +} + +// ListTasksByLabel returns all Task resources matching the given label selector. +// Returns an error if no tasks are found. +func (c *Client) ListTasksByLabel(ctx context.Context, labels map[string]string) ([]v1alpha1.Task, error) { + taskList := &v1alpha1.TaskList{} + if err := c.client.List(ctx, taskList, crClient.InNamespace(c.namespace), crClient.MatchingLabels(labels)); err != nil { + return nil, err + } + if len(taskList.Items) == 0 { + return nil, fmt.Errorf("no Task CRDs found matching labels %v", labels) + } + return taskList.Items, nil +} + +func (c *Client) CreateEnvironment(ctx context.Context, env *v1alpha1.Environment) error { + env.Namespace = c.namespace + return c.client.Create(ctx, env) +} + +func (c *Client) GetEnvironment(ctx context.Context, name string) (*v1alpha1.Environment, error) { + env := &v1alpha1.Environment{} + err := c.client.Get(ctx, types.NamespacedName{Name: name, Namespace: c.namespace}, env) + return env, err +} + +func (c *Client) UpdateEnvironment(ctx context.Context, env *v1alpha1.Environment) error { + env.Namespace = c.namespace + return c.client.Update(ctx, env) +} + +func (c *Client) DeleteEnvironment(ctx context.Context, name string) error { + return c.client.Delete(ctx, &v1alpha1.Environment{ + ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: c.namespace}, + }) +} + +// WatchEnvironments returns a watcher for all Environment resources in the namespace. +// Each event on ResultChan() carries a *v1alpha1.Environment as event.Object. +func (c *Client) WatchEnvironments(ctx context.Context) (watch.Interface, error) { + return c.client.Watch(ctx, &v1alpha1.EnvironmentList{}, crClient.InNamespace(c.namespace)) +} + +// WatchEnvironmentsFromVersion watches Environment resources starting from the given resourceVersion, +// ensuring no events that occurred after that version are missed. +func (c *Client) WatchEnvironmentsFromVersion(ctx context.Context, resourceVersion string) (watch.Interface, error) { + return c.client.Watch(ctx, &v1alpha1.EnvironmentList{}, + crClient.InNamespace(c.namespace), + &crClient.ListOptions{Raw: &metav1.ListOptions{ResourceVersion: resourceVersion}}) +} diff --git a/control-operator/pkg/client/client_test.go b/control-operator/pkg/client/client_test.go new file mode 100644 index 000000000..7e1b92967 --- /dev/null +++ b/control-operator/pkg/client/client_test.go @@ -0,0 +1,290 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2026 CERN and copyright holders of ALICE O². + * Author: Michal Tichak + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package client_test + +import ( + "context" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + k8swatch "k8s.io/apimachinery/pkg/watch" + + aliecsv1alpha1 "github.com/AliceO2Group/Control/control-operator/api/v1alpha1" + "github.com/AliceO2Group/Control/control-operator/pkg/client" +) + +var _ = Describe("Client", func() { + var ( + ctx context.Context + c *client.Client + ) + + BeforeEach(func() { + ctx = context.Background() + var err error + c, err = client.NewFromConfig(cfg, "default") + Expect(err).NotTo(HaveOccurred()) + }) + + Describe("Task Create, Read, Update, Delete", func() { + var task *aliecsv1alpha1.Task + + BeforeEach(func() { + task = &aliecsv1alpha1.Task{ + ObjectMeta: metav1.ObjectMeta{Name: "test-task"}, + Spec: aliecsv1alpha1.TaskSpec{State: "standby", Pod: v1.PodSpec{Containers: []v1.Container{}}}, + } + Expect(c.CreateTask(ctx, task)).To(Succeed()) + }) + + AfterEach(func() { + _ = c.DeleteTask(ctx, task.Name) + }) + + It("gets a created task", func() { + got, err := c.GetTask(ctx, "test-task") + Expect(err).NotTo(HaveOccurred()) + Expect(got.Name).To(Equal("test-task")) + Expect(got.Spec.State).To(Equal("standby")) + }) + + It("updates a task", func() { + got, err := c.GetTask(ctx, "test-task") + Expect(err).NotTo(HaveOccurred()) + + got.Spec.State = "running" + Expect(c.UpdateTask(ctx, got)).To(Succeed()) + + updated, err := c.GetTask(ctx, "test-task") + Expect(err).NotTo(HaveOccurred()) + Expect(updated.Spec.State).To(Equal("running")) + }) + + It("deletes a task", func() { + Expect(c.DeleteTask(ctx, "test-task")).To(Succeed()) + + _, err := c.GetTask(ctx, "test-task") + Expect(err).To(HaveOccurred()) + }) + }) + + Describe("Task Watch", func() { + It("receives events for task changes", func() { + watcher, err := c.WatchTasks(ctx) + Expect(err).NotTo(HaveOccurred()) + defer watcher.Stop() + + task := &aliecsv1alpha1.Task{ + ObjectMeta: metav1.ObjectMeta{Name: "watch-task"}, + Spec: aliecsv1alpha1.TaskSpec{State: "standby", Pod: v1.PodSpec{Containers: []v1.Container{}}}, + } + Expect(c.CreateTask(ctx, task)).To(Succeed()) + defer c.DeleteTask(ctx, task.Name) + + Eventually(watcher.ResultChan()).Should(Receive(Satisfy(func(e k8swatch.Event) bool { + t, ok := e.Object.(*aliecsv1alpha1.Task) + return ok && t.Name == "watch-task" && e.Type == k8swatch.Added + }))) + }) + }) + + Describe("Environment Create, Read, Update, Delete", func() { + var env *aliecsv1alpha1.Environment + + BeforeEach(func() { + env = &aliecsv1alpha1.Environment{ + ObjectMeta: metav1.ObjectMeta{Name: "test-env"}, + Spec: aliecsv1alpha1.EnvironmentSpec{ + State: "standby", + Tasks: map[string][]aliecsv1alpha1.TaskDefinition{}, + }, + TaskTemplates: aliecsv1alpha1.TemplateSpecification{ + Tasks: map[string][]aliecsv1alpha1.TaskReference{}, + }, + } + Expect(c.CreateEnvironment(ctx, env)).To(Succeed()) + }) + + AfterEach(func() { + _ = c.DeleteEnvironment(ctx, env.Name) + }) + + It("gets a created environment", func() { + got, err := c.GetEnvironment(ctx, "test-env") + Expect(err).NotTo(HaveOccurred()) + Expect(got.Name).To(Equal("test-env")) + Expect(got.Spec.State).To(Equal("standby")) + }) + + It("updates an environment", func() { + got, err := c.GetEnvironment(ctx, "test-env") + Expect(err).NotTo(HaveOccurred()) + + got.Spec.State = "running" + Expect(c.UpdateEnvironment(ctx, got)).To(Succeed()) + + updated, err := c.GetEnvironment(ctx, "test-env") + Expect(err).NotTo(HaveOccurred()) + Expect(updated.Spec.State).To(Equal("running")) + }) + + It("deletes an environment", func() { + Expect(c.DeleteEnvironment(ctx, "test-env")).To(Succeed()) + + _, err := c.GetEnvironment(ctx, "test-env") + Expect(err).To(HaveOccurred()) + }) + }) + + Describe("WatchEnvironmentsFromVersion", func() { + It("does not miss events that occurred between Get and Watch", func() { + env := &aliecsv1alpha1.Environment{ + ObjectMeta: metav1.ObjectMeta{Name: "versioned-watch-env"}, + Spec: aliecsv1alpha1.EnvironmentSpec{ + State: "standby", + Tasks: map[string][]aliecsv1alpha1.TaskDefinition{}, + }, + TaskTemplates: aliecsv1alpha1.TemplateSpecification{ + Tasks: map[string][]aliecsv1alpha1.TaskReference{}, + }, + } + Expect(c.CreateEnvironment(ctx, env)).To(Succeed()) + defer c.DeleteEnvironment(ctx, env.Name) + + got, err := c.GetEnvironment(ctx, env.Name) + Expect(err).NotTo(HaveOccurred()) + resourceVersionBeforeUpdate := got.ResourceVersion + + got.Spec.State = "configured" + Expect(c.UpdateEnvironment(ctx, got)).To(Succeed()) + + watcher, err := c.WatchEnvironmentsFromVersion(ctx, resourceVersionBeforeUpdate) + Expect(err).NotTo(HaveOccurred()) + defer watcher.Stop() + + Eventually(watcher.ResultChan()).Should(Receive(Satisfy(func(e k8swatch.Event) bool { + ev, ok := e.Object.(*aliecsv1alpha1.Environment) + return ok && ev.Name == env.Name && ev.Spec.State == "configured" + }))) + }) + }) + + Describe("Environment Watch", func() { + It("receives events for environment changes", func() { + watcher, err := c.WatchEnvironments(ctx) + Expect(err).NotTo(HaveOccurred()) + defer watcher.Stop() + + env := &aliecsv1alpha1.Environment{ + ObjectMeta: metav1.ObjectMeta{Name: "watch-env"}, + Spec: aliecsv1alpha1.EnvironmentSpec{ + State: "standby", + Tasks: map[string][]aliecsv1alpha1.TaskDefinition{}, + }, + TaskTemplates: aliecsv1alpha1.TemplateSpecification{ + Tasks: map[string][]aliecsv1alpha1.TaskReference{}, + }, + } + Expect(c.CreateEnvironment(ctx, env)).To(Succeed()) + defer c.DeleteEnvironment(ctx, env.Name) + + Eventually(watcher.ResultChan()).Should(Receive(Satisfy(func(e k8swatch.Event) bool { + ev, ok := e.Object.(*aliecsv1alpha1.Environment) + return ok && ev.Name == "watch-env" && e.Type == k8swatch.Added + }))) + }) + }) + + Describe("ListTasksByLabel", func() { + newTask := func(name string, labels map[string]string) *aliecsv1alpha1.Task { + return &aliecsv1alpha1.Task{ + ObjectMeta: metav1.ObjectMeta{Name: name, Labels: labels}, + Spec: aliecsv1alpha1.TaskSpec{State: "standby", Pod: v1.PodSpec{Containers: []v1.Container{}}}, + } + } + + It("returns an error when no tasks match", func() { + _, err := c.ListTasksByLabel(ctx, map[string]string{"env": "nonexistent"}) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("no Task CRDs found")) + }) + + It("returns tasks matching a single label", func() { + task := newTask("label-task-1", map[string]string{"env": "test"}) + Expect(c.CreateTask(ctx, task)).To(Succeed()) + defer c.DeleteTask(ctx, task.Name) + + tasks, err := c.ListTasksByLabel(ctx, map[string]string{"env": "test"}) + Expect(err).NotTo(HaveOccurred()) + Expect(tasks).To(HaveLen(1)) + Expect(tasks[0].Name).To(Equal("label-task-1")) + }) + + It("returns multiple tasks matching a label", func() { + task1 := newTask("label-task-2a", map[string]string{"env": "multi"}) + task2 := newTask("label-task-2b", map[string]string{"env": "multi"}) + Expect(c.CreateTask(ctx, task1)).To(Succeed()) + defer c.DeleteTask(ctx, task1.Name) + Expect(c.CreateTask(ctx, task2)).To(Succeed()) + defer c.DeleteTask(ctx, task2.Name) + + tasks, err := c.ListTasksByLabel(ctx, map[string]string{"env": "multi"}) + Expect(err).NotTo(HaveOccurred()) + Expect(tasks).To(HaveLen(2)) + names := []string{tasks[0].Name, tasks[1].Name} + Expect(names).To(ConsistOf("label-task-2a", "label-task-2b")) + }) + + It("returns only tasks matching all labels", func() { + taskBoth := newTask("label-task-3a", map[string]string{"env": "filter", "role": "worker"}) + taskOne := newTask("label-task-3b", map[string]string{"env": "filter"}) + Expect(c.CreateTask(ctx, taskBoth)).To(Succeed()) + defer c.DeleteTask(ctx, taskBoth.Name) + Expect(c.CreateTask(ctx, taskOne)).To(Succeed()) + defer c.DeleteTask(ctx, taskOne.Name) + + tasks, err := c.ListTasksByLabel(ctx, map[string]string{"env": "filter", "role": "worker"}) + Expect(err).NotTo(HaveOccurred()) + Expect(tasks).To(HaveLen(1)) + Expect(tasks[0].Name).To(Equal("label-task-3a")) + }) + + It("does not return tasks with a different label value", func() { + taskA := newTask("label-task-4a", map[string]string{"env": "staging"}) + taskB := newTask("label-task-4b", map[string]string{"env": "production"}) + Expect(c.CreateTask(ctx, taskA)).To(Succeed()) + defer c.DeleteTask(ctx, taskA.Name) + Expect(c.CreateTask(ctx, taskB)).To(Succeed()) + defer c.DeleteTask(ctx, taskB.Name) + + tasks, err := c.ListTasksByLabel(ctx, map[string]string{"env": "staging"}) + Expect(err).NotTo(HaveOccurred()) + Expect(tasks).To(HaveLen(1)) + Expect(tasks[0].Name).To(Equal("label-task-4a")) + }) + }) +}) diff --git a/control-operator/pkg/client/suite_test.go b/control-operator/pkg/client/suite_test.go new file mode 100644 index 000000000..cd571f8bf --- /dev/null +++ b/control-operator/pkg/client/suite_test.go @@ -0,0 +1,68 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2026 CERN and copyright holders of ALICE O². + * Author: Michal Tichak + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package client_test + +import ( + "path/filepath" + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + "k8s.io/client-go/rest" + "sigs.k8s.io/controller-runtime/pkg/envtest" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/log/zap" +) + +var ( + cfg *rest.Config + testEnv *envtest.Environment +) + +func TestClient(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Client Suite") +} + +var _ = BeforeSuite(func() { + logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true))) + + By("bootstrapping test environment") + testEnv = &envtest.Environment{ + CRDDirectoryPaths: []string{filepath.Join("..", "..", "config", "crd", "bases")}, + ErrorIfCRDPathMissing: true, + } + + var err error + cfg, err = testEnv.Start() + Expect(err).NotTo(HaveOccurred()) + Expect(cfg).NotTo(BeNil()) +}) + +var _ = AfterSuite(func() { + By("tearing down the test environment") + Expect(testEnv.Stop()).To(Succeed()) +}) diff --git a/core/config.go b/core/config.go index ca247284e..6e14c97af 100644 --- a/core/config.go +++ b/core/config.go @@ -25,83 +25,131 @@ package core import ( + "bytes" "errors" "fmt" + "net/url" + "os" + "os/user" + "path/filepath" + "strings" + "time" + + "github.com/AliceO2Group/Control/apricot" + apricotpb "github.com/AliceO2Group/Control/apricot/protos" "github.com/AliceO2Group/Control/common/product" "github.com/AliceO2Group/Control/common/utils" - "github.com/mesos/mesos-go/api/v1/cmd" - "github.com/mesos/mesos-go/api/v1/lib/encoding/codecs" + "github.com/AliceO2Group/Control/configuration/componentcfg" + "github.com/AliceO2Group/Control/core/task/schedutil" "github.com/spf13/pflag" "github.com/spf13/viper" "golang.org/x/sys/unix" - "net/url" - "os" - "path/filepath" ) -import _ "github.com/spf13/viper/remote" - func setDefaults() error { exe, err := os.Executable() if err != nil { - return errors.New("Cannot find scheduler executable path: " + err.Error()) + return errors.New("cannot find scheduler executable path: " + err.Error()) } exeDir := filepath.Dir(exe) - viper.SetDefault("controlPort", 47102) - viper.SetDefault("coreConfigurationUri", "consul://127.0.0.1:8500") //TODO: TBD - viper.SetDefault("defaultRepo", "github.com/AliceO2Group/ControlWorkflows") - viper.SetDefault("executor", env("EXEC_BINARY", filepath.Join(exeDir, "o2control-executor"))) - viper.SetDefault("executorCPU", envFloat("EXEC_CPU", "0.01")) - viper.SetDefault("executorMemory", envFloat("EXEC_MEMORY", "64")) + viper.Set("component", "core") + viper.SetDefault("version", false) + viper.SetDefault("controlPort", 32102) + viper.SetDefault("coreConfigurationUri", "") + viper.SetDefault("consulBasePath", "o2/components/aliecs/ANY/any") + viper.SetDefault("coreWorkingDir", "/var/lib/o2/aliecs") + viper.SetDefault("defaultRepo", "github.com/AliceO2Group/ControlWorkflows/") + viper.SetDefault("executor", getenv("EXEC_BINARY", filepath.Join(exeDir, "o2-aliecs-executor"))) + viper.SetDefault("executorCPU", getenvFloat("EXEC_CPU", "0.01")) + viper.SetDefault("executorMemory", getenvFloat("EXEC_MEMORY", "64")) + viper.SetDefault("globalDefaultRevision", "master") viper.SetDefault("instanceName", fmt.Sprintf("%s instance", product.PRETTY_SHORTNAME)) - viper.SetDefault("mesosApiTimeout", envDuration("MESOS_CONNECT_TIMEOUT", "20s")) - viper.SetDefault("mesosAuthMode", env("AUTH_MODE", "")) + viper.SetDefault("mesosApiTimeout", getenvDuration("MESOS_CONNECT_TIMEOUT", "20s")) + viper.SetDefault("mesosAuthMode", getenv("AUTH_MODE", "")) viper.SetDefault("mesosCheckpoint", true) - viper.SetDefault("mesosCodec", codec{Codec: codecs.ByMediaType[codecs.MediaTypeProtobuf]}) viper.SetDefault("mesosCompression", false) - viper.SetDefault("mesosCredentials.username", env("AUTH_USER", "")) - viper.SetDefault("mesosCredentials.passwordFile", env("AUTH_PASSWORD_FILE", "")) - viper.SetDefault("mesosExecutorImage", env("EXEC_IMAGE", cmd.DockerImageTag)) - viper.SetDefault("mesosFailoverTimeout", envDuration("SCHEDULER_FAILOVER_TIMEOUT", "1000h")) + viper.SetDefault("mesosUseSystemProxy", false) + viper.SetDefault("mesosCredentials.username", getenv("AUTH_USER", "")) + viper.SetDefault("mesosCredentials.passwordFile", getenv("AUTH_PASSWORD_FILE", "")) + viper.SetDefault("mesosExecutorImage", getenv("EXEC_IMAGE", "")) + viper.SetDefault("mesosFailoverTimeout", getenvDuration("SCHEDULER_FAILOVER_TIMEOUT", "1000h")) viper.SetDefault("mesosFrameworkHostname", "") - viper.SetDefault("mesosFrameworkName", env("FRAMEWORK_NAME", product.NAME)) + viper.SetDefault("mesosFrameworkName", getenv("FRAMEWORK_NAME", product.NAME)) viper.SetDefault("mesosFrameworkRole", "") - viper.SetDefault("mesosFrameworkUser", env("FRAMEWORK_USER", "root")) + viper.SetDefault("mesosFrameworkUser", getenv("FRAMEWORK_USER", "root")) viper.SetDefault("mesosGpuClusterCompat", false) - viper.SetDefault("mesosJobRestartDelay", envDuration("JOB_RESTART_DELAY", "5s")) - viper.SetDefault("mesosLabels", Labels{}) - viper.SetDefault("mesosMaxRefuseSeconds", envDuration("MAX_REFUSE_SECONDS", "5s")) + viper.SetDefault("mesosJobRestartDelay", getenvDuration("JOB_RESTART_DELAY", "5s")) + viper.SetDefault("mesosLabels", schedutil.Labels{}) + viper.SetDefault("mesosMaxRefuseSeconds", getenvDuration("MAX_REFUSE_SECONDS", "5s")) viper.SetDefault("mesosPrincipal", "") - viper.SetDefault("mesosReviveBurst", envInt("REVIVE_BURST", "3")) - viper.SetDefault("mesosReviveWait", envDuration("REVIVE_WAIT", "1s")) + viper.SetDefault("mesosReviveBurst", getenvInt("REVIVE_BURST", "3")) + viper.SetDefault("mesosReviveWait", getenvDuration("REVIVE_WAIT", "1s")) viper.SetDefault("mesosResourceTypeMetrics", false) - viper.SetDefault("mesosUrl", env("MESOS_MASTER_HTTP", "http://:5050/api/v1/scheduler")) + viper.SetDefault("mesosUrl", getenv("MESOS_MASTER_HTTP", "http://:5050/api/v1/scheduler")) viper.SetDefault("mesosCredentials.username", "") viper.SetDefault("mesosCredentials.passwordFile", "") - viper.SetDefault("metrics.address", env("LIBPROCESS_IP", "127.0.0.1")) - viper.SetDefault("metrics.port", envInt("PORT0", "64009")) - viper.SetDefault("metrics.path", env("METRICS_API_PATH", "/metrics")) - viper.SetDefault("repositoriesPath", "/etc/aliecs.d/repos") + viper.SetDefault("metrics.address", getenv("LIBPROCESS_IP", "127.0.0.1")) + viper.SetDefault("metrics.port", getenvInt("PORT0", "64009")) + viper.SetDefault("metrics.path", getenv("METRICS_API_PATH", "/metrics")) + viper.SetDefault("reposSshKey", "") viper.SetDefault("summaryMetrics", false) viper.SetDefault("verbose", false) viper.SetDefault("veryVerbose", false) - viper.SetDefault("globalConfigurationUri", "") //TODO: TBD - + viper.SetDefault("dumpWorkflows", false) + viper.SetDefault("configServiceUri", "apricot://127.0.0.1:32101") + viper.SetDefault("bookkeepingBaseUri", "http://127.0.0.1:4000") + viper.SetDefault("ccdbEndpoint", "http://ccdb-test.cern.ch:8080") + viper.SetDefault("dcsServiceEndpoint", "//127.0.0.1:50051") + viper.SetDefault("dcsServiceUseSystemProxy", false) + viper.SetDefault("ddSchedulerStatusTimeout", "30s") + viper.SetDefault("ddSchedulerEndpoint", "//127.0.0.1:50052") + viper.SetDefault("ddSchedulerUseSystemProxy", false) + viper.SetDefault("trgServiceEndpoint", "//127.0.0.1:50060") + viper.SetDefault("trgPollingInterval", "3s") + viper.SetDefault("trgPollingTimeout", "3s") + viper.SetDefault("trgReconciliationTimeout", "5s") + viper.SetDefault("odcEndpoint", "//127.0.0.1:50053") + viper.SetDefault("odcPollingInterval", "3s") + viper.SetDefault("odcUseSystemProxy", false) + viper.SetDefault("testPluginEndpoint", "//127.0.0.1:00000") + viper.SetDefault("integrationPlugins", []string{}) + viper.SetDefault("coreConfigEntry", "settings") + viper.SetDefault("fmqPlugin", "OCClite") + viper.SetDefault("fmqPluginSearchPath", "$CONTROL_OCCPLUGIN_ROOT/lib/") + viper.SetDefault("bookkeepingToken", "token") + viper.SetDefault("kafkaEndpoint", "localhost:9092") + viper.SetDefault("concurrentWorkflowTemplateProcessing", true) + viper.SetDefault("concurrentWorkflowTemplateIteratorProcessing", true) + viper.SetDefault("concurrentIteratorRoleExpansion", true) + viper.SetDefault("reuseUnlockedTasks", false) + viper.SetDefault("configCache", true) + viper.SetDefault("taskClassCacheTTL", 7*24*time.Hour) + viper.SetDefault("kafkaEndpoints", []string{"localhost:9092"}) + viper.SetDefault("enableKafka", true) + viper.SetDefault("logAllIL", false) + viper.SetDefault("metricsEndpoint", "8088/ecsmetrics") + viper.SetDefault("enableKubernetes", false) + viper.SetDefault("kubeNamespace", "alice-tasks") + viper.SetDefault("kubeConfig", "/home/aliecs/.kube/config") + viper.SetDefault("jitK8sBasePort", uint16(32000)) return nil } func setFlags() error { + pflag.Bool("version", viper.GetBool("version"), "The current AliECS core version") pflag.Int("controlPort", viper.GetInt("controlPort"), "Port of control server") - pflag.String("coreConfigurationUri", viper.GetString("coreConfigurationUri"), "URI of the Consul server or YAML configuration file, used for core configuration.") + pflag.String("coreConfigurationUri", viper.GetString("coreConfigurationUri"), "Consul URI or filesystem path to JSON/YAML configuration payload to initialize core settings [EXPERT SETTING]") + pflag.String("coreWorkingDir", viper.GetString("coreWorkingDir"), "Path to a writable directory for runtime AliECS data") pflag.String("executor", viper.GetString("executor"), "Full path to executor binary on Mesos agents") pflag.Float64("executorCPU", viper.GetFloat64("executorCPU"), "CPU resources to consume per-executor") pflag.Float64("executorMemory", viper.GetFloat64("executorMemory"), "Memory resources (MB) to consume per-executor") - pflag.String("instanceName", viper.GetString("instanceName"), "User-visible name for this AliECS instance.") + pflag.String("instanceName", viper.GetString("instanceName"), "User-visible name for this AliECS instance") pflag.Duration("mesosApiTimeout", viper.GetDuration("mesosApiTimeout"), "Mesos scheduler API connection timeout") - pflag.String("mesosAuthMode", viper.GetString("mesosAuthMode"), "Method to use for Mesos authentication; specify '"+AuthModeBasic+"' for simple HTTP authentication") + pflag.String("mesosAuthMode", viper.GetString("mesosAuthMode"), "Method to use for Mesos authentication; specify '"+schedutil.AuthModeBasic+"' for simple HTTP authentication") pflag.Bool("mesosCheckpoint", viper.GetBool("mesosCheckpoint"), "Enable/disable agent checkpointing for framework tasks (recover from agent failure)") - pflag.Bool("mesosCompression", viper.GetBool("mesosCompression"), "When true attempt to use compression for HTTP streams.") + pflag.Bool("mesosCompression", viper.GetBool("mesosCompression"), "When true attempt to use compression for HTTP streams") + pflag.Bool("mesosUseSystemProxy", viper.GetBool("mesosUseSystemProxy"), "When true the https_proxy, http_proxy and no_proxy environment variables are obeyed") pflag.String("mesosExecutorImage", viper.GetString("mesosExecutorImage"), "Name of the docker image to run the executor") pflag.Duration("mesosFailoverTimeout", viper.GetDuration("mesosFailoverTimeout"), "Framework failover timeout (recover from scheduler failure)") pflag.String("mesosFrameworkHostname", viper.GetString("mesosFrameworkHostname"), "Framework hostname that is advertised to the master") @@ -121,55 +169,145 @@ func setFlags() error { pflag.String("metrics.address", viper.GetString("metrics.address"), "IP of metrics server") pflag.Int("metrics.port", viper.GetInt("metrics.port"), "Port of metrics server (listens on server.address)") pflag.String("metrics.path", viper.GetString("metrics.path"), "URI path to metrics endpoint") + pflag.String("reposSshKey", viper.GetString("reposSshKey"), "Path to a readable private ssh key for repo operations") pflag.Bool("summaryMetrics", viper.GetBool("summaryMetrics"), "Collect summary metrics for tasks launched per-offer-cycle, offer processing time, etc.") - pflag.String("repositoriesPath", viper.GetString("repositoriesPath"), "Path to git-managed configuration repositories") pflag.Bool("verbose", viper.GetBool("verbose"), "Verbose logging") pflag.Bool("veryVerbose", viper.GetBool("veryVerbose"), "Very verbose logging") - pflag.String("globalConfigurationUri", viper.GetString("globalConfigurationUri"), "URI of the Consul server or YAML configuration file, used for global configuration.") + pflag.Bool("dumpWorkflows", viper.GetBool("dumpWorkflows"), "Dump unprocessed and processed workflow files (`$PWD/wf-{,un}processed-.json`)") + pflag.String("configServiceUri", viper.GetString("configServiceUri"), "URI of the Apricot instance (`apricot://host:port`), Consul server (`consul://`) or YAML configuration file, entry point for all configuration") + pflag.String("dcsServiceEndpoint", viper.GetString("dcsServiceEndpoint"), "Endpoint of the DCS gRPC service (`host:port`)") + pflag.Bool("dcsServiceUseSystemProxy", viper.GetBool("dcsServiceUseSystemProxy"), "When true the https_proxy, http_proxy and no_proxy environment variables are obeyed") + pflag.String("ddSchedulerEndpoint", viper.GetString("ddSchedulerEndpoint"), "Endpoint of the DD scheduler gRPC service (`host:port`)") + pflag.Duration("ddSchedulerStatusTimeout", viper.GetDuration("ddSchedulerStatusTimeout"), "Timeout for status calls in ddshed plugin") + pflag.Bool("ddSchedulerUseSystemProxy", viper.GetBool("ddSchedulerUseSystemProxy"), "When true the https_proxy, http_proxy and no_proxy environment variables are obeyed") + pflag.String("trgServiceEndpoint", viper.GetString("trgServiceEndpoint"), "Endpoint of the TRG gRPC service (`host:port`)") + pflag.Duration("trgPollingInterval", viper.GetDuration("trgPollingInterval"), "How often to query the TRG gRPC service for run status (default: 3s)") + pflag.Duration("trgPollingTimeout", viper.GetDuration("trgPollingTimeout"), "Timeout for the query to the TRG gRPC service for run status (default: 3s)") + pflag.Duration("trgReconciliationTimeout", viper.GetDuration("trgReconciliationTimeout"), "Timeout for reconciliation requests to the TRG gRPC service (default: 5s)") + pflag.String("odcEndpoint", viper.GetString("odcEndpoint"), "Endpoint of the ODC gRPC service (`host:port`)") + pflag.String("odcPollingInterval", viper.GetString("odcPollingInterval"), "How often to query the ODC gRPC service for partition status (default: 3s)") + pflag.Bool("odcUseSystemProxy", viper.GetBool("odcUseSystemProxy"), "When true the https_proxy, http_proxy and no_proxy environment variables are obeyed") + pflag.String("testPluginEndpoint", viper.GetString("testPluginEndpoint"), "Endpoint of the TEST plugin, actually a NOOP") + pflag.StringSlice("integrationPlugins", viper.GetStringSlice("integrationPlugins"), "List of integration plugins to load (default: empty)") + pflag.String("coreConfigEntry", viper.GetString("coreConfigEntry"), "key for AliECS core configuration within the `aliecs` component [EXPERT SETTING]") + pflag.String("fmqPlugin", viper.GetString("fmqPlugin"), "Name of the plugin for FairMQ tasks") + pflag.String("fmqPluginSearchPath", viper.GetString("fmqPluginSearchPath"), "Path to the directory where the FairMQ plugins are found on controlled nodes") + pflag.String("kafkaEndpoint", viper.GetString("kafkaEndpoint"), "Endpoint of the Kafka service (`host:port`)") + pflag.String("bookkeepingBaseUri", viper.GetString("bookkeepingBaseUri"), "URI of the O² Bookkeeping service (`protocol://host:port`)") + pflag.Bool("concurrentWorkflowTemplateProcessing", viper.GetBool("concurrentWorkflowTemplateProcessing"), "Process aggregators in workflow templates concurrently") + pflag.Bool("concurrentWorkflowTemplateIteratorProcessing", viper.GetBool("concurrentWorkflowTemplateIteratorProcessing"), "Process iterators in workflow templates concurrently") + pflag.Bool("concurrentIteratorRoleExpansion", viper.GetBool("concurrentIteratorRoleExpansion"), "Expand iterator roles concurrently during workflow template processing") + pflag.Bool("reuseUnlockedTasks", viper.GetBool("reuseUnlockedTasks"), "Reuse unlocked active tasks when satisfying environment deployment requests") + pflag.Bool("configCache", viper.GetBool("configCache"), "Enable cache layer between AliECS core and Apricot") + pflag.Duration("taskClassCacheTTL", viper.GetDuration("taskClassCacheTTL"), "TTL for task class cache entries") + pflag.StringSlice("kafkaEndpoints", viper.GetStringSlice("kafkaEndpoints"), "List of Kafka endpoints to connect to (default: localhost:9092)") + pflag.Bool("enableKafka", viper.GetBool("enableKafka"), "Turn on the kafka messaging") + pflag.Bool("logAllIL", viper.GetBool("logAllIL"), "Send all the logs into IL, including Debug and Trace messages") + pflag.String("metricsEndpoint", viper.GetString("metricsEndpoint"), "Http endpoint from which metrics can be scraped: [port/endpoint]") + pflag.Bool("enableKubernetes", viper.GetBool("enableKubernetes"), "Turn on Kubernetes support (requires valid kubeNamespace and kubeConfig)") + pflag.String("kubeNamespace", viper.GetString("kubeNamespace"), "Kubernetes namespace in which ECS creates Task and Environment objects") + pflag.String("kubeConfig", viper.GetString("kubeConfig"), "Path to the kubeconfig used to access the Kubernetes cluster (empty uses in-cluster config)") + pflag.Uint16("jitK8sBasePort", viper.GetUint16("jitK8sBasePort"), "First control port to allocate for JIT tasks on Kubernetes, incremented per-node") pflag.Parse() return viper.BindPFlags(pflag.CommandLine) } -func parseCoreConfig() error { - coreCfgUri := viper.GetString("coreConfigurationUri") - uri, err := url.Parse(coreCfgUri) - if err != nil { - return err +func checkFmqPluginName() error { + allowedPluginNames := []string{"OCC", "OCClite"} + chosenPlugin := viper.GetString("fmqPlugin") + for _, allowed := range allowedPluginNames { + if chosenPlugin == allowed { + return nil + } } + return fmt.Errorf("plugin name \"%s\" is invalid, allowed values: %s", + chosenPlugin, + strings.Join(allowedPluginNames, ", "), + ) +} - if uri.Scheme == "file" { - viper.SetConfigFile(uri.Host + uri.Path) - if err := viper.ReadInConfig(); err != nil { - return errors.New(coreCfgUri + ": " + err.Error()); - } - } else if uri.Scheme == "consul"{ - if err := viper.AddRemoteProvider("consul", uri.Host, uri.Path); err != nil { +func parseCoreConfig() error { + coreCfgUri := viper.GetString("coreConfigurationUri") + if len(coreCfgUri) != 0 { + log.WithField("coreConfigurationUri", coreCfgUri). + Warn("core configuration override (this should normally not happen)") + uri, err := url.Parse(coreCfgUri) + if err != nil { return err } - viper.SetConfigType("yaml") - if err := viper.ReadRemoteConfig(); err != nil { - return errors.New(coreCfgUri + ": " + err.Error()) + if uri.Scheme == "file" { + viper.SetConfigFile(uri.Host + uri.Path) + if err := viper.ReadInConfig(); err != nil { + return errors.New(coreCfgUri + ": " + err.Error()) + } + } else if uri.Scheme == "consul" { + if err := viper.AddRemoteProvider("consul", uri.Host, uri.Path); err != nil { + return err + } + viper.SetConfigType("yaml") + if err := viper.ReadRemoteConfig(); err != nil { + return errors.New(coreCfgUri + ": " + err.Error()) + } + } else { + return errors.New(coreCfgUri + ": Core configuration URI could not be parsed (Expecting consul://* or file://*)") } - } else { - return errors.New(coreCfgUri + ": Core configuration URI could not be parsed (Expecting consul://* or file://*)") } + // aliecs configuration is assumed to live in aliecs/ANY/any/settings + payload, err := apricot.Instance().GetComponentConfiguration(&componentcfg.Query{ + Component: "aliecs", + RunType: apricotpb.RunType_ANY, + RoleName: "any", + EntryKey: viper.GetString("coreConfigEntry"), // defaults to "settings" + }) + if err != nil { + return errors.New(viper.GetString("configServiceUri") + ": could not acquire core configuration (possibly bad configServiceUri, expecting apricot://*, consul://* or file://*)") + } + + viper.SetConfigType("yaml") + if err := viper.ReadConfig(bytes.NewBuffer([]byte(payload))); err != nil { + return errors.New(coreCfgUri + ": " + err.Error()) + } return nil } func checkRepoDirRights() error { - err := unix.Access(viper.GetString("repositoriesPath"), unix.W_OK) + repoDir := filepath.Join(viper.GetString("coreWorkingDir"), "repos") + utils.EnsureTrailingSlash(&repoDir) + err := unix.Access(repoDir, unix.W_OK) if err != nil { - return errors.New("No write access for configuration repositories path \"" + viper.GetString("repositoriesPath") + "\": "+ err.Error()) + return errors.New("No write access for configuration repositories path \"" + repoDir + "\": " + err.Error()) } return nil } -func sanitizeReposPath() { - sanitizedReposPath := viper.GetString("repositoriesPath") - utils.EnsureTrailingSlash(&sanitizedReposPath) - viper.Set("repositoriesPath", sanitizedReposPath) +func checkWorkingDirRights() error { + err := unix.Access(viper.GetString("coreWorkingDir"), unix.W_OK) + if err != nil { + return errors.New("No write access for core working path \"" + viper.GetString("coreWorkingDir") + "\": " + err.Error()) + } + return nil +} + +func checkSshKeyRights() error { + reposSshKey := viper.GetString("reposSshKey") + if reposSshKey == "" { + log.Trace("No repos ssh key provided") + return nil + } + + err := unix.Access(reposSshKey, unix.R_OK) + if err != nil { + return errors.New("No read access for repos ssh key: \"" + viper.GetString("reposSshKey") + "\": " + err.Error()) + } + return nil +} + +// Remove trailing '/' +func sanitizeWorkingPath() { + sanitizeWorkingPath := filepath.Clean(viper.GetString("coreWorkingDir")) + viper.Set("coreWorkingDir", sanitizeWorkingPath) } // Bind environment variables with the prefix ALIECS @@ -179,8 +317,6 @@ func bindEnvironmentVariables() { viper.AutomaticEnv() } -const AuthModeBasic = "basic" - // NewConfig is the constructor for a new config. func NewConfig() (err error) { if err = setDefaults(); err != nil { @@ -189,19 +325,48 @@ func NewConfig() (err error) { if err = setFlags(); err != nil { return } - if err = parseCoreConfig(); err != nil { + + if viper.GetBool("version") { + var executable string + executable, err = os.Executable() + if err != nil { + executable = "unknown path" + err = nil + } + var usr *user.User + usr, err = user.Current() + if err != nil { + usr = &user.User{} + err = nil + } + + fmt.Printf("%s core (%s) v%s build %s running as user %s from %s\n", + product.PRETTY_FULLNAME, + product.PRETTY_SHORTNAME, + product.VERSION, + product.BUILD, + usr.Username, + executable) + os.Exit(0) + return + } + + if err = parseCoreConfig(); err != nil { return } bindEnvironmentVariables() - sanitizeReposPath() + if err = checkFmqPluginName(); err != nil { + return + } if err = checkRepoDirRights(); err != nil { return } - + if err = checkSshKeyRights(); err != nil { + log.Warning(err) + } + sanitizeWorkingPath() + if err = checkWorkingDirRights(); err != nil { + return + } return } - -type credentials struct { - username string - password string -} diff --git a/core/confsys/service.go b/core/confsys/service.go deleted file mode 100644 index ff03feb98..000000000 --- a/core/confsys/service.go +++ /dev/null @@ -1,190 +0,0 @@ -/* - * === This file is part of ALICE O² === - * - * Copyright 2019 CERN and copyright holders of ALICE O². - * Author: Teo Mrnjavac - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * In applying this license CERN does not waive the privileges and - * immunities granted to it by virtue of its status as an - * Intergovernmental Organization or submit itself to any jurisdiction. - */ - -package confsys - -import ( - "io/ioutil" - "os" - "path/filepath" - "strconv" - "strings" - "sync" - - "github.com/AliceO2Group/Control/common/logger" - "github.com/AliceO2Group/Control/configuration" - "github.com/mitchellh/go-homedir" - "github.com/sirupsen/logrus" - "github.com/spf13/viper" -) - -var log = logger.New(logrus.StandardLogger(), "confsys") - -var ( - once sync.Once - instance *Service -) - -func Instance() *Service { - once.Do(func() { - var err error - configUri := viper.GetString("globalConfigurationUri") - instance, err = newService(configUri) - if err != nil { - log.WithField("globalConfigurationUri", configUri).Fatal("bad configuration URI") - } - }) - return instance -} - - - -type Service struct { - src configuration.Source -} - -/* Expected structure: -/o2/aliecs/ -{ - run_number_file: "/path/to/rn/file", - - or - - run_number: 47102, - - settings: { - log_level: "DEBUG" - }, - global_vars: { - n_flps: 42 - }, - repositories: { - base: { - url: "https://gitlab.cern.ch/foo/bar", - user: "gituser", - pass: "gitpass" - }, - extra: {...} - } -} - -*/ -func formatKey(key string) (consulKey string) { - // Trim leading slashes - consulKey = strings.TrimLeft(key, "/") - return -} - -func newService(uri string) (svc *Service, err error) { - var src configuration.Source - src, err = configuration.NewSource(uri) - return &Service{src: src}, err -} - -func (s *Service) NewDefaultRepo(defaultRepo string) error { - if cSrc, ok := s.src.(*configuration.ConsulSource); ok { - return cSrc.Put("o2/control/default_repo", defaultRepo) - } else { - data := []byte(defaultRepo) - return ioutil.WriteFile(viper.GetString("repositoriesPath") + "default_repo", data, 0644) - } -} - -func (s *Service) GetDefaultRepo() (defaultRepo string, err error) { - if cSrc, ok := s.src.(*configuration.ConsulSource); ok { - return cSrc.Get("o2/control/default_repo") - } else { - var defaultRepoData []byte - defaultRepoData, err = ioutil.ReadFile(viper.GetString("repositoriesPath") + "default_repo") - if err != nil { - return - } - defaultRepo = strings.TrimSuffix(string(defaultRepoData), "/n") - return - } -} - -func (s *Service) NewRunNumber() (runNumber uint32, err error) { - if cSrc, ok := s.src.(*configuration.ConsulSource); ok { - return cSrc.GetNextUInt32("o2/control/run_number") - } else { - // Unsafe check-and-set, only for file backend - var rnf string - rnf, err = s.src.Get("o2/control/run_number_file") - if err != nil { - return - } - rnf, err = homedir.Expand(rnf) // Resolve ~ into home dir - if err != nil { - return - } - if _, err = os.Stat(rnf); os.IsNotExist(err) { - err = os.MkdirAll(filepath.Dir(rnf), os.ModePerm) - if err != nil { - return - } - err = ioutil.WriteFile(rnf, []byte("0"), 0644) - if err != nil { - return - } - } - var raw []byte - raw, err = ioutil.ReadFile(rnf) - if err != nil { - return - } - var rn64 uint64 - rn64, err = strconv.ParseUint(string(raw[:]), 10, 32) - if err != nil { - return - } - runNumber = uint32(rn64) - runNumber++ - raw = []byte(strconv.FormatUint(uint64(runNumber), 10)) - err = ioutil.WriteFile(rnf, raw, 0) - return - } -} - -func (s *Service) GetROSource() configuration.ROSource { - return s.src -} - -// maybe this one shouldn't exist at all, because vars should get inserted -// response: but not all of them! some vars will likely only get parsed at deployment time i.e. right -// before pushing TaskInfos -func (s *Service) GetVars() map[string]string { - //FIXME: implement - return nil -} - -// Or maybe even "RefreshConfig" which will refresh all the things that happen to be runtime-refreshable -func (s *Service) RefreshRepositories() { - panic("not implemented yet") -} - -// Returns a YAML file OR even a structure made of Roles or Nodes with: -// import() functions already computed and resolved -// vars inserted (todo: figure out which vars get parsed when and where) -func (s *Service) GenerateWorkflowDescriptor(wfPath string, vars map[string]string /*vars from cli/gui*/) string { - panic("not implemented yet") -} \ No newline at end of file diff --git a/core/controlcommands/command.go b/core/controlcommands/command.go index 1698dc63b..4c782e25b 100644 --- a/core/controlcommands/command.go +++ b/core/controlcommands/command.go @@ -29,8 +29,8 @@ package controlcommands type Command interface { - Name() string - Args() map[string]interface{} + Name() string + Args() map[string]interface{} IsMultiCmd() bool - IsMutator() bool + IsMutator() bool } diff --git a/core/controlcommands/commandqueue.go b/core/controlcommands/commandqueue.go index 4a4ca3bcd..66af7c656 100644 --- a/core/controlcommands/commandqueue.go +++ b/core/controlcommands/commandqueue.go @@ -25,15 +25,19 @@ package controlcommands import ( - "sync" - "github.com/AliceO2Group/Control/common/logger" - "github.com/sirupsen/logrus" "errors" "fmt" + "github.com/AliceO2Group/Control/common/logger/infologger" "strings" + "sync" + "time" + + "github.com/AliceO2Group/Control/common/logger" + "github.com/AliceO2Group/Control/common/utils" + "github.com/sirupsen/logrus" ) -const QUEUE_SIZE = 1024 +const QUEUE_SIZE = 16384 // upper limit of command queue size var log = logger.New(logrus.StandardLogger(), "cmdq") @@ -47,8 +51,8 @@ type empty struct{} type CommandQueue struct { sync.Mutex - q chan queueEntry - servent *Servent + q chan queueEntry + servent *Servent } func NewCommandQueue(s *Servent) *CommandQueue { @@ -58,22 +62,20 @@ func NewCommandQueue(s *Servent) *CommandQueue { } func (m *CommandQueue) Enqueue(cmd MesosCommand, callback chan<- MesosCommandResponse) error { - m.Lock() - defer m.Unlock() - select { case m.q <- queueEntry{cmd, callback}: return nil default: // Buffer full! err := errors.New("the queue for MESSAGE commands is full") - log.WithField("error", err.Error()). - WithField("queueSize", QUEUE_SIZE). - Error("cannot enqueue control command") + log.WithField("partition", cmd.GetEnvironmentId().String()). + WithField("error", err.Error()). + WithField("queueSize", QUEUE_SIZE). + Error("cannot enqueue control command") return err } } -func (m* CommandQueue) Start() { +func (m *CommandQueue) Start() { m.Lock() m.q = make(chan queueEntry, QUEUE_SIZE) m.Unlock() @@ -82,16 +84,25 @@ func (m* CommandQueue) Start() { for { select { case entry, more := <-m.q: - m.Lock() - if !more { // if the channel is closed, we bail + if !more { // if the channel is closed, we bail return } + m.Lock() response, err := m.commit(entry.cmd) if err != nil { - log.Debug(err) + if entry.cmd != nil { + log.WithError(err). + WithField("partition", entry.cmd.GetEnvironmentId().String()). + Debugf("failed to commit CommandQueue entry %s", entry.cmd.GetName()) + } else { + log.WithError(err). + WithField("partition", entry.cmd.GetEnvironmentId().String()). + Debug("failed to commit unknown CommandQueue entry") + } } - if response == nil { - log.Error("nil response") + if err == nil && response == nil { + log.WithField("partition", entry.cmd.GetEnvironmentId().String()). + Errorf("did not receive neither response nor error for %s", entry.cmd.GetName()) } entry.callback <- response @@ -111,55 +122,98 @@ func (m *CommandQueue) commit(command MesosCommand) (response MesosCommandRespon if m == nil { return nil, errors.New("command queue is nil") } + log.WithPrefix("cmdq"). + WithField("id", command.GetId().String()). + WithField("partition", command.GetEnvironmentId().String()). + Debugf("cmdq.commit %s to %d targets begin", command.GetName(), len(command.targets())) + defer utils.TimeTrack(time.Now(), + fmt.Sprintf("cmdq.commit %s to %d targets", command.GetName(), len(command.targets())), + log.WithPrefix("cmdq"). + WithField("partition", command.GetEnvironmentId().String()). + WithField("id", command.GetId().String())) + + type responseSemaphore struct { + receiver MesosCommandTarget + response MesosCommandResponse + err error + } // Parallel for sendErrorList := make([]error, 0) - semaphore := make(chan empty, len(command.targets())) + semaphore := make(chan responseSemaphore, len(command.targets())) responses := make(map[MesosCommandTarget]MesosCommandResponse) - log.WithFields(logrus.Fields{ + log.WithField("partition", command.GetEnvironmentId().String()). + WithFields(logrus.Fields{ "name": command.GetName(), - "id": command.GetId(), + "id": command.GetId(), }). Debug("ready to commit MesosCommand") for _, rec := range command.targets() { go func(receiver MesosCommandTarget) { - log.WithFields(logrus.Fields{ - "agentId": receiver.AgentId, + log.WithField("partition", command.GetEnvironmentId().String()). + WithFields(logrus.Fields{ + "agentId": receiver.AgentId, "executorId": receiver.ExecutorId, - "name": command.GetName(), + "name": command.GetName(), }). - Debug("sending MesosCommand to target") + Trace("sending MesosCommand to target") singleCommand := command.MakeSingleTarget(receiver) res, err := m.servent.RunCommand(singleCommand, receiver) if err != nil { - log.WithError(err).Warning("MesosCommand send error") - sendErrorList = append(sendErrorList, err) - - responses[receiver] = res - semaphore <- empty{} + log.WithField("partition", command.GetEnvironmentId().String()). + WithField(infologger.Level, infologger.IL_Devel). + WithError(err). + Warning("MesosCommand send error") + + semaphore <- responseSemaphore{ + receiver: receiver, + response: res, + err: err, + } return } - log.WithFields(logrus.Fields{ - "commandName": res.GetCommandName(), - "error": res.Err().Error(), - }). - Debug("received MesosCommandResponse") - responses[receiver] = res + if res.Err() != nil { + log.WithField("partition", command.GetEnvironmentId().String()). + WithFields(logrus.Fields{ + "commandName": res.GetCommandName(), + "error": res.Err().Error(), + }). + Trace("received MesosCommandResponse with error") + } else { + log.WithField("partition", command.GetEnvironmentId().String()). + WithFields(logrus.Fields{ + "commandName": res.GetCommandName(), + }). + Trace("received MesosCommandResponse") + } - semaphore <- empty{} + semaphore <- responseSemaphore{ + receiver: receiver, + response: res, + } }(rec) } // Wait for goroutines to finish for i := 0; i < len(command.targets()); i++ { - <- semaphore + respSemaphore := <-semaphore + // for the sake of better error propagation, we treat a lack of response as a response with error, + // even though it's not technically the same. it can be surely done better, but it would require a larger refactoring. + if respSemaphore.err != nil && respSemaphore.response == nil { + respSemaphore.response = NewMesosCommandResponse(command, respSemaphore.err) + } + responses[respSemaphore.receiver] = respSemaphore.response + if respSemaphore.err != nil { + sendErrorList = append(sendErrorList, respSemaphore.err) + } } close(semaphore) - log.WithFields(logrus.Fields{}).Debug("responses collected") + log.WithField("partition", command.GetEnvironmentId().String()). + Debug("responses collected") if len(sendErrorList) != 0 { err = errors.New(strings.Join(func() (out []string) { @@ -168,11 +222,11 @@ func (m *CommandQueue) commit(command MesosCommand) (response MesosCommandRespon } return }(), "\n")) - return } response = consolidateResponses(command, responses) - log.Debug("responses consolidated, CommandQueue commit done") + log.WithField("partition", command.GetEnvironmentId().String()). + Debug("responses consolidated, CommandQueue commit done") - return response, nil + return response, err } diff --git a/core/controlcommands/mesoscommand.go b/core/controlcommands/mesoscommand.go index 29d1b5cbe..baaf23700 100644 --- a/core/controlcommands/mesoscommand.go +++ b/core/controlcommands/mesoscommand.go @@ -25,18 +25,23 @@ package controlcommands import ( + "github.com/AliceO2Group/Control/common" + "github.com/AliceO2Group/Control/common/utils/uid" "github.com/mesos/mesos-go/api/v1/lib" - "github.com/pborman/uuid" + "github.com/rs/xid" + "time" ) const ( - defaultResponseTimeout = 45 * time.Second + defaultResponseTimeout = 90 * time.Second ) type MesosCommand interface { + common.Labeler GetName() string - GetId() uuid.Array + GetId() xid.ID + GetEnvironmentId() uid.ID IsMultiCmd() bool MakeSingleTarget(target MesosCommandTarget) MesosCommand IsMutator() bool @@ -46,9 +51,9 @@ type MesosCommand interface { } type MesosCommandTarget struct { - AgentId mesos.AgentID - ExecutorId mesos.ExecutorID - TaskId mesos.TaskID + AgentId mesos.AgentID + ExecutorId mesos.ExecutorID + TaskId mesos.TaskID } type PropertyMap map[string]string @@ -56,17 +61,20 @@ type PropertyMapsMap map[MesosCommandTarget]PropertyMap type MesosCommandBase struct { Name string `json:"name"` - Id uuid.Array `json:"id"` + Id xid.ID `json:"id"` + EnvironmentId uid.ID `json:"environmentId"` ResponseTimeout time.Duration `json:"timeout"` Arguments PropertyMap `json:"arguments"` TargetList []MesosCommandTarget `json:"targetList"` + Labels map[string]string `json:"labels"` argMap PropertyMapsMap `json:"-"` } -func NewMesosCommand(name string, receivers []MesosCommandTarget, argMap PropertyMapsMap) (*MesosCommandBase) { +func NewMesosCommand(name string, envId uid.ID, receivers []MesosCommandTarget, argMap PropertyMapsMap) *MesosCommandBase { return &MesosCommandBase{ Name: name, - Id: uuid.NewUUID().Array(), + Id: xid.New(), + EnvironmentId: envId, ResponseTimeout: defaultResponseTimeout, TargetList: receivers, argMap: argMap, @@ -80,11 +88,18 @@ func (m *MesosCommandBase) GetName() string { return "" } -func (m *MesosCommandBase) GetId() uuid.Array { +func (m *MesosCommandBase) GetId() xid.ID { if m != nil { return m.Id } - return uuid.NIL.Array() + return xid.NilID() +} + +func (m *MesosCommandBase) GetEnvironmentId() uid.ID { + if m != nil { + return m.EnvironmentId + } + return uid.NilID() } func (m *MesosCommandBase) IsMultiCmd() bool { @@ -94,6 +109,13 @@ func (m *MesosCommandBase) IsMultiCmd() bool { return false } +func (m *MesosCommandBase) GetLabels() map[string]string { + if m != nil { + return m.Labels + } + return map[string]string{} +} + func (m *MesosCommandBase) MakeSingleTarget(receiver MesosCommandTarget) (cmd MesosCommand) { if m == nil { return @@ -119,6 +141,7 @@ func (m *MesosCommandBase) MakeSingleTarget(receiver MesosCommandTarget) (cmd Me cmd = &MesosCommandBase{ Name: m.Name, Id: m.Id, + EnvironmentId: m.EnvironmentId, ResponseTimeout: m.ResponseTimeout, TargetList: []MesosCommandTarget{receiver}, argMap: argMap, @@ -143,4 +166,4 @@ func (m *MesosCommandBase) targets() []MesosCommandTarget { return m.TargetList } return nil -} \ No newline at end of file +} diff --git a/core/controlcommands/mesoscommand_transition.go b/core/controlcommands/mesoscommand_transition.go index 97c59435d..48e56dbf7 100644 --- a/core/controlcommands/mesoscommand_transition.go +++ b/core/controlcommands/mesoscommand_transition.go @@ -24,12 +24,14 @@ package controlcommands +import "github.com/AliceO2Group/Control/common/utils/uid" + type MesosCommand_Transition struct { MesosCommandBase - Source string `json:"source"` - Event string `json:"event"` - Destination string `json:"destination"` + Source string `json:"source"` + Event string `json:"event"` + Destination string `json:"destination"` } func (m *MesosCommand_Transition) MakeSingleTarget(target MesosCommandTarget) (cmd MesosCommand) { @@ -51,9 +53,9 @@ func (m *MesosCommand_Transition) MakeSingleTarget(target MesosCommandTarget) (c return } -func NewMesosCommand_Transition(receivers []MesosCommandTarget, source string, event string, destination string, arguments PropertyMapsMap) (*MesosCommand_Transition) { +func NewMesosCommand_Transition(envId uid.ID, receivers []MesosCommandTarget, source string, event string, destination string, arguments PropertyMapsMap) *MesosCommand_Transition { return &MesosCommand_Transition{ - MesosCommandBase: *NewMesosCommand("MesosCommand_Transition", receivers, arguments), + MesosCommandBase: *NewMesosCommand("MesosCommand_Transition", envId, receivers, arguments), Source: source, Event: event, Destination: destination, @@ -73,4 +75,4 @@ func NewMesosCommandResponse_Transition(mesosCommand *MesosCommand_Transition, e CurrentState: currentState, TaskId: taskId, } -} \ No newline at end of file +} diff --git a/core/controlcommands/mesoscommand_triggerhook.go b/core/controlcommands/mesoscommand_triggerhook.go new file mode 100644 index 000000000..13bd0cc4e --- /dev/null +++ b/core/controlcommands/mesoscommand_triggerhook.go @@ -0,0 +1,65 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2020 CERN and copyright holders of ALICE O². + * Author: Teo Mrnjavac + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package controlcommands + +import "github.com/AliceO2Group/Control/common/utils/uid" + +type MesosCommand_TriggerHook struct { + MesosCommandBase +} + +func (m *MesosCommand_TriggerHook) MakeSingleTarget(target MesosCommandTarget) (cmd MesosCommand) { + if m == nil { + return + } + mc := m.MesosCommandBase.MakeSingleTarget(target) + mcb, ok := mc.(*MesosCommandBase) + if !ok { + return + } + + cmd = &MesosCommand_TriggerHook{ + MesosCommandBase: *mcb, + } + return +} + +func NewMesosCommand_TriggerHook(envId uid.ID, receivers []MesosCommandTarget) *MesosCommand_TriggerHook { + return &MesosCommand_TriggerHook{ + MesosCommandBase: *NewMesosCommand("MesosCommand_TriggerHook", envId, receivers, PropertyMapsMap{}), + } +} + +type MesosCommandResponse_TriggerHook struct { + MesosCommandResponseBase + TaskId string `json:"taskId"` +} + +func NewMesosCommandResponse_TriggerHook(mesosCommand *MesosCommand_TriggerHook, err error, taskId string) *MesosCommandResponse_TriggerHook { + return &MesosCommandResponse_TriggerHook{ + MesosCommandResponseBase: *NewMesosCommandResponse(mesosCommand, err), + TaskId: taskId, + } +} diff --git a/core/controlcommands/mesoscommandresponse.go b/core/controlcommands/mesoscommandresponse.go index 78a15dd1d..6163de889 100644 --- a/core/controlcommands/mesoscommandresponse.go +++ b/core/controlcommands/mesoscommandresponse.go @@ -27,24 +27,30 @@ package controlcommands import ( "errors" - "github.com/pborman/uuid" + "github.com/AliceO2Group/Control/common/utils/uid" + "github.com/rs/xid" ) type MesosCommandResponse interface { GetCommandName() string - GetCommandId() uuid.Array + GetCommandId() xid.ID + GetEnvironmentId() uid.ID + GetResponseSenders() []MesosCommandTarget IsMultiResponse() bool Err() error + Errors() map[MesosCommandTarget]error } type MesosCommandResponseBase struct { - CommandName string `json:"name"` - CommandId uuid.Array `json:"id"` - ErrorString string `json:"error"` - MessageType string `json:"_messageType"` + CommandName string `json:"name"` + CommandId xid.ID `json:"id"` + EnvironmentId uid.ID `json:"environmentId"` + ErrorString string `json:"error"` + MessageType string `json:"_messageType"` + ResponseSenders []MesosCommandTarget `json:"-"` } -func NewMesosCommandResponse(mesosCommand MesosCommand, err error) (*MesosCommandResponseBase) { +func NewMesosCommandResponse(mesosCommand MesosCommand, err error) *MesosCommandResponseBase { if mesosCommand == nil { log.Debug("trying to create MesosCommandResponseBase for nil MesosCommand, failing miserably") return nil @@ -58,10 +64,11 @@ func NewMesosCommandResponse(mesosCommand MesosCommand, err error) (*MesosComman } return &MesosCommandResponseBase{ - CommandName: mesosCommand.GetName(), - CommandId: mesosCommand.GetId(), - ErrorString: errStr, - MessageType: "MesosCommandResponse", + CommandName: mesosCommand.GetName(), + CommandId: mesosCommand.GetId(), + EnvironmentId: mesosCommand.GetEnvironmentId(), + ErrorString: errStr, + MessageType: "MesosCommandResponse", } } @@ -72,20 +79,53 @@ func (m *MesosCommandResponseBase) GetCommandName() string { return "" } -func (m *MesosCommandResponseBase) GetCommandId() uuid.Array { +func (m *MesosCommandResponseBase) GetCommandId() xid.ID { if m != nil { return m.CommandId } - return uuid.NIL.Array() + return xid.NilID() +} + +func (m *MesosCommandResponseBase) GetEnvironmentId() uid.ID { + if m != nil { + return m.EnvironmentId + } + return uid.NilID() } func (m *MesosCommandResponseBase) IsMultiResponse() bool { return false } +func (m *MesosCommandResponseBase) GetResponseSenders() []MesosCommandTarget { + if m != nil { + return m.ResponseSenders + } + return nil +} + func (m *MesosCommandResponseBase) Err() error { if m != nil { - return errors.New(m.ErrorString) + if len(m.ErrorString) > 0 { + return errors.New(m.ErrorString) + } + return nil } return errors.New("nil response") } + +// dummy implementation for single-origin responses which defaults to Err() +func (m *MesosCommandResponseBase) Errors() map[MesosCommandTarget]error { + errMap := make(map[MesosCommandTarget]error) + mct := MesosCommandTarget{} + if len(m.ResponseSenders) > 0 { + mct = m.ResponseSenders[0] + } + + if m != nil { + errMap[mct] = errors.New(m.ErrorString) + return errMap + } + errMap[mct] = errors.New("nil response") + return errMap +} diff --git a/core/controlcommands/mesoscommandservent.go b/core/controlcommands/mesoscommandservent.go index b0aad4565..4eded55eb 100644 --- a/core/controlcommands/mesoscommandservent.go +++ b/core/controlcommands/mesoscommandservent.go @@ -25,11 +25,13 @@ package controlcommands import ( - "errors" + "fmt" + "strings" "sync" "time" - "github.com/pborman/uuid" + "github.com/AliceO2Group/Control/common/utils" + "github.com/rs/xid" "github.com/sirupsen/logrus" ) @@ -45,20 +47,20 @@ type Call struct { func NewCall(cmd MesosCommand) *Call { return &Call{ - Request: cmd, + Request: cmd, Response: nil, - Done: make(chan empty), - Error: nil, + Done: make(chan empty), + Error: nil, } } type CallId struct { - Id uuid.Array + Id xid.ID Target MesosCommandTarget } type Servent struct { - mu sync.Mutex + mu sync.Mutex pending map[CallId]*Call SendFunc SendCommandFunc @@ -66,59 +68,102 @@ type Servent struct { func NewServent(commandFunc SendCommandFunc) *Servent { return &Servent{ - SendFunc: commandFunc, - pending: make(map[CallId]*Call), + SendFunc: commandFunc, + pending: make(map[CallId]*Call), } } func (s *Servent) RunCommand(cmd MesosCommand, receiver MesosCommandTarget) (MesosCommandResponse, error) { log.Debug("Servent.RunCommand BEGIN") defer log.Debug("Servent.RunCommand END") - log.Debug("servent mutex locking") - s.mu.Lock() - log.Debug("servent mutex locked") + + defer utils.TimeTrack(time.Now(), fmt.Sprintf("servent.RunCommand %s to target %s", cmd.GetName(), receiver.TaskId.Value), + log.WithPrefix("servent").WithField("partition", cmd.GetEnvironmentId().String())) + cmdId := cmd.GetId() call := NewCall(cmd) callId := CallId{ - Id: cmdId, + Id: cmdId, Target: receiver, } + log.WithPrefix("servent"). + WithField("partition", cmd.GetEnvironmentId().String()). + Trace("servent mutex locking") + s.mu.Lock() + log.WithPrefix("servent"). + WithField("partition", cmd.GetEnvironmentId().String()). + Trace("servent mutex locked") + // We append the new call to the pending map, and send the request s.pending[callId] = call - log.WithFields(logrus.Fields{ - "name": cmd.GetName(), - "id": cmd.GetId(), - "agentId": receiver.AgentId, + + s.mu.Unlock() + log.WithPrefix("servent"). + WithField("partition", cmd.GetEnvironmentId().String()). + Trace("servent mutex unlocked") + + log.WithPrefix("servent"). + WithFields(logrus.Fields{ + "name": cmd.GetName(), + "partition": cmd.GetEnvironmentId().String(), + "id": cmd.GetId(), + "agentId": receiver.AgentId, "executorId": receiver.ExecutorId, }). - Debug("calling scheduler SendFunc") + Trace("calling scheduler SendFunc") + err := s.SendFunc(cmd, receiver) if err != nil { + log.WithPrefix("servent"). + WithField("partition", cmd.GetEnvironmentId().String()). + Trace("servent mutex locking") + s.mu.Lock() + log.WithPrefix("servent"). + WithField("partition", cmd.GetEnvironmentId().String()). + Trace("servent mutex locked") + delete(s.pending, callId) + s.mu.Unlock() - log.WithError(err).Debug("servent mutex unlocked") + log.WithPrefix("servent"). + WithField("partition", cmd.GetEnvironmentId().String()). + WithError(err). + Trace("servent mutex unlocked") + return nil, err } - s.mu.Unlock() - log.Debug("servent mutex unlocked") - log.WithField("timeout", cmd.GetResponseTimeout()).Debug("blocking until response or timeout") + log.WithPrefix("servent"). + WithField("partition", cmd.GetEnvironmentId().String()). + WithField("timeout", cmd.GetResponseTimeout()). + Trace("blocking until response or timeout") // Neat, now we block until done||timeout select { case <-call.Done: // By the time we get here, ProcessResponse should have already added a Response to the // pending call, and removed it from servent.pending. case <-time.After(cmd.GetResponseTimeout()): - log.Debug("servent mutex locking") + userFriendlyCommandName, _ := strings.CutPrefix(cmd.GetName(), "MesosCommand_") + call.Error = fmt.Errorf("%s timed out", userFriendlyCommandName) + + log.WithPrefix("servent"). + WithField("partition", cmd.GetEnvironmentId().String()). + Trace("servent mutex locking") s.mu.Lock() - log.Debug("servent mutex locked") - call.Error = errors.New("MesosCommand timed out") + log.WithPrefix("servent"). + WithField("partition", cmd.GetEnvironmentId().String()). + Trace("servent mutex locked") + delete(s.pending, callId) + s.mu.Unlock() - log.Debug("servent mutex unlocked") + log.WithPrefix("servent"). + WithField("partition", cmd.GetEnvironmentId().String()). + Trace("servent mutex unlocked") } + if call.Error != nil { return nil, call.Error } @@ -126,26 +171,29 @@ func (s *Servent) RunCommand(cmd MesosCommand, receiver MesosCommandTarget) (Mes } func (s *Servent) ProcessResponse(res MesosCommandResponse, sender MesosCommandTarget) { - s.mu.Lock() callId := CallId{ - Id: res.GetCommandId(), + Id: res.GetCommandId(), Target: sender, } - call := s.pending[callId] + + s.mu.Lock() + call, ok := s.pending[callId] delete(s.pending, callId) s.mu.Unlock() - if call == nil { - log.WithFields(logrus.Fields{ - "commandName": res.GetCommandName(), - "commandId": res.GetCommandId(), - "agentId": sender.AgentId, - "executorId": sender.ExecutorId, - }). - Warning("no pending request found") + if !ok || call == nil { + log.WithPrefix("servent"). + WithField("partition", res.GetEnvironmentId().String()). + WithFields(logrus.Fields{ + "commandName": res.GetCommandName(), + "commandId": res.GetCommandId(), + "agentId": sender.AgentId, + "executorId": sender.ExecutorId, + }). + Warning("no pending request found") return } call.Response = res call.Done <- empty{} -} \ No newline at end of file +} diff --git a/core/controlcommands/multiresponse.go b/core/controlcommands/multiresponse.go index c7bc5a835..c6615f043 100644 --- a/core/controlcommands/multiresponse.go +++ b/core/controlcommands/multiresponse.go @@ -25,15 +25,15 @@ package controlcommands import ( - "strings" - "fmt" "errors" + "fmt" + "strings" ) type MesosCommandMultiResponse struct { MesosCommandResponseBase - responses map[MesosCommandTarget]MesosCommandResponse + responses map[MesosCommandTarget]MesosCommandResponse } func (m *MesosCommandMultiResponse) GetResponses() map[MesosCommandTarget]MesosCommandResponse { @@ -47,10 +47,24 @@ func (m *MesosCommandMultiResponse) IsMultiResponse() bool { return true } +func (m *MesosCommandMultiResponse) GetResponseSenders() []MesosCommandTarget { + if m != nil { + senders := make([]MesosCommandTarget, len(m.responses)) + i := 0 + for k := range m.responses { + senders[i] = k + i++ + } + return senders + } + return nil +} + func (m *MesosCommandMultiResponse) Err() error { if m == nil { - return nil + return errors.New("nil response") } + errs := make(map[MesosCommandTarget]error, 0) for k, v := range m.responses { if v.Err() != nil { @@ -61,13 +75,28 @@ func (m *MesosCommandMultiResponse) Err() error { return errors.New(strings.Join(func() (out []string) { for k, v := range errs { if v != nil && len(strings.TrimSpace(v.Error())) != 0 { - out = append(out, fmt.Sprintf("[%s/%s] %s", k.AgentId, k.ExecutorId, v.Error())) + out = append(out, fmt.Sprintf("[task %s] %s", k.TaskId.Value, v.Error())) } } return }(), "\n")) } +func (m *MesosCommandMultiResponse) Errors() map[MesosCommandTarget]error { + errMap := make(map[MesosCommandTarget]error) + + if m != nil { + for k, v := range m.responses { + if v.Err() != nil { + errMap[k] = v.Err() + } + } + return errMap + } + + return nil +} + func consolidateResponses(command MesosCommand, responses map[MesosCommandTarget]MesosCommandResponse) MesosCommandResponse { if len(responses) == 0 { return nil @@ -79,6 +108,6 @@ func consolidateResponses(command MesosCommand, responses map[MesosCommandTarget } return &MesosCommandMultiResponse{ MesosCommandResponseBase: *NewMesosCommandResponse(command, nil), - responses: responses, + responses: responses, } -} \ No newline at end of file +} diff --git a/core/core.go b/core/core.go index bd7a955ac..f2370f11b 100644 --- a/core/core.go +++ b/core/core.go @@ -1,7 +1,7 @@ /* * === This file is part of ALICE O² === * - * Copyright 2017-2018 CERN and copyright holders of ALICE O². + * Copyright 2017-2020 CERN and copyright holders of ALICE O². * Author: Teo Mrnjavac * * This program is free software: you can redistribute it and/or modify @@ -22,133 +22,139 @@ * Intergovernmental Organization or submit itself to any jurisdiction. */ +// Package core implements the main AliECS core functionality including +// the gRPC API server, environment management, and integration with various services. package core import ( "context" - "github.com/AliceO2Group/Control/common/product" - "github.com/spf13/viper" + "errors" + "fmt" + "net" + "net/http" + "regexp" + "strconv" + "syscall" "time" - "fmt" + "github.com/AliceO2Group/Control/common/event/topic" + "github.com/AliceO2Group/Control/common/golangmetrics" + "github.com/AliceO2Group/Control/common/logger/infologger" + "github.com/AliceO2Group/Control/common/monitoring" + pb "github.com/AliceO2Group/Control/common/protos" + "github.com/AliceO2Group/Control/core/the" + "github.com/AliceO2Group/Control/common/logger" - "github.com/looplab/fsm" - "github.com/mesos/mesos-go/api/v1/lib/extras/scheduler/callrules" - "github.com/mesos/mesos-go/api/v1/lib/extras/store" - "github.com/mesos/mesos-go/api/v1/lib/scheduler" + "github.com/AliceO2Group/Control/common/product" + "github.com/AliceO2Group/Control/core/integration" "github.com/sirupsen/logrus" - "net" + "github.com/spf13/viper" ) -var log = logger.New(logrus.StandardLogger(),"core") +var log = logger.New(logrus.StandardLogger(), "core") + +const ( + fileLimitWant = 65536 + fileLimitMin = 8192 +) + +func parseMetricsEndpoint(metricsEndpoint string) (error, uint16, string) { + pattern := `(^[0-9]{4,5})\/([a-zA-Z]+)` + re := regexp.MustCompile(pattern) + matches := re.FindStringSubmatch(metricsEndpoint) + + if matches != nil { + port, err := strconv.ParseUint(matches[1], 10, 16) + if err != nil { + return err, 0, "" + } + return nil, uint16(port), matches[2] + } else { + return errors.New("Failed to parse metrics endpoint: %s"), 0, "" + } +} + +func runMetrics() { + metricsEndpoint := viper.GetString("metricsEndpoint") + err, port, endpoint := parseMetricsEndpoint(metricsEndpoint) + if err != nil { + log.WithField("error", err).Error("Failed to parse metrics endpoint") + return + } + + go func() { + log.Infof("Starting to listen on endpoint %s:%d for metrics", endpoint, port) + if err := monitoring.Run(port, fmt.Sprintf("/%s", endpoint)); err != nil && err != http.ErrServerClosed { + golangmetrics.Stop() + log.Errorf("failed to run metrics on port %d and endpoint: %s") + } + }() + + monitoringTimeout := 30 * time.Second + if !monitoring.WaitUntilRunning(monitoringTimeout) { + log.WithField(infologger.Level, infologger.IL_Devel).Warnf("Failed to initialize monitoring framework in %v, it might catch up later. For now we are starting without metrics", monitoringTimeout) + } + + golangmetrics.Start(10 * time.Second) +} // Run is the entry point for this scheduler. // TODO: refactor Config to reflect our specific requirements func Run() error { - if viper.GetBool("verbose") || viper.GetBool("veryVerbose") { + if viper.GetBool("verbose") { logrus.SetLevel(logrus.DebugLevel) } + if viper.GetBool("veryVerbose") { + logrus.SetLevel(logrus.TraceLevel) + } if viper.GetBool("veryVerbose") { log.WithField("configuration", viper.AllSettings()).Debug("core starting up") } - log.Infof("%s (%s v%s build %s) starting up", product.PRETTY_FULLNAME, product.PRETTY_SHORTNAME, product.VERSION, product.BUILD) + log.WithField("level", infologger.IL_Support).Infof("%s core (%s v%s build %s) starting up", product.PRETTY_FULLNAME, product.PRETTY_SHORTNAME, product.VERSION, product.BUILD) // We create a context and use its cancel func as a shutdown func to release - // all resources. The shutdown func is stored in the app.internalState. + // all resources. The shutdown func is stored in the scheduler.internalState. ctx, cancel := context.WithCancel(context.Background()) // This only runs once to create a container for all data which comprises the // scheduler's state. // It also keeps count of the tasks launched/finished - state, err := newInternalState(cancel) + state, err := newGlobalState(cancel) if err != nil { return err } - // TODO(jdef) how to track/handle timeout errors that occur for SUBSCRIBE calls? we should - // probably tolerate X number of subsequent subscribe failures before bailing. we'll need - // to track the lastCallAttempted along with subsequentSubscribeTimeouts. - - // store.Singleton is a thread-safe abstraction to load and store and string, - // provided by mesos-go. - // We also make sure that a log message is printed when the FrameworkID changes. - fidStore := store.DecorateSingleton( - store.NewInMemorySingleton(), - store.DoSet().AndThen(func(_ store.Setter, v string, _ error) error { - log.WithField("frameworkId", v).Debug("generated new frameworkId") - return nil - })) - - // callrules.New returns a Rules and accept a bunch of Rule values as arguments. - // WithFrameworkID returns a Rule which injects a frameworkID to outgoing calls. - // logCalls returns a rule which prints to the log all calls of type SUBSCRIBE. - // callMetrics logs metrics for every outgoing call. - state.cli = callrules.New( - callrules.WithFrameworkID(store.GetIgnoreErrors(fidStore)), - logCalls(map[scheduler.Call_Type]string{scheduler.Call_SUBSCRIBE: "subscribe connecting"}), - callMetrics(state.metricsAPI, time.Now, viper.GetBool("summaryMetrics")), - ).Caller(state.cli) - - state.sm = fsm.NewFSM( - "INITIAL", - fsm.Events{ - {Name: "CONNECT", Src: []string{"INITIAL"}, Dst: "CONNECTED"}, - {Name: "NEW_ENVIRONMENT", Src: []string{"CONNECTED"}, Dst: "CONNECTED"}, - {Name: "GO_ERROR", Src: []string{"CONNECTED"}, Dst: "ERROR"}, - {Name: "RESET", Src: []string{"ERROR"}, Dst: "INITIAL"}, - {Name: "EXIT", Src: []string{"CONNECTED"}, Dst: "FINAL"}, - }, - fsm.Callbacks{ - "before_event": func(e *fsm.Event) { - log.WithFields(logrus.Fields{ - "event": e.Event, - "src": e.Src, - "dst": e.Dst, - }).Debug("state.sm starting transition") - }, - "enter_state": func(e *fsm.Event) { - log.WithFields(logrus.Fields{ - "event": e.Event, - "src": e.Src, - "dst": e.Dst, - }).Debug("state.sm entering state") - }, - "leave_CONNECTED": func(e *fsm.Event) { - log.Debug("leave_CONNECTED") - - }, - "before_NEW_ENVIRONMENT": func(e *fsm.Event) { - log.Debug("before_NEW_ENVIRONMENT") - e.Async() //transition frozen until the corresponding fsm.Transition call - }, - "enter_CONNECTED": func(e *fsm.Event) { - log.Debug("enter_CONNECTED") - log.Info("scheduler connected") - }, - "after_NEW_ENVIRONMENT": func(e *fsm.Event) { - log.Debug("after_NEW_ENVIRONMENT") - }, - }, - ) + // Set up channel to receive Unix Signals + signals(state) + + // Raise soft file limit + err = setLimits() + if err != nil { + return err + } + + // Start the Repo Manager instance + log.WithField("level", infologger.IL_Support).Infof("Starting the Control Workflows repo manager") + _ = the.RepoManager() // We now build the Control server - s := NewServer(state, fidStore) + s := NewServer(state) - // Async start of the scheduler controller. This runs in parallel with the grpc server. - go func() { - err = runSchedulerController(ctx, state, fidStore) - state.RLock() - defer state.RUnlock() - if state.err != nil { - err = state.err - log.WithField("error", err.Error()).Debug("scheduler quit with error, main state machine GO_ERROR") - state.sm.Event("GO_ERROR", err) //TODO: use error information in GO_ERROR - } else { - log.Debug("scheduler quit, no errors") - state.sm.Event("EXIT") - } - }() + state.taskman.Start(ctx) + + // First message to Kafka + the.EventWriterWithTopic(topic.Core).WriteEvent(&pb.Ev_MetaEvent_CoreStart{ + FrameworkId: state.taskman.GetFrameworkID(), + }) + + // Plugins need to start after taskman is running, because taskman provides the FID + integration.PluginsInstance().InitAll(state.taskman.GetFrameworkID()) + runMetrics() + defer golangmetrics.Stop() + defer monitoring.Stop() + + log.WithField("level", infologger.IL_Devel).Infof("Everything initiated and listening on control port: %d", viper.GetInt("controlPort")) lis, err := net.Listen("tcp", fmt.Sprintf(":%d", viper.GetInt("controlPort"))) if err != nil { @@ -156,11 +162,48 @@ func Run() error { WithField("port", viper.GetInt("controlPort")). Fatal("net.Listener failed to listen") } - if err := s.Serve(lis); err != nil { + if err = s.Serve(lis); err != nil { log.WithField("error", err).Fatal("GRPC server failed to serve") } + // Ensure all workflow plugins are destroyed before core teardown + integration.PluginsInstance().DestroyAll() + the.ClearEventWriters() + return err } +func setLimits() error { + var rLimit syscall.Rlimit + + err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rLimit) + if err != nil { + return err + } + if rLimit.Cur > fileLimitWant { + return nil + } + if rLimit.Max < fileLimitMin { + err = fmt.Errorf("need at least %v file descriptors", + fileLimitMin) + return err + } + if rLimit.Max < fileLimitWant { + rLimit.Cur = rLimit.Max + } else { + rLimit.Cur = fileLimitWant + } + err = syscall.Setrlimit(syscall.RLIMIT_NOFILE, &rLimit) + if err != nil { + // try min value + rLimit.Cur = fileLimitMin + err = syscall.Setrlimit(syscall.RLIMIT_NOFILE, &rLimit) + if err != nil { + return err + } + } + + return nil +} + // end Run diff --git a/core/environment/environment.go b/core/environment/environment.go index 2cb8e406a..176b80a2d 100644 --- a/core/environment/environment.go +++ b/core/environment/environment.go @@ -27,72 +27,1032 @@ package environment import ( + "context" + "encoding/json" "errors" + "fmt" + "sort" + "strconv" + "strings" "sync" "time" + "github.com/AliceO2Group/Control/common/event" + "github.com/AliceO2Group/Control/common/event/topic" + "github.com/AliceO2Group/Control/common/gera" "github.com/AliceO2Group/Control/common/logger" + "github.com/AliceO2Group/Control/common/logger/infologger" + "github.com/AliceO2Group/Control/common/monitoring" + pb "github.com/AliceO2Group/Control/common/protos" + "github.com/AliceO2Group/Control/common/runtype" + "github.com/AliceO2Group/Control/common/system" + "github.com/AliceO2Group/Control/common/utils" + "github.com/AliceO2Group/Control/common/utils/uid" + "github.com/AliceO2Group/Control/core/task" + "github.com/AliceO2Group/Control/core/task/sm" + "github.com/AliceO2Group/Control/core/the" "github.com/AliceO2Group/Control/core/workflow" + "github.com/AliceO2Group/Control/core/workflow/callable" "github.com/gobwas/glob" "github.com/looplab/fsm" "github.com/pborman/uuid" "github.com/sirupsen/logrus" ) -var log = logger.New(logrus.StandardLogger(),"env") - +var log = logger.New(logrus.StandardLogger(), "env") type Environment struct { Mu sync.RWMutex + once sync.Once + transitionMutex sync.RWMutex Sm *fsm.FSM name string - id uuid.UUID + id uid.ID ts time.Time workflow workflow.Role wfAdapter *workflow.ParentAdapter currentRunNumber uint32 + hookHandlerF func(hooks task.Tasks) error + incomingEvents chan event.DeviceEvent + + GlobalDefaults gera.Map[string, string] // From Consul + GlobalVars gera.Map[string, string] // From Consul + UserVars gera.Map[string, string] // From user input + BaseConfigStack map[string]string // Exclusively from Consul, already flattened for performance + + stateChangedCh chan *event.TasksStateChangedEvent + unsubscribe chan struct{} + eventStream Subscription + Public bool // From workflow or user + Description string // From workflow + WorkflowPath string // From workflow load + + callsPendingAwait map[string] /*await expression, trigger only*/ callable.CallsMap + currentTransition string + + autoStopTimer *time.Timer + autoStopCancelFcn context.CancelFunc } -func newEnvironment() (env *Environment, err error) { - envId := uuid.NewUUID() +func (env *Environment) NotifyEvent(e event.DeviceEvent) { + if e != nil && env.incomingEvents != nil { + select { + case env.incomingEvents <- e: + default: + } + } +} + +func newEnvironment(userVars map[string]string, newId uid.ID) (env *Environment, err error) { + envId := newId env = &Environment{ - id: envId, - workflow: nil, - ts: time.Now(), + id: envId, + workflow: nil, + ts: time.Now(), + incomingEvents: make(chan event.DeviceEvent), + // Every Environment instantiation performs a ConfSvc query for defaults and vars + // these key-values stay frozen throughout the lifetime of the environment + GlobalDefaults: gera.MakeMapWithMap(the.ConfSvc().GetDefaults()), + GlobalVars: gera.MakeMapWithMap(the.ConfSvc().GetVars()), + UserVars: gera.MakeMapWithMap(userVars), + stateChangedCh: make(chan *event.TasksStateChangedEvent), + + callsPendingAwait: make(map[string]callable.CallsMap), } - env.wfAdapter = workflow.NewParentAdapter(func() uuid.Array { return env.Id().Array() }) + + // Make the KVs accessible to the workflow via ParentAdapter + env.wfAdapter = workflow.NewParentAdapter( + func() uid.ID { return env.Id() }, + func() uint32 { return env.GetCurrentRunNumber() }, + func() gera.Map[string, string] { return env.GlobalDefaults }, + func() gera.Map[string, string] { return env.GlobalVars }, + func() gera.Map[string, string] { return env.UserVars }, + func(ev event.Event) { + env.Mu.Lock() + defer env.Mu.Unlock() + if env.eventStream != nil { + env.eventStream.Send(ev) + } + }, + ) + env.GlobalVars.Set("__fmq_cleanup_count", "0") // initialize to 0 the number of START transitions + + env.BaseConfigStack, err = gera.MakeMapWithMap(env.GlobalVars.Raw()). + WrappedAndFlattened(gera.MakeMapWithMap(env.GlobalDefaults.Raw())) // prepare the base config stack + if err != nil { + return nil, err + } + + // We start with STANDBY, which will not be preceded with enter_STANDBY, thus we set the value here. + enterStateTimeMs := strconv.FormatInt(time.Now().UnixMilli(), 10) + env.UserVars.Set("enter_state_time_ms", enterStateTimeMs) + env.Sm = fsm.NewFSM( "STANDBY", fsm.Events{ - {Name: "CONFIGURE", Src: []string{"STANDBY"}, Dst: "CONFIGURED"}, - {Name: "RESET", Src: []string{"CONFIGURED"}, Dst: "STANDBY"}, - {Name: "START_ACTIVITY", Src: []string{"CONFIGURED"}, Dst: "RUNNING"}, - {Name: "STOP_ACTIVITY", Src: []string{"RUNNING"}, Dst: "CONFIGURED"}, - {Name: "EXIT", Src: []string{"CONFIGURED", "STANDBY"}, Dst: "DONE"}, - {Name: "GO_ERROR", Src: []string{"CONFIGURED", "RUNNING"}, Dst: "ERROR"}, - {Name: "RECOVER", Src: []string{"ERROR"}, Dst: "STANDBY"}, + {Name: "DEPLOY", Src: []string{"STANDBY"}, Dst: "DEPLOYED"}, + {Name: "CONFIGURE", Src: []string{"DEPLOYED"}, Dst: "CONFIGURED"}, + {Name: "RESET", Src: []string{"CONFIGURED"}, Dst: "DEPLOYED"}, + {Name: "START_ACTIVITY", Src: []string{"CONFIGURED"}, Dst: "RUNNING"}, + {Name: "STOP_ACTIVITY", Src: []string{"RUNNING"}, Dst: "CONFIGURED"}, + {Name: "EXIT", Src: []string{"CONFIGURED", "DEPLOYED", "STANDBY"}, Dst: "DONE"}, + {Name: "GO_ERROR", Src: []string{"STANDBY", "CONFIGURED", "DEPLOYED", "RUNNING"}, Dst: "ERROR"}, + {Name: "RECOVER", Src: []string{"ERROR"}, Dst: "DEPLOYED"}, }, fsm.Callbacks{ - "enter_state": func(e *fsm.Event) { + "before_event": func(_ context.Context, e *fsm.Event) { + env.Mu.Lock() + env.currentTransition = e.Event + env.Mu.Unlock() + + trigger := fmt.Sprintf("before_%s", e.Event) + + the.EventWriterWithTopic(topic.Environment).WriteEvent(&pb.Ev_EnvironmentEvent{ + EnvironmentId: env.id.String(), + State: env.Sm.Current(), + RunNumber: env.GetCurrentRunNumber(), + Transition: e.Event, + TransitionStep: trigger, + TransitionStatus: pb.OpStatus_ONGOING, + Message: "transition step starting", + LastRequestUser: env.GetLastRequestUser(), + WorkflowTemplateInfo: env.GetWorkflowInfo(), + }) + + // first, we execute hooks which should be executed before an event officially starts + errHooks := env.handleHooksWithNegativeWeights(env.Workflow(), trigger) + if errHooks != nil { + e.Cancel(errHooks) + the.EventWriterWithTopic(topic.Environment).WriteEvent(&pb.Ev_EnvironmentEvent{ + EnvironmentId: env.id.String(), + State: env.Sm.Current(), + RunNumber: env.GetCurrentRunNumber(), + Error: errHooks.Error(), + Transition: e.Event, + TransitionStep: trigger, + TransitionStatus: pb.OpStatus_DONE_ERROR, + Message: "transition step finished", + WorkflowTemplateInfo: env.GetWorkflowInfo(), + }) + return + } + + // If the event is START_ACTIVITY, we set up and update variables relevant to plugins early on. + // This used to be done inside the transition_startactivity, but then the new RN isn't available to the + // before_START_ACTIVITY hooks. By setting it up here, we ensure the run number is available especially + // to plugin hooks. + if e.Event == "START_ACTIVITY" { + runNumber, rnErr := the.ConfSvc().NewRunNumber() + if rnErr != nil { + e.Cancel(rnErr) + return + } + env.currentRunNumber = runNumber + rnString := strconv.FormatUint(uint64(runNumber), 10) + env.workflow.GetVars().Set("run_number", rnString) + env.workflow.GetVars().Set("runNumber", rnString) + + runStartTime := time.Now() + runStartTimeStr := strconv.FormatInt(runStartTime.UnixMilli(), 10) + env.workflow.SetRuntimeVar("run_start_time_ms", runStartTimeStr) + env.workflow.SetRuntimeVar("run_start_completion_time_ms", "") // we delete previous EOSOR + env.workflow.SetRuntimeVar("run_end_time_ms", "") // we delete previous SOEOR + env.workflow.SetRuntimeVar("run_end_completion_time_ms", "") // we delete previous EOEOR + + the.EventWriterWithTopic(topic.Run).WriteEventWithTimestamp(&pb.Ev_RunEvent{ + EnvironmentId: envId.String(), + RunNumber: runNumber, + State: env.Sm.Current(), + Error: "", + Transition: e.Event, + TransitionStatus: pb.OpStatus_STARTED, + LastRequestUser: env.GetLastRequestUser(), + }, runStartTime) + + cleanupCount := 0 + cleanupCountS, ok := env.GlobalVars.Get("__fmq_cleanup_count") + if ok && len(cleanupCountS) > 0 { + var parseErr error + cleanupCount, parseErr = strconv.Atoi(cleanupCountS) + if parseErr != nil { + cleanupCount = 1 // something was there, even though non-parsable, so we signal to clean up + } + } + env.GlobalVars.Set("__fmq_cleanup_count", strconv.Itoa(cleanupCount)) // number of times the START transition has run for this env + + lhcPeriod, ok := env.BaseConfigStack["lhc_period"] + if ok { + env.workflow.GetVars().Set("lhc_period", lhcPeriod) + } + nHbfPerTf, ok := env.BaseConfigStack["pdp_n_hbf_per_tf"] + if ok { + env.workflow.GetVars().Set("pdp_n_hbf_per_tf", nHbfPerTf) + } + } else if e.Event == "STOP_ACTIVITY" { + endTime, ok := env.workflow.GetUserVars().Get("run_end_time_ms") + if ok && endTime == "" { + runEndTime := time.Now() + runEndTimeStr := strconv.FormatInt(runEndTime.UnixMilli(), 10) + env.workflow.SetRuntimeVar("run_end_time_ms", runEndTimeStr) + + the.EventWriterWithTopic(topic.Run).WriteEventWithTimestamp(&pb.Ev_RunEvent{ + EnvironmentId: envId.String(), + RunNumber: env.GetCurrentRunNumber(), + State: env.Sm.Current(), + Error: "", + Transition: e.Event, + TransitionStatus: pb.OpStatus_STARTED, + LastRequestUser: env.GetLastRequestUser(), + }, runEndTime) + + } else { + log.WithField("partition", envId.String()). + Debug("O2 End time already set before before_STOP_ACTIVITY") + } + } else if e.Event == "GO_ERROR" { + endTime, ok := env.workflow.GetUserVars().Get("run_end_time_ms") + if ok && endTime == "" { + runEndTime := time.Now() + runEndTimeStr := strconv.FormatInt(runEndTime.UnixMilli(), 10) + env.workflow.SetRuntimeVar("run_end_time_ms", runEndTimeStr) + + the.EventWriterWithTopic(topic.Run).WriteEventWithTimestamp(&pb.Ev_RunEvent{ + EnvironmentId: envId.String(), + RunNumber: env.GetCurrentRunNumber(), + State: env.Sm.Current(), + Error: "", + Transition: e.Event, + TransitionStatus: pb.OpStatus_STARTED, + LastRequestUser: env.GetLastRequestUser(), + }, runEndTime) + + } else { + log.WithField("partition", envId.String()). + Debug("O2 End time already set before before_GO_ERROR") + } + } + + if rn := env.GetCurrentRunNumber(); rn != 0 { + log.WithField("partition", envId). + WithField("run", rn). + Infof("%s transition starting", + e.Event, + ) + } else { + log.WithField("partition", envId). + Infof("%s transition starting", + e.Event, + ) + } + + errHooks = env.handleHooksWithPositiveWeights(env.Workflow(), trigger) + if errHooks != nil { + e.Cancel(errHooks) + } + + errorMsg := "" + if e.Err != nil { + errorMsg = e.Err.Error() + } + + the.EventWriterWithTopic(topic.Environment).WriteEvent(&pb.Ev_EnvironmentEvent{ + EnvironmentId: env.id.String(), + State: env.Sm.Current(), + RunNumber: env.currentRunNumber, + Error: errorMsg, + Message: "transition step finished", + Transition: e.Event, + TransitionStep: trigger, + TransitionStatus: pb.OpStatus_ONGOING, + LastRequestUser: env.GetLastRequestUser(), + WorkflowTemplateInfo: env.GetWorkflowInfo(), + }) + }, + "leave_state": func(_ context.Context, e *fsm.Event) { + trigger := fmt.Sprintf("leave_%s", e.Src) + + the.EventWriterWithTopic(topic.Environment).WriteEvent(&pb.Ev_EnvironmentEvent{ + EnvironmentId: env.id.String(), + State: env.Sm.Current(), + RunNumber: env.currentRunNumber, + Transition: e.Event, + TransitionStep: trigger, + Message: "transition step starting", + TransitionStatus: pb.OpStatus_ONGOING, + LastRequestUser: env.GetLastRequestUser(), + WorkflowTemplateInfo: env.GetWorkflowInfo(), + }) + + errHooks := env.handleHooksWithNegativeWeights(env.Workflow(), trigger) + // fixme: in principle we should not need it anymore, since both STOP_ACTIVITY and GO_ERROR set EOR + // We might leave RUNNING not only through STOP_ACTIVITY. In such cases we also need a run stop time. + if e.Src == "RUNNING" { + endTime, ok := env.workflow.GetUserVars().Get("run_end_time_ms") + if ok && endTime == "" { + runEndTime := strconv.FormatInt(time.Now().UnixMilli(), 10) + env.workflow.SetRuntimeVar("run_end_time_ms", runEndTime) + } else { + log.WithField("partition", envId.String()). + Debug("O2 End time already set before leave_RUNNING") + } + } + if errHooks != nil { + e.Cancel(errHooks) + the.EventWriterWithTopic(topic.Environment).WriteEvent(&pb.Ev_EnvironmentEvent{ + EnvironmentId: env.id.String(), + State: env.Sm.Current(), + RunNumber: env.GetCurrentRunNumber(), + Error: errHooks.Error(), + Transition: e.Event, + TransitionStep: trigger, + TransitionStatus: pb.OpStatus_DONE_ERROR, + Message: "transition step finished", + WorkflowTemplateInfo: env.GetWorkflowInfo(), + }) + return + } + + errHooks = env.handleHooksWithPositiveWeights(env.Workflow(), trigger) + if errHooks != nil { + e.Cancel(errHooks) + } + + errorMsg := "" + if e.Err != nil { + errorMsg = e.Err.Error() + } + + the.EventWriterWithTopic(topic.Environment).WriteEvent(&pb.Ev_EnvironmentEvent{ + EnvironmentId: env.id.String(), + State: env.Sm.Current(), + RunNumber: env.currentRunNumber, + Error: errorMsg, + Message: "transition step finished", + Transition: e.Event, + TransitionStep: trigger, + TransitionStatus: pb.OpStatus_DONE_ERROR, + LastRequestUser: env.GetLastRequestUser(), + WorkflowTemplateInfo: env.GetWorkflowInfo(), + }) + + if e.Err != nil { + return + } + + the.EventWriterWithTopic(topic.Environment).WriteEvent(&pb.Ev_EnvironmentEvent{ + EnvironmentId: env.id.String(), + State: env.Sm.Current(), + RunNumber: env.currentRunNumber, + Message: "transition step starting", + Transition: e.Event, + TransitionStep: fmt.Sprintf("tasks_%s", e.Event), + TransitionStatus: pb.OpStatus_ONGOING, + LastRequestUser: env.GetLastRequestUser(), + WorkflowTemplateInfo: env.GetWorkflowInfo(), + }) + + env.handlerFunc()(e) + + eventState := e.Dst // we set the destination state here instead of the current for the event write, if the tasks have transitioned + transitionStatus := pb.OpStatus_ONGOING + if e.Err != nil { + errorMsg = e.Err.Error() + eventState = e.Src + transitionStatus = pb.OpStatus_DONE_ERROR + } + + the.EventWriterWithTopic(topic.Environment).WriteEvent(&pb.Ev_EnvironmentEvent{ + EnvironmentId: env.id.String(), + State: eventState, + RunNumber: env.currentRunNumber, + Error: errorMsg, + Message: "transition step finished", + Transition: e.Event, + TransitionStep: fmt.Sprintf("tasks_%s", e.Event), + TransitionStatus: transitionStatus, + LastRequestUser: env.GetLastRequestUser(), + WorkflowTemplateInfo: env.GetWorkflowInfo(), + }) + }, + "enter_state": func(_ context.Context, e *fsm.Event) { + trigger := fmt.Sprintf("enter_%s", e.Dst) + + the.EventWriterWithTopic(topic.Environment).WriteEvent(&pb.Ev_EnvironmentEvent{ + EnvironmentId: env.id.String(), + State: env.Sm.Current(), + RunNumber: env.currentRunNumber, + Transition: e.Event, + TransitionStep: trigger, + TransitionStatus: pb.OpStatus_ONGOING, + Message: "transition step starting", + LastRequestUser: env.GetLastRequestUser(), + WorkflowTemplateInfo: env.GetWorkflowInfo(), + }) + + errHooks := env.handleHooksWithNegativeWeights(env.Workflow(), trigger) + + enterStateTimeMs = strconv.FormatInt(time.Now().UnixMilli(), 10) + env.workflow.SetRuntimeVar("enter_state_time_ms", enterStateTimeMs) + + errHooks = errors.Join(errHooks, env.handleHooksWithPositiveWeights(env.Workflow(), trigger)) + if errHooks != nil { + // at enter_ it will not cancel the transition but only set the error + e.Cancel(errHooks) + } + + errorMsg := "" + transitionStatus := pb.OpStatus_ONGOING + if e.Err != nil { + errorMsg = e.Err.Error() + transitionStatus = pb.OpStatus_DONE_ERROR + } + + the.EventWriterWithTopic(topic.Environment).WriteEvent(&pb.Ev_EnvironmentEvent{ + EnvironmentId: env.id.String(), + State: env.Sm.Current(), + RunNumber: env.currentRunNumber, + Error: errorMsg, + Message: "transition step finished", + Transition: e.Event, + TransitionStep: trigger, + TransitionStatus: transitionStatus, + LastRequestUser: env.GetLastRequestUser(), + WorkflowTemplateInfo: env.GetWorkflowInfo(), + }) + + if e.Err != nil { + return + } + log.WithFields(logrus.Fields{ - "event": e.Event, - "src": e.Src, - "dst": e.Dst, - "environmentId": envId, + "event": e.Event, + "src": e.Src, + "dst": e.Dst, + "partition": envId, }).Debug("environment.sm entering state") }, - "before_event": env.handlerFunc(), + "after_event": func(_ context.Context, e *fsm.Event) { + defer func() { + env.Mu.Lock() + env.currentTransition = "" + env.Mu.Unlock() + }() + + trigger := fmt.Sprintf("after_%s", e.Event) + + the.EventWriterWithTopic(topic.Environment).WriteEvent(&pb.Ev_EnvironmentEvent{ + EnvironmentId: env.id.String(), + State: env.Sm.Current(), + RunNumber: env.currentRunNumber, + Transition: e.Event, + TransitionStep: trigger, + TransitionStatus: pb.OpStatus_ONGOING, + Message: "transition step starting", + LastRequestUser: env.GetLastRequestUser(), + WorkflowTemplateInfo: env.GetWorkflowInfo(), + }) + + errHooks := env.handleHooksWithNegativeWeights(env.Workflow(), trigger) + if errHooks != nil { + // at after_ it will not cancel the transition but only set the error + e.Cancel(errHooks) + } + + if rn := env.GetCurrentRunNumber(); rn != 0 { + log.WithField("partition", envId). + WithField("run", rn). + Infof("%s transition complete", + e.Event, + ) + } else { + log.WithField("partition", envId). + Infof("%s transition complete", + e.Event, + ) + } + + if e.Event == "START_ACTIVITY" { + // If START_ACTIVITY is complete, we increment the FairMQ cleanup counter + cleanupCount := 0 + cleanupCountS, ok := env.GlobalVars.Get("__fmq_cleanup_count") + if ok && len(cleanupCountS) > 0 { + cleanupCount, _ = strconv.Atoi(cleanupCountS) + } + cleanupCount++ + env.GlobalVars.Set("__fmq_cleanup_count", strconv.Itoa(cleanupCount)) + + // Register auto stop transition (if enabled) + scheduled, expected := env.scheduleAutoStopTransition() + if scheduled { + log.WithField("partition", env.id). + WithField("run", env.currentRunNumber). + Infof("auto stop transition scheduled, expected execution at %s", expected) + } + + runStartCompletionTime := time.Now() + runStartCompletionTimeStr := strconv.FormatInt(runStartCompletionTime.UnixMilli(), 10) + env.workflow.SetRuntimeVar("run_start_completion_time_ms", runStartCompletionTimeStr) + + runEvent := &pb.Ev_RunEvent{ + EnvironmentId: envId.String(), + RunNumber: env.GetCurrentRunNumber(), + State: env.Sm.Current(), + Error: "", + Transition: e.Event, + TransitionStatus: pb.OpStatus_DONE_OK, + LastRequestUser: env.GetLastRequestUser(), + } + if e.Err != nil { + runEvent.Error = e.Err.Error() + runEvent.TransitionStatus = pb.OpStatus_DONE_ERROR + } + + the.EventWriterWithTopic(topic.Run).WriteEventWithTimestamp(runEvent, runStartCompletionTime) + + } else if e.Event == "STOP_ACTIVITY" { + runEndCompletionTime := time.Now() + runEndCompletionTimeStr := strconv.FormatInt(runEndCompletionTime.UnixMilli(), 10) + env.workflow.SetRuntimeVar("run_end_completion_time_ms", runEndCompletionTimeStr) + + runEvent := &pb.Ev_RunEvent{ + EnvironmentId: envId.String(), + RunNumber: env.GetCurrentRunNumber(), + State: env.Sm.Current(), + Error: "", + Transition: e.Event, + TransitionStatus: pb.OpStatus_DONE_OK, + LastRequestUser: env.GetLastRequestUser(), + } + if e.Err != nil { + runEvent.Error = e.Err.Error() + runEvent.TransitionStatus = pb.OpStatus_DONE_ERROR + } + + the.EventWriterWithTopic(topic.Run).WriteEventWithTimestamp(runEvent, runEndCompletionTime) + + // Ensure the auto stop timer is stopped (important for stop transitions NOT triggered by the timer itself) + env.invalidateAutoStopTransition() + } else if e.Event == "GO_ERROR" { + endCompletionTime, ok := env.workflow.GetUserVars().Get("run_end_completion_time_ms") + if ok && endCompletionTime == "" { + runEndCompletionTime := time.Now() + runEndCompletionTimeStr := strconv.FormatInt(runEndCompletionTime.UnixMilli(), 10) + env.workflow.SetRuntimeVar("run_end_completion_time_ms", runEndCompletionTimeStr) + + the.EventWriterWithTopic(topic.Run).WriteEventWithTimestamp(&pb.Ev_RunEvent{ + EnvironmentId: envId.String(), + RunNumber: env.GetCurrentRunNumber(), + State: env.Sm.Current(), + Error: "", + Transition: e.Event, + TransitionStatus: pb.OpStatus_DONE_OK, + LastRequestUser: env.GetLastRequestUser(), + }, runEndCompletionTime) + + } else { + log.WithField("partition", envId.String()). + Debug("O2 End Completion time already set before after_GO_ERROR") + } + env.invalidateAutoStopTransition() + } + + errHooks = errors.Join(errHooks, env.handleHooksWithPositiveWeights(env.Workflow(), trigger)) + if errHooks != nil { + e.Cancel(errHooks) + } + + errorMsg := "" + transitionStatus := pb.OpStatus_ONGOING + if e.Err != nil { + errorMsg = e.Err.Error() + transitionStatus = pb.OpStatus_DONE_ERROR + } + + // publish transition step complete event + the.EventWriterWithTopic(topic.Environment).WriteEvent(&pb.Ev_EnvironmentEvent{ + EnvironmentId: env.id.String(), + State: env.Sm.Current(), + RunNumber: env.currentRunNumber, + Error: errorMsg, + Message: "transition step finished", + Transition: e.Event, + TransitionStep: trigger, + TransitionStatus: transitionStatus, + LastRequestUser: env.GetLastRequestUser(), + WorkflowTemplateInfo: env.GetWorkflowInfo(), + }) + + if e.Event == "STOP_ACTIVITY" || e.Event == "GO_ERROR" { + // If the event is STOP_ACTIVITY or GO_ERROR, we remove the active run number after all hooks are done. + if env.currentRunNumber != 0 { + env.workflow.GetVars().Set("last_run_number", strconv.Itoa(int(env.currentRunNumber))) + } + env.currentRunNumber = 0 + env.workflow.GetVars().Del("run_number") + env.workflow.GetVars().Del("runNumber") + } + }, }, ) return } +func (env *Environment) handleHooks(workflow workflow.Role, trigger string, weightPredicate func(callable.HookWeight) bool) (err error) { + // Starting point: get all hooks to be started for the current trigger + hooksMapForTrigger := workflow.GetHooksMapForTrigger(trigger) + callsMapForAwait := env.callsPendingAwait[trigger] + + metric := monitoring.NewMetric("hooks") + metric.AddTag("trigger", trigger) + metric.AddTag("envId", env.id.String()) + metric.AddTag("runtype", env.GetRunType().String()) + defer monitoring.TimerSendSingle(&metric, monitoring.Millisecond)() + + allWeightsSet := make(callable.HooksMap) + for k := range hooksMapForTrigger { + allWeightsSet[k] = callable.Hooks{} + } + for k := range callsMapForAwait { + allWeightsSet[k] = callable.Hooks{} + } + allWeights := allWeightsSet.GetWeights() + + filteredWeights := make([]callable.HookWeight, 0) + for _, weight := range allWeights { + if weightPredicate(weight) { + filteredWeights = append(filteredWeights, weight) + } + } + + // Prepare structures to accumulate errors + allErrors := make(map[callable.Hook]error) + criticalFailures := make([]error, 0) + + // FOR EACH weight within the current state machine trigger moment + // 4 phases: start calls, await calls, execute task hooks, error handling + for _, weight := range filteredWeights { + hooksForWeight, thereAreHooksToStartForTheCurrentTriggerAndWeight := hooksMapForTrigger[weight] + + // PHASE 1: start asynchronously any call hooks and add them to the pending await map + + if thereAreHooksToStartForTheCurrentTriggerAndWeight { + // Hooks can be call hooks or task hooks, we do the calls first + callsToStart := hooksForWeight.FilterCalls() + if len(callsToStart) != 0 { + // Before we run anything asynchronously we must associate each call we're about + // to start with its corresponding await expression + for _, call := range callsToStart { + awaitExpr := call.GetTraits().Await + + awaitName, awaitWeight := callable.ParseTriggerExpression(awaitExpr) + + // If the callsPendingAwait map has no pending calls list for the given await expression + // (await name + await weight), we make sure the per-name map and per-weight slice are + // created before we add any pending awaits. + if _, ok := env.callsPendingAwait[awaitName]; !ok || len(env.callsPendingAwait[awaitName]) == 0 { + env.callsPendingAwait[awaitName] = make(callable.CallsMap) + } + if _, ok := env.callsPendingAwait[awaitName][awaitWeight]; !ok || len(env.callsPendingAwait[awaitName][awaitWeight]) == 0 { + env.callsPendingAwait[awaitName][awaitWeight] = make(callable.Calls, 0) + } + env.callsPendingAwait[awaitName][awaitWeight] = append( + env.callsPendingAwait[awaitName][awaitWeight], call) + } + callsToStart.StartAll() // returns immediately (async) + } + } + + // PHASE 2: collect any calls awaiting termination + + // We take care of any pending hooks whose await expression corresponds to the current trigger, + // including any calls that have just been started (for which trigger == call.Trigger == call.Await). + callErrors := make(map[*callable.Call]error) + if _, ok := env.callsPendingAwait[trigger]; ok { + pendingCalls, ok := env.callsPendingAwait[trigger][weight] + if ok && len(pendingCalls) != 0 { // meaning there are hook calls to take care of + // AwaitAll blocks with no global timeout - it is up to the specific called function to implement + // a timeout internally. + // The Call instance pushes to the call's varStack some special values including the timeout + // (provided by the workflow template). At that point the integration plugin must acquire the + // timeout value and use the Context mechanism or some other approach to ensure the timeouts are + // respected. + + callErrors = pendingCalls.AwaitAll() + delete(env.callsPendingAwait[trigger], weight) + } + } + + // PHASE 3: start and finish any task hooks (synchronous!) + + taskErrors := make(map[*task.Task]error) + if thereAreHooksToStartForTheCurrentTriggerAndWeight { + + // Tasks are handled separately for now, and they must have trigger==await + hookTasksToTrigger := hooksForWeight.FilterTasks() + taskErrors = env.runTasksAsHooks(hookTasksToTrigger) // blocking call, timeouts in executor + } + + // PHASE 4: collect any errors + + thereAreCriticalErrors := false + // We merge hook call errors and hook task errors into a single map for + // critical trait processing + for hook, err := range callErrors { + allErrors[hook] = err + if hook.GetTraits().Critical { + thereAreCriticalErrors = true + } + } + for hook, err := range taskErrors { + allErrors[hook] = err + if hook.GetTraits().Critical { + thereAreCriticalErrors = true + } + } + + if thereAreCriticalErrors { + break + // if at least one critical error occurred, we stop processing hooks for the current trigger beyond the + // current weight step + } + } + + for hook, err := range allErrors { + if hook == nil || err == nil { + continue + } + + // If the hook call or task is critical: true + if hook.GetTraits().Critical { + log.WithField("partition", env.Id().String()). + Logf(logrus.FatalLevel, "critical hook failed at trigger %s: %s", trigger, err) // Must use Logf(FatalLevel) instead of + // Fatalf because the latter calls Exit + criticalFailures = append(criticalFailures, err) + } else { + log.WithField("level", infologger.IL_Support). + WithField("partition", env.Id().String()). + Warnf("non-critical hook failed at trigger %s: %s", trigger, err) + } + } + + if len(criticalFailures) > 3 { + return fmt.Errorf("%d critical hooks failed at trigger %s (see InfoLogger for details)", len(criticalFailures), trigger) + } else if len(criticalFailures) > 0 { // 1-3 failed hooks + consolidated := make([]string, len(criticalFailures)) + for i, cf := range criticalFailures { + consolidated[i] = cf.Error() + } + consolidatedS := strings.Join(consolidated, "; ") + + return errors.New(consolidatedS) + } else { + return nil + } +} + +func (env *Environment) handleAllHooks(workflow workflow.Role, trigger string) (err error) { + log.WithField("partition", env.id).Debugf("begin handling hooks for trigger %s", trigger) + defer utils.TimeTrack(time.Now(), fmt.Sprintf("finished handling hooks for trigger %s", trigger), log.WithPrefix("env").WithField("partition", env.id)) + return env.handleHooks(workflow, trigger, func(w callable.HookWeight) bool { return true }) +} + +func (env *Environment) handleHooksWithNegativeWeights(workflow workflow.Role, trigger string) (err error) { + log.WithField("partition", env.id).Debugf("begin handling hooks with negative weights for trigger %s", trigger) + defer utils.TimeTrack(time.Now(), fmt.Sprintf("finished handling hooks with negative weights for trigger %s", trigger), log.WithPrefix("env").WithField("partition", env.id)) + return env.handleHooks(workflow, trigger, func(w callable.HookWeight) bool { return w < 0 }) +} + +// "positive" include 0 +func (env *Environment) handleHooksWithPositiveWeights(workflow workflow.Role, trigger string) (err error) { + log.WithField("partition", env.id).Debugf("begin handling hooks with positive weights for trigger %s", trigger) + defer utils.TimeTrack(time.Now(), fmt.Sprintf("finished handling hooks with positive weights for trigger %s", trigger), log.WithPrefix("env").WithField("partition", env.id)) + return env.handleHooks(workflow, trigger, func(w callable.HookWeight) bool { return w >= 0 }) +} + +// runTasksAsHooks returns a map of failed hook tasks and their respective error values. +// The returned map includes both critical and non-critical failures, and it's up to the caller +// to further filter as needed. +func (env *Environment) runTasksAsHooks(hooksToTrigger task.Tasks) (errorMap map[*task.Task]error) { + errorMap = make(map[*task.Task]error) + + if len(hooksToTrigger) == 0 { + return + } + + timeoutCh := make(chan string) + hookTimers := make(map[string]*time.Timer) + + for _, hook := range hooksToTrigger { + timeout, _ := time.ParseDuration(hook.GetTraits().Timeout) + log.WithField("partition", env.Id().String()). + WithField("task", hook.GetName()). + WithField("taskId", hook.GetTaskId()). + WithField("command", hook.GetTaskCommandInfo().GetValue()). + WithField("args", hook.GetTaskCommandInfo().GetArguments()). + WithField("failedHost", hook.GetHostname()). + WithField("timeout", timeout.Seconds()). + Trace("setting timer for hook before triggering") + + tid := hook.GetTaskId() + hookTimers[tid] = time.AfterFunc(timeout, + func() { + timeoutCh <- tid + }) + } + + doneCh := make(chan struct{}) + + go func() { + successfulHooks := make(task.Tasks, 0) + + for { + select { + case tid := <-timeoutCh: + log.WithField("taskId", tid).Debug("incoming hook timeout") + thisHook := hooksToTrigger.GetByTaskId(tid) + if thisHook != nil { + if _, hasTimer := hookTimers[tid]; !hasTimer { + log.WithField("partition", env.Id().String()). + WithField("task", thisHook.GetName()). + WithField("taskId", thisHook.GetTaskId()). + WithField("command", thisHook.GetTaskCommandInfo().GetValue()). + WithField("args", thisHook.GetTaskCommandInfo().GetArguments()). + WithField("failedHost", thisHook.GetHostname()). + WithField("level", infologger.IL_Devel). + Warn("timeout for hook but no timer in timers map") + } else { + log.WithField("partition", env.Id().String()). + WithField("task", thisHook.GetName()). + WithField("taskId", thisHook.GetTaskId()). + WithField("command", thisHook.GetTaskCommandInfo().GetValue()). + WithField("args", thisHook.GetTaskCommandInfo().GetArguments()). + WithField("failedHost", thisHook.GetHostname()). + WithField("level", infologger.IL_Devel). + Warn("hook response timed out") + delete(hookTimers, tid) + errorMap[thisHook] = fmt.Errorf("hook task %s timed out after %s", + thisHook.GetName(), thisHook.GetTraits().Timeout) + } + } + + case e := <-env.incomingEvents: + if evt, ok := e.(*event.BasicTaskTerminated); ok { + tid := evt.GetOrigin().TaskId.Value + thisHook := hooksToTrigger.GetByTaskId(tid) + if thisHook == nil { + continue + } + + hookTimers[tid].Stop() + delete(hookTimers, tid) + + if evt.ExitCode != 0 { + errorMap[thisHook] = fmt.Errorf("hook task %s finished with non-zero exit code %d (status %s)", + thisHook.GetName(), evt.ExitCode, evt.FinalMesosState) + + log.WithField("task", thisHook.GetName()). + WithFields(logrus.Fields{ + "exitCode": evt.ExitCode, + "stdout": evt.Stdout, + "stderr": evt.Stderr, + "partition": env.Id().String(), + "finalMesosState": evt.FinalMesosState.String(), + }). + Warn("hook failed") + } else if !evt.VoluntaryTermination { + errorMap[thisHook] = fmt.Errorf("hook task %s involuntary termination with exit code %d (status %s)", + thisHook.GetName(), evt.ExitCode, evt.FinalMesosState) + + log.WithField("task", thisHook.GetName()). + WithFields(logrus.Fields{ + "exitCode": evt.ExitCode, + "stdout": evt.Stdout, + "stderr": evt.Stderr, + "partition": env.Id().String(), + "finalMesosState": evt.FinalMesosState.String(), + }). + Warn("hook failed") + } else { + successfulHooks = append(successfulHooks, thisHook) + log.WithField("partition", env.Id().String()). + WithField("taskId", tid). + WithField("task", thisHook.GetName()). + Debug("hook completed") + } + } + } + + if len(hookTimers) == 0 { + break + } else { + keys := make([]string, 0) + for k := range hookTimers { + keys = append(keys, k) + } + log.WithField("taskIds", strings.Join(keys, ",")). + WithField("successfulHooks", len(successfulHooks)). + WithField("level", infologger.IL_Devel). + WithField("partition", env.Id().String()). + Debugf("hook timeout timers still left: %d, next cycle", len(hookTimers)) + } + } + + log.WithField("level", infologger.IL_Devel). + WithField("partition", env.Id().String()). + Debugf("hooks to trigger: %d, successful: %d", len(hooksToTrigger), len(successfulHooks)) + + if len(hooksToTrigger) == len(successfulHooks) { + errorMap = make(map[*task.Task]error) + } + doneCh <- struct{}{} + }() + + err := env.hookHandlerF(hooksToTrigger) + if err != nil { + for _, h := range hooksToTrigger { + errorMap[h] = err + timer, ok := hookTimers[h.GetTaskId()] + if ok { + timer.Stop() + delete(hookTimers, h.GetTaskId()) + } + } + return + } + + <-doneCh + + return +} + func (env *Environment) TryTransition(t Transition) (err error) { + if !env.transitionMutex.TryLock() { + log.WithField("partition", env.id.String()). + Warnf("environment transition '%s' attempt delayed: transition '%s' in progress. waiting for completion or failure", t.eventName(), env.currentTransition) + env.transitionMutex.Lock() + log.WithField("level", infologger.IL_Support). + WithField("partition", env.id.String()). + Infof("environment transition '%s' attempt resumed", t.eventName()) + } + defer env.transitionMutex.Unlock() + + the.EventWriterWithTopic(topic.Environment).WriteEvent(&pb.Ev_EnvironmentEvent{ + EnvironmentId: env.id.String(), + State: env.Sm.Current(), + RunNumber: env.currentRunNumber, + Message: "transition starting", + Transition: t.eventName(), + TransitionStatus: pb.OpStatus_STARTED, + LastRequestUser: env.GetLastRequestUser(), + WorkflowTemplateInfo: env.GetWorkflowInfo(), + }) + + metric := transitionMetric(env, t.eventName(), "try") + defer monitoring.TimerSendSingle(&metric, monitoring.Millisecond)() + err = t.check() if err != nil { + the.EventWriterWithTopic(topic.Environment).WriteEvent(&pb.Ev_EnvironmentEvent{ + EnvironmentId: env.id.String(), + State: env.Sm.Current(), + RunNumber: env.currentRunNumber, + Error: err.Error(), + Message: "transition impossible", + Transition: t.eventName(), + TransitionStatus: pb.OpStatus_DONE_ERROR, + LastRequestUser: env.GetLastRequestUser(), + WorkflowTemplateInfo: env.GetWorkflowInfo(), + }) + metric.AddError(err) return } - err = env.Sm.Event(t.eventName(), t) + err = env.Sm.Event(context.Background(), t.eventName(), t) + + if err != nil { + the.EventWriterWithTopic(topic.Environment).WriteEvent(&pb.Ev_EnvironmentEvent{ + EnvironmentId: env.id.String(), + State: env.Sm.Current(), + RunNumber: env.currentRunNumber, + Error: err.Error(), + Message: "transition error", + Transition: t.eventName(), + TransitionStatus: pb.OpStatus_DONE_ERROR, + LastRequestUser: env.GetLastRequestUser(), + WorkflowTemplateInfo: env.GetWorkflowInfo(), + }) + } else { + the.EventWriterWithTopic(topic.Environment).WriteEvent(&pb.Ev_EnvironmentEvent{ + EnvironmentId: env.id.String(), + State: env.Sm.Current(), + RunNumber: env.currentRunNumber, + Message: "transition completed successfully", + Transition: t.eventName(), + TransitionStatus: pb.OpStatus_DONE_OK, + LastRequestUser: env.GetLastRequestUser(), + WorkflowTemplateInfo: env.GetWorkflowInfo(), + }) + } + metric.AddError(err) return } @@ -101,13 +1061,20 @@ func (env *Environment) handlerFunc() func(e *fsm.Event) { return nil } return func(e *fsm.Event) { + if e.Err != nil { // If the event was already cancelled + return + } log.WithFields(logrus.Fields{ - "event": e.Event, - "src": e.Src, - "dst": e.Dst, - "environmentId": env.id.String(), + "event": e.Event, + "src": e.Src, + "dst": e.Dst, + "partition": env.id.String(), }).Debug("environment.sm starting transition") + if len(e.Args) == 0 { + e.Cancel(errors.New("transition missing in FSM event")) + return + } transition, ok := e.Args[0].(Transition) if !ok { e.Cancel(errors.New("transition wrapping error")) @@ -123,12 +1090,11 @@ func (env *Environment) handlerFunc() func(e *fsm.Event) { } } - // Accessors -func (env *Environment) Id() uuid.UUID { +func (env *Environment) Id() uid.ID { if env == nil { - return uuid.NIL + return uid.NilID() } env.Mu.RLock() defer env.Mu.RUnlock() @@ -137,7 +1103,7 @@ func (env *Environment) Id() uuid.UUID { func (env *Environment) CreatedWhen() time.Time { if env == nil { - return time.Unix(0,0) + return time.Unix(0, 0) } env.Mu.RLock() defer env.Mu.RUnlock() @@ -153,6 +1119,47 @@ func (env *Environment) CurrentState() string { return env.Sm.Current() } +func (env *Environment) CurrentTransition() string { + if env == nil { + return "" + } + env.Mu.RLock() + defer env.Mu.RUnlock() + return env.currentTransition +} + +func (env *Environment) SetLastRequestUser(lastRequestUser *pb.User) { + if env == nil { + return + } + lastRequestUserJ, err := json.Marshal(lastRequestUser) + if err == nil { + env.UserVars.Set("last_request_user", string(lastRequestUserJ[:])) + } +} + +func (env *Environment) GetLastRequestUser() *pb.User { + if env == nil { + return nil + } + lastRequestUser := &pb.User{} + lastRequestUserJ, ok := env.UserVars.Get("last_request_user") + if ok { + _ = json.Unmarshal([]byte(lastRequestUserJ), lastRequestUser) + } + return lastRequestUser +} + +func (env *Environment) IsSafeToStop() bool { + tasks := env.Workflow().GetTasks() + for _, t := range tasks { + if !t.IsSafeToStop() { + return false + } + } + return true +} + func (env *Environment) Workflow() workflow.Role { if env == nil { return nil @@ -163,11 +1170,22 @@ func (env *Environment) Workflow() workflow.Role { } func (env *Environment) QueryRoles(pathSpec string) (rs []workflow.Role) { + env.Mu.RLock() + defer env.Mu.RUnlock() g := glob.MustCompile(pathSpec, workflow.PATH_SEPARATOR_RUNE) rs = env.workflow.GlobFilter(g) return } +func (env *Environment) GetId() uid.ID { + if env == nil { + return "" + } + env.Mu.RLock() + defer env.Mu.RUnlock() + return env.id +} + func (env *Environment) GetPath() string { return "" } @@ -176,8 +1194,305 @@ func (env *Environment) GetCurrentRunNumber() (rn uint32) { env.Mu.RLock() defer env.Mu.RUnlock() - if env.Sm.Current() != "RUNNING" { - return 0 - } return env.currentRunNumber -} \ No newline at end of file +} + +// setState will move environment to a given state from current state. +// The call does not trigger any callbacks, if defined. +func (env *Environment) setState(state string) { + if env == nil { + return + } + env.Mu.Lock() + defer env.Mu.Unlock() + env.Sm.SetState(state) +} + +func (env *Environment) subscribeToWfState(taskman *task.Manager) { + go func() { + wf := env.Workflow() + notify := make(chan sm.State) + subscriptionId := uuid.NewUUID().String() + env.wfAdapter.SubscribeToStateChange(subscriptionId, notify) + defer env.wfAdapter.UnsubscribeFromStateChange(subscriptionId) + env.unsubscribe = make(chan struct{}) + + wfState := wf.GetState() + if wfState != sm.ERROR { + handlingError := false + WORKFLOW_STATE_LOOP: + for { + select { + case wfState = <-notify: + if wfState == sm.ERROR { + if !handlingError { + handlingError = true + + time.AfterFunc(500*time.Millisecond, func() { // wait 0.5s for any other tasks to go to ERROR/INACTIVE + log.WithField("partition", env.id). + WithField("level", infologger.IL_Ops). + Error("one of the critical tasks went into ERROR state, transitioning the environment into ERROR") + + the.EventWriterWithTopic(topic.Environment).WriteEvent( + NewEnvGoErrorEvent(env, newCriticalTasksErrorMessage(env)), + ) + err := env.TryTransition(NewGoErrorTransition(taskman)) + if err != nil { + HandleFailedGoError(err, env) + } + }) + break WORKFLOW_STATE_LOOP + } + } + if wfState == sm.DONE { + break WORKFLOW_STATE_LOOP + } + case <-env.unsubscribe: + break WORKFLOW_STATE_LOOP + } + } + } + }() +} + +func (env *Environment) unsubscribeFromWfState() { + // Use select to unblock in case the above goroutine + // exits due to an ERROR state. If that's the case + // we close the channel. + env.once.Do(func() { + select { + case env.unsubscribe <- struct{}{}: + default: + if env.unsubscribe != nil { + close(env.unsubscribe) + } + } + }) +} + +func (env *Environment) addSubscription(sub Subscription) { + env.Mu.Lock() + env.eventStream = sub + env.Mu.Unlock() +} + +func (env *Environment) sendEnvironmentEvent(ev event.Event) { + env.Mu.Lock() + if env.eventStream != nil { + env.eventStream.Send(ev) + } + env.Mu.Unlock() +} + +func (env *Environment) closeStream() { + env.Mu.Lock() + if env.eventStream != nil { + env.eventStream.Unsubscribe() + env.eventStream = nil + } + env.Mu.Unlock() +} + +func (env *Environment) GetKV(path, key string) string { + if env == nil { + return "" + } + if len(path) == 0 { // empty path provided, we default to root item of current env workflow + path = env.workflow.GetName() + } + rolesForPath := env.QueryRoles(path) + if len(rolesForPath) == 0 { + return "" + } + env.Mu.RLock() + defer env.Mu.RUnlock() + role := rolesForPath[0] + varStack, err := role.ConsolidatedVarStack() + if err != nil { + return "" + } + payload := varStack[key] + return payload +} + +func (env *Environment) GetActiveDetectors() (response system.IDMap) { + if env == nil || env.workflow == nil { + return nil + } + response = make(system.IDMap) + + payload := env.GetKV("", "detectors") + slice, err := JSONSliceToSlice(payload) + if err != nil { + return + } + for _, det := range slice { + sid, err := system.IDString(det) // generated by enumer + if err != nil { + continue + } + response[sid] = struct{}{} + } + return +} + +func (env *Environment) GetFLPs() []string { + if env == nil || env.workflow == nil { + return nil + } + payload := env.GetKV("", "hosts") + response, err := JSONSliceToSlice(payload) + if err != nil { + return []string{} + } + return response +} + +func (env *Environment) GetAllHosts() []string { + if env == nil || env.workflow == nil { + return nil + } + + tasks := env.workflow.GetTasks() + hostSet := make(map[string]struct{}) + for _, t := range tasks { + hostSet[t.GetHostname()] = struct{}{} + } + + out := make([]string, len(hostSet)) + i := 0 + for hostname := range hostSet { + out[i] = hostname + i++ + } + sort.Strings(out) + return out +} + +func (env *Environment) GetRunType() runtype.RunType { + if env == nil || env.workflow == nil { + return runtype.NONE + } + rtString := env.GetKV("", "run_type") + rt, err := runtype.RunTypeString(rtString) + if err != nil { + log.WithField("partition", env.id). + WithField("level", infologger.IL_Support). + WithError(err). + Warnf("invalid run type %s", rtString) + return runtype.NONE + } + return rt +} + +func (env *Environment) GetVarsAsString() string { + if env == nil { + return "" + } + path := env.workflow.GetName() + rolesForPath := env.QueryRoles(path) + if len(rolesForPath) == 0 { + return "" + } + env.Mu.RLock() + defer env.Mu.RUnlock() + role := rolesForPath[0] + varStack, err := role.ConsolidatedVarStack() + if err != nil { + return "" + } + for _, r := range role.GetRoles() { + if r.GetName() == "odc" { + epnWorkflowCmdResult, ok := r.GetVars().Get("odc_script") + if !ok { + epnWorkflowCmdResult = "" + } + varStack["odc_script"] = epnWorkflowCmdResult + return sortMapToString(varStack) + } + } + return sortMapToString(varStack) +} + +func (env *Environment) GetWorkflowInfo() *pb.WorkflowTemplateInfo { + if env == nil || env.workflow == nil { + return &pb.WorkflowTemplateInfo{} + } + + out := &pb.WorkflowTemplateInfo{ + Name: env.name, + Description: env.Description, + Path: env.WorkflowPath, + Public: env.Public, + } + return out +} + +// return true if the auto stop transition has been scheduled, false otherwise +// if true, the expected stop time is also returned +func (env *Environment) scheduleAutoStopTransition() (scheduled bool, expected time.Time) { + autoStopEnabled := env.GetKV("", "auto_stop_enabled") + if autoStopEnabled == "true" { + autoStopTimeout := env.GetKV("", "auto_stop_timeout") + if autoStopTimeout != "" { + // if auto stop is enabled, parse the timeout, start a timer + // and start a go routine that will try a STOP transition after the timeout + autoStopDuration, err := time.ParseDuration(autoStopTimeout) + if err != nil { + log.WithField("partition", env.id). + WithField("run", env.currentRunNumber). + Errorf("Auto stop duration string parsing failed: %s", err.Error()) + return + } + + env.autoStopTimer = time.NewTimer(autoStopDuration) + ctx, cancel := context.WithCancel(context.Background()) + env.autoStopCancelFcn = cancel + go func() { + select { + case <-env.autoStopTimer.C: + log.WithField("partition", env.id). + WithField("run", env.currentRunNumber). + Infof("Executing scheduled auto stop transition following expiration of %s", autoStopDuration) + env.SetLastRequestUser(pb.SpecialUser(pb.SpecialUserId_TIMER)) + err = env.TryTransition(NewStopActivityTransition(ManagerInstance().taskman)) + if err != nil { + log.WithField("partition", env.id). + WithField("run", env.currentRunNumber). + Errorf("Scheduled auto stop transition failed: %s, Transitioning into ERROR", err.Error()) + + the.EventWriterWithTopic(topic.Environment).WriteEvent( + NewEnvGoErrorEvent(env, fmt.Sprintf("scheduled auto stop transition failed: %s", err.Error())), + ) + + err = env.TryTransition(NewGoErrorTransition(ManagerInstance().taskman)) + if err != nil { + HandleFailedGoError(err, env) + } + return + } + case <-ctx.Done(): + log.WithField("partition", env.id). + WithField("run", env.currentRunNumber). + Debugf("Scheduled auto stop transition was cancelled") + } + }() + + // if registered + scheduled = true + expected = time.Now().Add(autoStopDuration) + return + } + } + return +} + +func (env *Environment) invalidateAutoStopTransition() { + // Only try to stop an initialized timer + if env.autoStopTimer == nil { + return + } + if env.autoStopTimer.Stop() && env.autoStopCancelFcn != nil { + env.autoStopCancelFcn() + } +} diff --git a/core/environment/environment_test.go b/core/environment/environment_test.go new file mode 100644 index 000000000..0badb9d4f --- /dev/null +++ b/core/environment/environment_test.go @@ -0,0 +1,24 @@ +package environment + +import ( + "github.com/AliceO2Group/Control/core/integration" + "github.com/AliceO2Group/Control/core/integration/testplugin" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "github.com/spf13/viper" + "testing" +) + +var _ = BeforeSuite(func() { + integration.Reset() + integration.RegisterPlugin("testplugin", "testPluginEndpoint", testplugin.NewPlugin) + viper.Reset() + viper.Set("integrationPlugins", []string{"testplugin"}) + viper.Set("testPluginEndpoint", "http://example.com") + viper.Set("config_endpoint", "mock://") +}) + +func TestCoreEnvironment(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Core Environment Test Suite") +} diff --git a/core/environment/eventStream.go b/core/environment/eventStream.go new file mode 100644 index 000000000..d1075ec25 --- /dev/null +++ b/core/environment/eventStream.go @@ -0,0 +1,132 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2020 CERN and copyright holders of ALICE O². + * Author: Miltiadis Alexis + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package environment + +import ( + "sync" + + "github.com/AliceO2Group/Control/common/event" + pb "github.com/AliceO2Group/Control/common/protos" +) + +type Subscription interface { + Unsubscribe() + GetFeed() chan *pb.Event + Send(event.Event) + Err() <-chan error +} + +type eventStream struct { + stream chan *pb.Event + mu sync.Mutex +} + +func SubscribeToStream(ch chan *pb.Event) Subscription { + return &eventSub{ + feed: &eventStream{ + stream: ch, + }, + err: make(chan error), + } +} + +func (e *eventStream) send(data *pb.Event) { + e.mu.Lock() + defer e.mu.Unlock() + if e.stream != nil { + e.stream <- data + } +} + +func (e *eventStream) closeStream() { + e.mu.Lock() + defer e.mu.Unlock() + close(e.stream) + e.stream = nil +} + +type eventSub struct { + feed *eventStream + once sync.Once + err chan error +} + +func (s *eventSub) Unsubscribe() { + s.once.Do(func() { + s.feed.closeStream() + close(s.err) + }) +} + +func (s *eventSub) GetFeed() chan *pb.Event { + return s.feed.stream +} + +func (s *eventSub) Send(ev event.Event) { + var data *pb.Event + + switch typedEvent := ev.(type) { + case *event.RoleEvent: + re := pb.Event_RoleEvent{ + RoleEvent: &pb.Ev_RoleEvent{ + Name: typedEvent.GetName(), + State: typedEvent.GetState(), + Status: typedEvent.GetStatus(), + RolePath: typedEvent.GetRolePath(), + }, + } + data = pb.WrapEvent(&re) + case *event.TaskEvent: + re := pb.Event_TaskEvent{ + TaskEvent: &pb.Ev_TaskEvent{ + Name: typedEvent.GetName(), + Taskid: typedEvent.GetTaskID(), + State: typedEvent.GetState(), + Status: typedEvent.GetStatus(), + Hostname: typedEvent.GetHostname(), + ClassName: typedEvent.GetClassName(), + }, + } + data = pb.WrapEvent(&re) + case *event.EnvironmentEvent: + re := pb.Event_EnvironmentEvent{ + EnvironmentEvent: &pb.Ev_EnvironmentEvent{ + EnvironmentId: typedEvent.GetName(), + State: typedEvent.GetState(), + RunNumber: typedEvent.GetRun(), + Error: typedEvent.GetError(), + Message: typedEvent.GetMessage(), + }, + } + data = pb.WrapEvent(&re) + default: + // noop + } + s.feed.send(data) +} + +func (s *eventSub) Err() <-chan error { + return s.err +} diff --git a/core/environment/fsm_test.go b/core/environment/fsm_test.go new file mode 100644 index 000000000..2fa50757e --- /dev/null +++ b/core/environment/fsm_test.go @@ -0,0 +1,120 @@ +package environment + +import ( + "github.com/AliceO2Group/Control/common/utils/uid" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +var _ = Describe("allowed states and transitions in the environment FSM", func() { + var env *Environment + BeforeEach(func() { + envId, err := uid.FromString("2oDvieFrVTi") + Expect(err).NotTo(HaveOccurred()) + + env, err = newEnvironment(nil, envId) + Expect(err).NotTo(HaveOccurred()) + Expect(env).NotTo(BeNil()) + }) + When("FSM is created", func() { + It("should be in STANDBY", func() { + Expect(env.Sm.Current()).To(Equal("STANDBY")) + }) + }) + When("FSM is in STANDBY", func() { + It("should allow for DEPLOY, GO_ERROR and EXIT transitions", func() { + env.Sm.SetState("STANDBY") + Expect(env.Sm.Can("DEPLOY")).To(BeTrue()) + Expect(env.Sm.Can("GO_ERROR")).To(BeTrue()) + Expect(env.Sm.Can("EXIT")).To(BeTrue()) + }) + It("should not allow for other transitions", func() { + env.Sm.SetState("STANDBY") + Expect(env.Sm.Cannot("CONFIGURE")).To(BeTrue()) + Expect(env.Sm.Cannot("RESET")).To(BeTrue()) + Expect(env.Sm.Cannot("START_ACTIVITY")).To(BeTrue()) + Expect(env.Sm.Cannot("STOP_ACTIVITY")).To(BeTrue()) + Expect(env.Sm.Cannot("RECOVER")).To(BeTrue()) + }) + }) + When("FSM is in DEPLOYED", func() { + It("should allow for CONFIGURED, GO_ERROR and EXIT transitions", func() { + env.Sm.SetState("DEPLOYED") + Expect(env.Sm.Can("CONFIGURE")).To(BeTrue()) + Expect(env.Sm.Can("GO_ERROR")).To(BeTrue()) + Expect(env.Sm.Can("EXIT")).To(BeTrue()) + }) + It("should not allow for other transitions", func() { + env.Sm.SetState("DEPLOYED") + Expect(env.Sm.Cannot("DEPLOY")).To(BeTrue()) + Expect(env.Sm.Cannot("RESET")).To(BeTrue()) + Expect(env.Sm.Cannot("START_ACTIVITY")).To(BeTrue()) + Expect(env.Sm.Cannot("STOP_ACTIVITY")).To(BeTrue()) + Expect(env.Sm.Cannot("RECOVER")).To(BeTrue()) + }) + }) + When("FSM is in CONFIGURED", func() { + It("should allow for START_ACTIVITY, RESET, GO_ERROR and EXIT transitions", func() { + env.Sm.SetState("CONFIGURED") + Expect(env.Sm.Can("START_ACTIVITY")).To(BeTrue()) + Expect(env.Sm.Can("RESET")).To(BeTrue()) + Expect(env.Sm.Can("GO_ERROR")).To(BeTrue()) + Expect(env.Sm.Can("EXIT")).To(BeTrue()) + }) + It("should not allow for other transitions", func() { + env.Sm.SetState("CONFIGURED") + Expect(env.Sm.Cannot("DEPLOY")).To(BeTrue()) + Expect(env.Sm.Cannot("CONFIGURE")).To(BeTrue()) + Expect(env.Sm.Cannot("STOP_ACTIVITY")).To(BeTrue()) + Expect(env.Sm.Cannot("RECOVER")).To(BeTrue()) + }) + }) + When("FSM is in RUNNING", func() { + It("should allow for STOP_ACTIVITY and GO_ERROR transitions", func() { + env.Sm.SetState("RUNNING") + Expect(env.Sm.Can("STOP_ACTIVITY")).To(BeTrue()) + Expect(env.Sm.Can("GO_ERROR")).To(BeTrue()) + }) + It("should not allow for other transitions", func() { + env.Sm.SetState("RUNNING") + Expect(env.Sm.Cannot("DEPLOY")).To(BeTrue()) + Expect(env.Sm.Cannot("RESET")).To(BeTrue()) + Expect(env.Sm.Cannot("CONFIGURE")).To(BeTrue()) + Expect(env.Sm.Cannot("START_ACTIVITY")).To(BeTrue()) + Expect(env.Sm.Cannot("RECOVER")).To(BeTrue()) + Expect(env.Sm.Cannot("EXIT")).To(BeTrue()) + }) + }) + When("FSM is in ERROR", func() { + It("should allow for RECOVER transition", func() { + env.Sm.SetState("ERROR") + Expect(env.Sm.Can("RECOVER")).To(BeTrue()) + // We do not include EXIT as possible transition, since anyway we kill tasks not caring about the FSM. + // There is no known issue which could forbid us from that. + // TEARDOWN and DESTROY are the artificial transitions which correspond to that. + }) + It("should not allow for other transitions", func() { + env.Sm.SetState("ERROR") + Expect(env.Sm.Cannot("GO_ERROR")).To(BeTrue()) + Expect(env.Sm.Cannot("DEPLOY")).To(BeTrue()) + Expect(env.Sm.Cannot("RESET")).To(BeTrue()) + Expect(env.Sm.Cannot("CONFIGURE")).To(BeTrue()) + Expect(env.Sm.Cannot("START_ACTIVITY")).To(BeTrue()) + Expect(env.Sm.Cannot("STOP_ACTIVITY")).To(BeTrue()) + Expect(env.Sm.Cannot("EXIT")).To(BeTrue()) + }) + }) + When("FSM is in DONE", func() { + It("should not allow for any transitions", func() { + env.Sm.SetState("DONE") + Expect(env.Sm.Cannot("GO_ERROR")).To(BeTrue()) + Expect(env.Sm.Cannot("DEPLOY")).To(BeTrue()) + Expect(env.Sm.Cannot("RESET")).To(BeTrue()) + Expect(env.Sm.Cannot("CONFIGURE")).To(BeTrue()) + Expect(env.Sm.Cannot("START_ACTIVITY")).To(BeTrue()) + Expect(env.Sm.Cannot("STOP_ACTIVITY")).To(BeTrue()) + Expect(env.Sm.Cannot("RECOVER")).To(BeTrue()) + Expect(env.Sm.Cannot("EXIT")).To(BeTrue()) + }) + }) +}) diff --git a/core/environment/hooks_test.go b/core/environment/hooks_test.go new file mode 100644 index 000000000..b4d70131e --- /dev/null +++ b/core/environment/hooks_test.go @@ -0,0 +1,494 @@ +package environment + +import ( + "context" + "fmt" + "github.com/AliceO2Group/Control/common/utils/uid" + "github.com/AliceO2Group/Control/core/task" + "github.com/AliceO2Group/Control/core/workflow" + "github.com/AliceO2Group/Control/core/workflow/callable" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +type DummyTransition struct { + baseTransition + fail bool +} + +func NewDummyTransition(transition string, fail bool) Transition { + return &DummyTransition{ + baseTransition: baseTransition{ + name: transition, + taskman: nil, + }, + fail: fail, + } +} + +func (t DummyTransition) do(env *Environment) (err error) { + if t.fail { + return fmt.Errorf("transition successfully failed") + } + return nil +} + +var _ = Describe("calling hooks on FSM events", func() { + var env *Environment + BeforeEach(func() { + envId, err := uid.FromString("2oDvieFrVTi") + Expect(err).NotTo(HaveOccurred()) + + env, err = newEnvironment(map[string]string{}, envId) + Expect(err).NotTo(HaveOccurred()) + Expect(env).NotTo(BeNil()) + }) + + It("should execute the requested plugin call without errors", func() { + env.workflow = workflow.NewAggregatorRole("root", []workflow.Role{ + workflow.NewCallRole( + "call", + task.Traits{Trigger: "before_CONFIGURE", Timeout: "5s", Critical: true, Await: "before_CONFIGURE"}, + "testplugin.Test()", + "")}) + workflow.LinkChildrenToParents(env.workflow) + env.Sm.SetState("DEPLOYED") + + err := env.Sm.Event(context.Background(), "CONFIGURE", NewDummyTransition("CONFIGURE", false)) + + Expect(err).NotTo(HaveOccurred()) + v, ok := env.workflow.GetUserVars().Get("root.call_called") + Expect(ok).To(BeTrue()) + Expect(v).To(Equal("true")) + }) + + It("should return an error and cancel transition if a critical hook fails at before_", func() { + env.workflow = workflow.NewAggregatorRole("root", []workflow.Role{ + workflow.NewCallRole( + "call", + task.Traits{Trigger: "before_CONFIGURE", Timeout: "5s", Critical: true, Await: "before_CONFIGURE"}, + "testplugin.Test()", + "")}) + workflow.LinkChildrenToParents(env.workflow) + env.Sm.SetState("DEPLOYED") + env.workflow.GetUserVars().Set("testplugin_fail", "true") + + err := env.Sm.Event(context.Background(), "CONFIGURE", NewDummyTransition("CONFIGURE", false)) + + Expect(err).To(HaveOccurred()) + v, ok := env.workflow.GetUserVars().Get("root.call_called") + Expect(ok).To(BeTrue()) + Expect(v).To(Equal("true")) + Expect(env.Sm.Current()).To(Equal("DEPLOYED")) + }) + + It("should return an error and cancel transition if a critical hook fails at leave_", func() { + env.workflow = workflow.NewAggregatorRole("root", []workflow.Role{ + workflow.NewCallRole( + "call", + task.Traits{Trigger: "leave_DEPLOYED", Timeout: "5s", Critical: true, Await: "leave_DEPLOYED"}, + "testplugin.Test()", + "")}) + workflow.LinkChildrenToParents(env.workflow) + env.Sm.SetState("DEPLOYED") + env.workflow.GetUserVars().Set("testplugin_fail", "true") + + err := env.Sm.Event(context.Background(), "CONFIGURE", NewDummyTransition("CONFIGURE", false)) + + Expect(err).To(HaveOccurred()) + v, ok := env.workflow.GetUserVars().Get("root.call_called") + Expect(ok).To(BeTrue()) + Expect(v).To(Equal("true")) + Expect(env.Sm.Current()).To(Equal("DEPLOYED")) + }) + + It("should return an error, but NOT cancel the transition if a critical hook fails at enter_", func() { + // ...because we cannot cancel transition that is already done. + env.workflow = workflow.NewAggregatorRole("root", []workflow.Role{ + workflow.NewCallRole( + "call", + task.Traits{Trigger: "enter_CONFIGURED", Timeout: "5s", Critical: true, Await: "enter_CONFIGURED"}, + "testplugin.Test()", + "")}) + workflow.LinkChildrenToParents(env.workflow) + env.Sm.SetState("DEPLOYED") + env.workflow.GetUserVars().Set("testplugin_fail", "true") + + err := env.Sm.Event(context.Background(), "CONFIGURE", NewDummyTransition("CONFIGURE", false)) + + Expect(err).To(HaveOccurred()) + v, ok := env.workflow.GetUserVars().Get("root.call_called") + Expect(ok).To(BeTrue()) + Expect(v).To(Equal("true")) + Expect(env.Sm.Current()).To(Equal("CONFIGURED")) + }) + + It("should return an error, but NOT cancel the transition if a critical hook fails at after_", func() { + // ...because we cannot cancel transition that is already done. + env.workflow = workflow.NewAggregatorRole("root", []workflow.Role{ + workflow.NewCallRole( + "call", + task.Traits{Trigger: "after_CONFIGURE", Timeout: "5s", Critical: true, Await: "after_CONFIGURE"}, + "testplugin.Test()", + "")}) + workflow.LinkChildrenToParents(env.workflow) + env.Sm.SetState("DEPLOYED") + env.workflow.GetUserVars().Set("testplugin_fail", "true") + + err := env.Sm.Event(context.Background(), "CONFIGURE", NewDummyTransition("CONFIGURE", false)) + + Expect(err).To(HaveOccurred()) + v, ok := env.workflow.GetUserVars().Get("root.call_called") + Expect(ok).To(BeTrue()) + Expect(v).To(Equal("true")) + Expect(env.Sm.Current()).To(Equal("CONFIGURED")) + }) + + It("should not return an error if an non-critical hook fails", func() { + env.workflow = workflow.NewAggregatorRole("root", []workflow.Role{ + workflow.NewCallRole( + "call", + task.Traits{Trigger: "before_CONFIGURE", Timeout: "5s", Critical: false, Await: "before_CONFIGURE"}, + "testplugin.Test()", + "")}) + workflow.LinkChildrenToParents(env.workflow) + env.Sm.SetState("DEPLOYED") + env.workflow.GetUserVars().Set("testplugin_fail", "true") + + err := env.Sm.Event(context.Background(), "CONFIGURE", NewDummyTransition("CONFIGURE", false)) + + Expect(err).NotTo(HaveOccurred()) + v, ok := env.workflow.GetUserVars().Get("root.call_called") + Expect(ok).To(BeTrue()) + Expect(v).To(Equal("true")) + }) + + It("should execute a hook with await statement different than the trigger, but within the same transition", func() { + env.workflow = workflow.NewAggregatorRole("root", []workflow.Role{ + workflow.NewCallRole( + "call", + task.Traits{Trigger: "before_CONFIGURE", Timeout: "5s", Critical: true, Await: "after_CONFIGURE"}, + "testplugin.Test()", + "")}) + workflow.LinkChildrenToParents(env.workflow) + env.Sm.SetState("DEPLOYED") + + err := env.Sm.Event(context.Background(), "CONFIGURE", NewDummyTransition("CONFIGURE", false)) + + Expect(err).NotTo(HaveOccurred()) + v, ok := env.workflow.GetUserVars().Get("root.call_called") + Expect(ok).To(BeTrue()) + Expect(v).To(Equal("true")) + }) + + It("should execute a hook with await statement at a different transition than the trigger", func() { + env.workflow = workflow.NewAggregatorRole("root", []workflow.Role{ + workflow.NewCallRole( + "call", + task.Traits{Trigger: "before_CONFIGURE", Timeout: "5s", Critical: true, Await: "before_RESET"}, + "testplugin.Test()", + "")}) + workflow.LinkChildrenToParents(env.workflow) + env.Sm.SetState("DEPLOYED") + + err := env.Sm.Event(context.Background(), "CONFIGURE", NewDummyTransition("CONFIGURE", false)) + Expect(err).NotTo(HaveOccurred()) + err = env.Sm.Event(context.Background(), "RESET", NewDummyTransition("RESET", false)) + Expect(err).NotTo(HaveOccurred()) + + v, ok := env.workflow.GetUserVars().Get("root.call_called") + Expect(ok).To(BeTrue()) + Expect(v).To(Equal("true")) + }) + + It("should not execute a hook that should happen after a successful transition, but the transition fails", func() { + env.workflow = workflow.NewAggregatorRole("root", []workflow.Role{ + workflow.NewCallRole( + "call", + task.Traits{Trigger: "after_CONFIGURE", Timeout: "5s", Critical: true, Await: "after_CONFIGURE"}, + "testplugin.Test()", + "")}) + workflow.LinkChildrenToParents(env.workflow) + env.Sm.SetState("DEPLOYED") + + err := env.Sm.Event(context.Background(), "CONFIGURE", NewDummyTransition("CONFIGURE", true)) + Expect(err).To(HaveOccurred()) + + _, ok := env.workflow.GetUserVars().Get("root.call_called") + Expect(ok).To(BeFalse()) + }) + + Context("activity-related timestamps", func() { + It("should set run_start_time_ms just after before_START_ACTIVITY<0 hooks are executed", func() { + env.workflow = workflow.NewAggregatorRole("root", []workflow.Role{ + workflow.NewCallRole( + "call1", + task.Traits{Trigger: "before_START_ACTIVITY-1", Timeout: "5s", Critical: true, Await: "before_START_ACTIVITY-1"}, + "testplugin.TimestampObserver()", + ""), + workflow.NewCallRole( + "call2", + task.Traits{Trigger: "before_START_ACTIVITY", Timeout: "5s", Critical: true, Await: "before_START_ACTIVITY"}, + "testplugin.TimestampObserver()", + "")}) + workflow.LinkChildrenToParents(env.workflow) + env.Sm.SetState("CONFIGURED") + + err := env.Sm.Event(context.Background(), "START_ACTIVITY", NewDummyTransition("START_ACTIVITY", false)) + Expect(err).NotTo(HaveOccurred()) + + _, ok := env.workflow.GetUserVars().Get("root.call1_saw_run_start_time_ms") + Expect(ok).To(BeFalse()) + _, ok = env.workflow.GetUserVars().Get("root.call2_saw_run_start_time_ms") + Expect(ok).To(BeTrue()) + _, ok = env.workflow.GetUserVars().Get("root.call2_saw_run_start_completion_time_ms") + Expect(ok).To(BeFalse()) + }) + It("should set run_start_completion_time_ms just after after_START_ACTIVITY<0 hooks are executed", func() { + env.workflow = workflow.NewAggregatorRole("root", []workflow.Role{ + workflow.NewCallRole( + "call1", + task.Traits{Trigger: "after_START_ACTIVITY-1", Timeout: "5s", Critical: true, Await: "after_START_ACTIVITY-1"}, + "testplugin.TimestampObserver()", + ""), + workflow.NewCallRole( + "call2", + task.Traits{Trigger: "after_START_ACTIVITY", Timeout: "5s", Critical: true, Await: "after_START_ACTIVITY"}, + "testplugin.TimestampObserver()", + "")}) + workflow.LinkChildrenToParents(env.workflow) + env.Sm.SetState("CONFIGURED") + + err := env.Sm.Event(context.Background(), "START_ACTIVITY", NewDummyTransition("START_ACTIVITY", false)) + Expect(err).NotTo(HaveOccurred()) + + _, ok := env.workflow.GetUserVars().Get("root.call1_saw_run_start_completion_time_ms") + Expect(ok).To(BeFalse()) + _, ok = env.workflow.GetUserVars().Get("root.call2_saw_run_start_completion_time_ms") + Expect(ok).To(BeTrue()) + }) + It("should set run_end_time_ms just after before_STOP_ACTIVITY<0 hooks are executed", func() { + env.workflow = workflow.NewAggregatorRole("root", []workflow.Role{ + workflow.NewCallRole( + "call1", + task.Traits{Trigger: "before_STOP_ACTIVITY-1", Timeout: "5s", Critical: true, Await: "before_STOP_ACTIVITY-1"}, + "testplugin.TimestampObserver()", + ""), + workflow.NewCallRole( + "call2", + task.Traits{Trigger: "before_STOP_ACTIVITY", Timeout: "5s", Critical: true, Await: "before_STOP_ACTIVITY"}, + "testplugin.TimestampObserver()", + "")}) + workflow.LinkChildrenToParents(env.workflow) + env.Sm.SetState("CONFIGURED") + + err := env.Sm.Event(context.Background(), "START_ACTIVITY", NewDummyTransition("START_ACTIVITY", false)) + Expect(err).NotTo(HaveOccurred()) + err = env.Sm.Event(context.Background(), "STOP_ACTIVITY", NewDummyTransition("STOP_ACTIVITY", false)) + Expect(err).NotTo(HaveOccurred()) + + _, ok := env.workflow.GetUserVars().Get("root.call1_saw_run_end_time_ms") + Expect(ok).To(BeFalse()) + _, ok = env.workflow.GetUserVars().Get("root.call2_saw_run_end_time_ms") + Expect(ok).To(BeTrue()) + _, ok = env.workflow.GetUserVars().Get("root.call2_saw_run_end_completion_time_ms") + Expect(ok).To(BeFalse()) + }) + It("should set run_end_completion_time_ms just before after_STOP_ACTIVITY<0 hooks are executed", func() { + env.workflow = workflow.NewAggregatorRole("root", []workflow.Role{ + workflow.NewCallRole( + "call1", + task.Traits{Trigger: "after_STOP_ACTIVITY-1", Timeout: "5s", Critical: true, Await: "after_STOP_ACTIVITY-1"}, + "testplugin.TimestampObserver()", + ""), + workflow.NewCallRole( + "call2", + task.Traits{Trigger: "after_STOP_ACTIVITY", Timeout: "5s", Critical: true, Await: "after_STOP_ACTIVITY"}, + "testplugin.TimestampObserver()", + "")}) + workflow.LinkChildrenToParents(env.workflow) + env.Sm.SetState("CONFIGURED") + + err := env.Sm.Event(context.Background(), "START_ACTIVITY", NewDummyTransition("START_ACTIVITY", false)) + Expect(err).NotTo(HaveOccurred()) + err = env.Sm.Event(context.Background(), "STOP_ACTIVITY", NewDummyTransition("STOP_ACTIVITY", false)) + Expect(err).NotTo(HaveOccurred()) + + _, ok := env.workflow.GetUserVars().Get("root.call1_saw_run_end_completion_time_ms") + Expect(ok).To(BeFalse()) + _, ok = env.workflow.GetUserVars().Get("root.call2_saw_run_end_completion_time_ms") + Expect(ok).To(BeTrue()) + }) + It("should clear timestamps from previous runs and set run_start_time_ms again before before_START_ACTIVITY hooks", func() { + env.workflow = workflow.NewAggregatorRole("root", []workflow.Role{ + workflow.NewCallRole( + "call", + task.Traits{Trigger: "before_START_ACTIVITY", Timeout: "5s", Critical: true, Await: "before_START_ACTIVITY"}, + "testplugin.TimestampObserver()", + "")}) + workflow.LinkChildrenToParents(env.workflow) + env.Sm.SetState("CONFIGURED") + + err := env.Sm.Event(context.Background(), "START_ACTIVITY", NewDummyTransition("START_ACTIVITY", false)) + Expect(err).NotTo(HaveOccurred()) + err = env.Sm.Event(context.Background(), "STOP_ACTIVITY", NewDummyTransition("STOP_ACTIVITY", false)) + Expect(err).NotTo(HaveOccurred()) + + env.workflow.GetUserVars().Del("root.call_saw_run_start_time_ms") + env.workflow.GetUserVars().Del("root.call_saw_run_start_completion_time_ms") + env.workflow.GetUserVars().Del("root.call_saw_run_end_time_ms") + env.workflow.GetUserVars().Del("root.call_saw_run_end_completion_time_ms") + err = env.Sm.Event(context.Background(), "START_ACTIVITY", NewDummyTransition("START_ACTIVITY", false)) + Expect(err).NotTo(HaveOccurred()) + + v, ok := env.workflow.GetUserVars().Get("root.call_saw_run_start_time_ms") + Expect(ok).To(BeTrue()) + Expect(v).To(Equal("true")) + _, ok = env.workflow.GetUserVars().Get("root.call_saw_run_start_completion_time_ms") + Expect(ok).To(BeFalse()) + _, ok = env.workflow.GetUserVars().Get("root.call_saw_run_end_time_ms") + Expect(ok).To(BeFalse()) + _, ok = env.workflow.GetUserVars().Get("root.call_saw_run_end_completion_time_ms") + Expect(ok).To(BeFalse()) + }) + When("START_ACTIVITY transition fails", func() { + It("should set SOSOR timestamp, while the subsequent GO_ERROR transition should set SOEOR and EOEOR (but NOT EOSOR)", func() { + env.workflow = workflow.NewAggregatorRole("root", []workflow.Role{}) + workflow.LinkChildrenToParents(env.workflow) + env.Sm.SetState("CONFIGURED") + + err := env.Sm.Event(context.Background(), "START_ACTIVITY", NewDummyTransition("START_ACTIVITY", true)) + Expect(err).To(HaveOccurred()) + + v, ok := env.workflow.GetUserVars().Get("run_start_time_ms") + Expect(ok).To(BeTrue()) + Expect(v).NotTo(BeEmpty()) + v, ok = env.workflow.GetUserVars().Get("run_start_completion_time_ms") + Expect(ok).To(BeTrue()) + Expect(v).To(BeEmpty()) + + err = env.Sm.Event(context.Background(), "GO_ERROR", NewDummyTransition("GO_ERROR", false)) + Expect(err).NotTo(HaveOccurred()) + v, ok = env.workflow.GetUserVars().Get("run_start_completion_time_ms") + Expect(ok).To(BeTrue()) + Expect(v).To(BeEmpty()) + v, ok = env.workflow.GetUserVars().Get("run_end_time_ms") + Expect(ok).To(BeTrue()) + Expect(v).NotTo(BeEmpty()) + v, ok = env.workflow.GetUserVars().Get("run_end_completion_time_ms") + Expect(ok).To(BeTrue()) + Expect(v).NotTo(BeEmpty()) + }) + }) + When("STOP_ACTIVITY transition fails", func() { + It("should set SOEOR timestamp, while EOEOR should be set by subsequent GO_ERROR transition", func() { + env.workflow = workflow.NewAggregatorRole("root", []workflow.Role{}) + workflow.LinkChildrenToParents(env.workflow) + env.Sm.SetState("CONFIGURED") + + err := env.Sm.Event(context.Background(), "START_ACTIVITY", NewDummyTransition("START_ACTIVITY", false)) + Expect(err).NotTo(HaveOccurred()) + err = env.Sm.Event(context.Background(), "STOP_ACTIVITY", NewDummyTransition("STOP_ACTIVITY", true)) + Expect(err).To(HaveOccurred()) + + v, ok := env.workflow.GetUserVars().Get("run_end_time_ms") + Expect(ok).To(BeTrue()) + Expect(v).NotTo(BeEmpty()) + v, ok = env.workflow.GetUserVars().Get("run_end_completion_time_ms") + Expect(ok).To(BeTrue()) + Expect(v).To(BeEmpty()) + + err = env.Sm.Event(context.Background(), "GO_ERROR", NewDummyTransition("GO_ERROR", false)) + Expect(err).NotTo(HaveOccurred()) + v, ok = env.workflow.GetUserVars().Get("run_end_completion_time_ms") + Expect(ok).To(BeTrue()) + Expect(v).NotTo(BeEmpty()) + }) + }) + When("environment goes to ERROR while in RUNNING", func() { + It("should set both run end timestamps", func() { + env.workflow = workflow.NewAggregatorRole("root", []workflow.Role{ + workflow.NewCallRole( + "call", + task.Traits{Trigger: "leave_RUNNING", Timeout: "5s", Critical: true, Await: "leave_RUNNING"}, + "testplugin.TimestampObserver()", + "")}) + workflow.LinkChildrenToParents(env.workflow) + env.Sm.SetState("CONFIGURED") + + err := env.Sm.Event(context.Background(), "START_ACTIVITY", NewDummyTransition("START_ACTIVITY", false)) + Expect(err).NotTo(HaveOccurred()) + err = env.Sm.Event(context.Background(), "GO_ERROR", NewDummyTransition("GO_ERROR", false)) + Expect(err).NotTo(HaveOccurred()) + + v, ok := env.workflow.GetUserVars().Get("root.call_saw_run_end_time_ms") + Expect(ok).To(BeTrue()) + Expect(v).To(Equal("true")) + _, ok = env.workflow.GetUserVars().Get("root.call_saw_run_end_completion_time_ms") + Expect(ok).To(BeFalse()) + v, ok = env.workflow.GetUserVars().Get("run_end_completion_time_ms") + Expect(ok).To(BeTrue()) + Expect(v).NotTo(BeEmpty()) + }) + }) + }) + + It("should allow to arrange multiple calls in order", func() { + env.workflow = workflow.NewAggregatorRole("root", []workflow.Role{ + workflow.NewCallRole( + "call3", + task.Traits{Trigger: "before_CONFIGURE+50", Timeout: "5s", Critical: true, Await: "before_CONFIGURE+50"}, + "testplugin.CallOrderObserver()", + ""), + workflow.NewCallRole( + "call2", + task.Traits{Trigger: "before_CONFIGURE+0", Timeout: "5s", Critical: true, Await: "before_CONFIGURE+0"}, + "testplugin.CallOrderObserver()", + ""), + workflow.NewCallRole( + "call1", + task.Traits{Trigger: "before_CONFIGURE-50", Timeout: "5s", Critical: true, Await: "before_CONFIGURE-50"}, + "testplugin.CallOrderObserver()", + "")}) + workflow.LinkChildrenToParents(env.workflow) + env.Sm.SetState("DEPLOYED") + + err := env.Sm.Event(context.Background(), "CONFIGURE", NewDummyTransition("CONFIGURE", false)) + + Expect(err).NotTo(HaveOccurred()) + v, ok := env.workflow.GetUserVars().Get("call_history") // set by testplugin.CallOrderObserver + Expect(ok).To(BeTrue()) + Expect(v).To(Equal("root.call1,root.call2,root.call3")) + }) + + It("should allow to cancel hooks in case that await trigger never happens", func() { + env.workflow = workflow.NewAggregatorRole("root", []workflow.Role{ + workflow.NewCallRole( + "call1", // this call should return immediately and should not be accessible later + task.Traits{Trigger: "before_CONFIGURE", Timeout: "5s", Critical: true, Await: "before_CONFIGURE"}, + "testplugin.Test()", + ""), + workflow.NewCallRole( + "call2", // this call should not return, but should be cancelled later + task.Traits{Trigger: "before_CONFIGURE", Timeout: "5s", Critical: true, Await: "after_CONFIGURE"}, + "testplugin.Test()", + "")}) + workflow.LinkChildrenToParents(env.workflow) + env.Sm.SetState("DEPLOYED") + + err := env.Sm.Event(context.Background(), "CONFIGURE", NewDummyTransition("CONFIGURE", true)) + Expect(err).To(HaveOccurred()) + + callMapForAwait := env.callsPendingAwait + Expect(callMapForAwait).To(HaveKey("after_CONFIGURE")) + callsForWeight := callMapForAwait["after_CONFIGURE"] + Expect(callsForWeight).To(HaveKey(callable.HookWeight(0))) + calls := callsForWeight[0] + Expect(calls).To(HaveLen(1)) + Expect(calls[0]).NotTo(BeNil()) + // the first cancel attempt should return "true" to say it was successful + Expect(calls[0].Cancel()).To(BeTrue()) + // the subsequent cancel attempts should return "false", because the call was already cancelled + Expect(calls[0].Cancel()).To(BeFalse()) + }) +}) diff --git a/core/environment/manager.go b/core/environment/manager.go index bec5d107e..8546bd0c9 100644 --- a/core/environment/manager.go +++ b/core/environment/manager.go @@ -1,7 +1,7 @@ /* * === This file is part of ALICE O² === * - * Copyright 2017-2018 CERN and copyright holders of ALICE O². + * Copyright 2017-2022 CERN and copyright holders of ALICE O². * Author: Teo Mrnjavac * * This program is free software: you can redistribute it and/or modify @@ -25,124 +25,1546 @@ package environment import ( + "encoding/json" "errors" "fmt" + "strconv" "strings" "sync" + "time" + "github.com/AliceO2Group/Control/common" + "github.com/AliceO2Group/Control/common/controlmode" + "github.com/AliceO2Group/Control/common/event" + "github.com/AliceO2Group/Control/common/event/topic" + "github.com/AliceO2Group/Control/common/logger/infologger" + evpb "github.com/AliceO2Group/Control/common/protos" + "github.com/AliceO2Group/Control/common/system" + "github.com/AliceO2Group/Control/common/utils" + "github.com/AliceO2Group/Control/common/utils/uid" + lhcevent "github.com/AliceO2Group/Control/core/integration/lhc/event" + event2 "github.com/AliceO2Group/Control/core/integration/odc/event" "github.com/AliceO2Group/Control/core/task" + "github.com/AliceO2Group/Control/core/task/sm" + "github.com/AliceO2Group/Control/core/task/taskop" "github.com/AliceO2Group/Control/core/the" "github.com/AliceO2Group/Control/core/workflow" - "github.com/pborman/uuid" + pb "github.com/AliceO2Group/Control/executor/protos" + "github.com/sirupsen/logrus" ) type Manager struct { - mu sync.RWMutex - m map[uuid.Array]*Environment - taskman *task.Manager + mu sync.RWMutex + m map[uid.ID]*Environment + taskman *task.Manager + incomingEventCh chan event.Event + pendingTeardownsCh map[uid.ID]chan *event.TasksReleasedEvent + pendingStateChangeCh map[uid.ID]chan *event.TasksStateChangedEvent } -func NewEnvManager(tm *task.Manager) *Manager { - return &Manager{ - m: make(map[uuid.Array]*Environment), - taskman: tm, +var instance *Manager + +func ManagerInstance() *Manager { + return instance +} + +func NewEnvManager(tm *task.Manager, incomingEventCh chan event.Event) *Manager { + instance = &Manager{ + m: make(map[uid.ID]*Environment), + taskman: tm, + incomingEventCh: incomingEventCh, + pendingTeardownsCh: make(map[uid.ID]chan *event.TasksReleasedEvent), + pendingStateChangeCh: make(map[uid.ID]chan *event.TasksStateChangedEvent), } + + go func() { + for { + select { + case incomingEvent := <-instance.incomingEventCh: + switch typedEvent := incomingEvent.(type) { + case event.DeviceEvent: + instance.handleDeviceEvent(typedEvent) + + case event.IntegratedServiceEvent: + instance.handleIntegratedServiceEvent(typedEvent) + + case *event.ExecutorFailedEvent: + envIdsAffected := tm.HandleExecutorFailed(typedEvent) + for envId := range envIdsAffected { + env, err := instance.environment(envId) + if err != nil { + log.WithPrefix("scheduler"). + WithField("partition", envId.String()). + WithField("executorId", typedEvent.GetId().Value). + WithError(err). + Error("cannot find environment for incoming executor failed event") + } + log.WithPrefix("scheduler"). + WithField("partition", envId.String()). + WithField("executorId", typedEvent.GetId().Value). + WithField("envState", env.CurrentState()). + Debug("received executor failed event") + } + + case *event.AgentFailedEvent: + envIdsAffected := tm.HandleAgentFailed(typedEvent) + for envId := range envIdsAffected { + env, err := instance.environment(envId) + if err != nil { + log.WithPrefix("scheduler"). + WithField("partition", envId.String()). + WithField("agentId", typedEvent.GetId().Value). + WithError(err). + Error("cannot find environment for incoming agent failed event") + } + log.WithPrefix("scheduler"). + WithField("partition", envId.String()). + WithField("agentId", typedEvent.GetId().Value). + WithField("envState", env.CurrentState()). + Debug("received agent failed event") + } + + case *event.TasksReleasedEvent: + // If we got a TasksReleasedEvent, it must be matched with a pending + // environment teardown. + + instance.mu.RLock() + thisEnvCh, ok := instance.pendingTeardownsCh[typedEvent.GetEnvironmentId()] + instance.mu.RUnlock() + + if ok { + thisEnvCh <- typedEvent + + instance.mu.Lock() + close(thisEnvCh) + delete(instance.pendingTeardownsCh, typedEvent.GetEnvironmentId()) + instance.mu.Unlock() + + } else { + // If there is no pending environment teardown, it means that the released task stopped + // unexpectedly. In that case, the environment should get torn-down only if the task + // is critical. + releaseCriticalTask := false + for _, v := range typedEvent.GetTaskIds() { + if tm.GetTask(v) != nil { + if tm.GetTask(v).GetTraits().Critical == true { + //|| tm.GetTask(v).GetParent().GetTaskTraits().Critical == true + releaseCriticalTask = true + } + } + } + if releaseCriticalTask { + thisEnvCh <- typedEvent + + instance.mu.Lock() + close(thisEnvCh) + delete(instance.pendingTeardownsCh, typedEvent.GetEnvironmentId()) + instance.mu.Unlock() + } + } + + case *event.TasksStateChangedEvent: + // If we got a TasksStateChangedEvent, it must be matched with a pending + // environment transition. + instance.mu.RLock() + thisEnvCh, ok := instance.pendingStateChangeCh[typedEvent.GetEnvironmentId()] + instance.mu.RUnlock() + // If environment is not in state transition message is being propagated through task/manager + if ok { + thisEnvCh <- typedEvent + } + default: + // noop + } + } + } + }() + return instance } -func (envs *Manager) CreateEnvironment(workflowPath string) (uuid.UUID, error) { - envs.mu.Lock() - defer envs.mu.Unlock() +func (envs *Manager) NotifyIntegratedServiceEvent(event event.IntegratedServiceEvent) { + envs.incomingEventCh <- event +} + +func (envs *Manager) GetActiveDetectors() system.IDMap { + envs.mu.RLock() + defer envs.mu.RUnlock() + + response := make(system.IDMap) + for _, env := range envs.m { + if env.workflow == nil { // we can only query for detectors post-workflow-load + continue + } + envDetectors := env.GetActiveDetectors() + for det := range envDetectors { + response[det] = struct{}{} + } + } + return response +} - env, err := newEnvironment() +func (envs *Manager) CreateEnvironment(workflowPath string, userVars map[string]string, public bool, newId uid.ID, autoTransition bool) (resultEnvId uid.ID, resultErr error) { + // Before we load the workflow, we get the list of currently active detectors. This query must be performed before + // loading the workflow in order to compare the currently used detectors with the detectors required by the newly + // created environment. + alreadyActiveDetectors := envs.GetActiveDetectors() + + lastRequestUser := &evpb.User{} + lastRequestUserJ, ok := userVars["last_request_user"] + if ok { + _ = json.Unmarshal([]byte(lastRequestUserJ), lastRequestUser) + } + + // CreateEnvironment() is not transition from state machine, so we need to emit the same message as in TryTransition + the.EventWriterWithTopic(topic.Environment).WriteEvent(&evpb.Ev_EnvironmentEvent{ + EnvironmentId: newId.String(), + State: "PENDING", + Transition: "CREATE", + TransitionStatus: evpb.OpStatus_STARTED, + LastRequestUser: lastRequestUser, + Message: "transition starting", + }) + + // report error of the CreateEnvironment() in the same way as in TryTransition + defer func() { + if resultErr != nil { + the.EventWriterWithTopic(topic.Environment).WriteEvent(&evpb.Ev_EnvironmentEvent{ + EnvironmentId: newId.String(), + Error: resultErr.Error(), + LastRequestUser: lastRequestUser, + Message: "transition error", + State: "PENDING", + Transition: "CREATE", + TransitionStatus: evpb.OpStatus_DONE_ERROR, + }) + } + }() + + // in case of err==nil, env will be false unless user + // set it to True which will be overwritten in server.go + workflowPublicInfo, err := parseWorkflowPublicInfo(workflowPath) if err != nil { - return uuid.NIL, err + log.WithField("public info", public). + WithField("workflow path", workflowPath). + WithError(err). + Warn("parse workflow public info failed.") + + resultEnvId = newId + resultErr = fmt.Errorf("workflow public info parsing failed: %w", err) + return } - env.workflow, err = envs.loadWorkflow(workflowPath, env.wfAdapter) + + the.EventWriterWithTopic(topic.Environment).WriteEvent(&evpb.Ev_EnvironmentEvent{ + EnvironmentId: newId.String(), + State: "PENDING", + Transition: "CREATE", + TransitionStep: "before_CREATE", + TransitionStatus: evpb.OpStatus_STARTED, + Message: "instantiating", + LastRequestUser: lastRequestUser, + WorkflowTemplateInfo: &evpb.WorkflowTemplateInfo{ + Path: workflowPath, + Public: workflowPublicInfo.IsPublic, + Name: workflowPublicInfo.Name, + Description: workflowPublicInfo.Description, + }, + }) + + // userVar identifiers come in 2 forms: + // environment user var: "someKey" + // workflow user var: "path.to.some.role:someKey" + // We need to split them into 2 structures, the first of which is passed to newEnvironment and the other one + // to loadWorkflow as its keys must be injected into one or more specific roles. + + envUserVars := make(map[string]string) + workflowUserVars := make(map[string]string) + for k, v := range userVars { + // If the key contains a ':', means we have a var associated with a specific workflow role + if strings.ContainsRune(k, task.TARGET_SEPARATOR_RUNE) { + workflowUserVars[k] = v + } else { + envUserVars[k] = v + } + } + + cleanedUpTasks, runningTasks, err := envs.taskman.Cleanup() if err != nil { - err = fmt.Errorf("cannot load workflow template: %s", err.Error()) - return env.id, err + log.WithError(err). + Warnf("pre-deployment cleanup failed, continuing anyway") + err = nil + } else { + cleanedUpTaskIds, runningTaskIds := make([]string, 0), make([]string, 0) + for _, t := range cleanedUpTasks { + cleanedUpTaskIds = append(cleanedUpTaskIds, fmt.Sprintf("%s.%s#%s", t.GetHostname(), t.GetClassName(), t.GetTaskId())) + } + for _, t := range runningTasks { + runningTaskIds = append(runningTaskIds, fmt.Sprintf("%s.%s#%s", t.GetHostname(), t.GetClassName(), t.GetTaskId())) + } + log.WithField("tasksCleanedUp", strings.Join(cleanedUpTaskIds, ", ")). + WithField("level", infologger.IL_Devel). + Debug("tasks cleaned up during pre-deployment cleanup") + log.WithField("tasksStillRunning", strings.Join(runningTaskIds, ", ")). + WithField("level", infologger.IL_Devel). + Debug("tasks still running after pre-deployment cleanup") + log.WithField("level", infologger.IL_Ops). + Infof("pre-deployment cleanup completed (%d tasks cleaned up, %d tasks still running)", len(cleanedUpTasks), len(runningTasks)) } - envs.m[env.id.Array()] = env + var env *Environment + env, err = newEnvironment(envUserVars, newId) + if public { + env.Public = true + } - err = env.TryTransition(NewConfigureTransition( - envs.taskman, - nil, //roles, - nil, - true )) + gotEnvId := uid.NilID() + if err == nil { + if env != nil { + gotEnvId = env.Id() + } else { + err = errors.New("newEnvironment returned nil environment") + log.WithError(err). + WithField("partition", newId.String()). + Logf(logrus.FatalLevel, "environment creation failed") + } + } + if err != nil || env == nil { + if env == nil { + err = errors.New("newEnvironment returned nil environment") + } + log.WithError(err). + WithField("partition", gotEnvId.String()). + Logf(logrus.FatalLevel, "environment creation failed") + resultEnvId = gotEnvId + resultErr = err + return + } + + log.WithFields(logrus.Fields{ + "workflow": workflowPath, + "partition": gotEnvId.String(), + }).Info("creating new environment") + + env.Public = workflowPublicInfo.IsPublic + env.name = workflowPublicInfo.Name + env.Description = workflowPublicInfo.Description + env.WorkflowPath = workflowPath + + the.EventWriterWithTopic(topic.Environment).WriteEvent(&evpb.Ev_EnvironmentEvent{ + EnvironmentId: newId.String(), + State: "PENDING", + Transition: "CREATE", + TransitionStep: "before_CREATE", + TransitionStatus: evpb.OpStatus_ONGOING, + Message: "running hooks", + LastRequestUser: lastRequestUser, + WorkflowTemplateInfo: env.GetWorkflowInfo(), + }) + + env.hookHandlerF = func(hooks task.Tasks) error { + return envs.taskman.TriggerHooks(gotEnvId, hooks) + } + + // Ensure the environment_id is available to all + env.UserVars.Set("environment_id", env.id.String()) + + the.EventWriterWithTopic(topic.Environment).WriteEvent(&evpb.Ev_EnvironmentEvent{ + EnvironmentId: newId.String(), + State: "PENDING", + Transition: "CREATE", + TransitionStep: "CREATE", + TransitionStatus: evpb.OpStatus_ONGOING, + Message: "loading workflow", + LastRequestUser: lastRequestUser, + WorkflowTemplateInfo: env.GetWorkflowInfo(), + }) + + // We load the workflow (includes template processing) + env.workflow, err = envs.loadWorkflow(workflowPath, env.wfAdapter, workflowUserVars, env.BaseConfigStack) if err != nil { - rlsErr := envs.taskman.ReleaseTasks(env.id.Array(), env.Workflow().GetTasks()) - if rlsErr != nil { - log.WithError(rlsErr).Warning("environment configure failed, some tasks could not be released") + err = fmt.Errorf("cannot load workflow template: %w", err) + + resultEnvId = env.id + resultErr = err + return + } + + // Ensure we provide a very defaulty `detectors` variable + detectors, err := the.ConfSvc().GetDetectorsForHosts(env.GetFLPs()) + if err != nil { + err = fmt.Errorf("cannot acquire detectors in loaded workflow template: %w", err) + + resultEnvId = env.id + resultErr = err + return + } + detectorsStr, err := SliceToJSONSlice(detectors) + if err != nil { + err = fmt.Errorf("cannot process detectors in loaded workflow template: %w", err) + + resultEnvId = env.id + resultErr = err + return + } + env.GlobalDefaults.Set("detectors", detectorsStr) + + log.WithFields(logrus.Fields{ + "partition": gotEnvId.String(), + }).Infof("detectors in environment: %s", strings.Join(detectors, " ")) + + // env.GetActiveDetectors() is valid starting now, so we can check for detector exclusion + neededDetectors := env.GetActiveDetectors() + for det := range neededDetectors { + if _, contains := alreadyActiveDetectors[det]; contains { + // required detector det is already active in some other environment + + resultEnvId = env.id + resultErr = fmt.Errorf("detector %s is already in use", det.String()) + return } + } + + cvs, _ := env.Workflow().ConsolidatedVarStack() + the.EventWriterWithTopic(topic.Environment).WriteEvent(&evpb.Ev_EnvironmentEvent{ + EnvironmentId: newId.String(), + State: env.CurrentState(), + Transition: "CREATE", + TransitionStep: "after_CREATE", + TransitionStatus: evpb.OpStatus_DONE_OK, + Message: "workflow loaded", + Vars: cvs, // we push the full var stack of the root role in the workflow loaded event + LastRequestUser: lastRequestUser, + WorkflowTemplateInfo: env.GetWorkflowInfo(), + }) - delete(envs.m, env.id.Array()) - return env.id, err + the.EventWriterWithTopic(topic.Environment).WriteEvent(&evpb.Ev_EnvironmentEvent{ + EnvironmentId: newId.String(), + LastRequestUser: lastRequestUser, + Message: "transition completed successfully", + State: env.CurrentState(), + Transition: "CREATE", + TransitionStatus: evpb.OpStatus_DONE_OK, + }) + + log.WithField("method", "CreateEnvironment"). + WithField("level", infologger.IL_Devel). + Debug("envman write lock") + envs.mu.Lock() + envs.m[env.id] = env + envs.pendingStateChangeCh[env.id] = env.stateChangedCh + envs.mu.Unlock() + log.WithField("method", "CreateEnvironment"). + WithField("level", infologger.IL_Devel). + Debug("envman write unlock") + + err = env.TryTransition(NewDeployTransition( + envs.taskman, + nil, // roles, + nil), + ) + + if err == nil { + err = env.TryTransition(NewConfigureTransition( + envs.taskman), + ) } + if err == nil { + // CONFIGURE transition successful! + env.subscribeToWfState(envs.taskman) + + if autoTransition { + // We now return the configured environment from CreateEnvironment, but if autoTransition is set to true + // then the auto-transitioner starts here. + // Sequence: + // * CONFIGURED×START_ACTIVITY→RUNNING + // * wait for RUNNING to finish as required by tasks (RUNNING×STOP_ACTIVITY→CONFIGURED) + // * safe DESTROY path + go func() { + defer env.unsubscribeFromWfState() + + goErrorKillDestroy := func(op string) { + envState := env.CurrentState() + log.WithField("state", envState). + WithField("partition", env.Id().String()). + WithError(err). + Errorf("auto-transitioning environment failed %s, cleanup in progress", op) + + the.EventWriterWithTopic(topic.Environment).WriteEvent( + NewEnvGoErrorEvent(env, fmt.Sprintf("%s failed: %v", op, err)), + ) + err := env.TryTransition(NewGoErrorTransition( + envs.taskman), + ) + if err != nil { + HandleFailedGoError(err, env) + } + + envTasks := env.Workflow().GetTasks() + // TeardownEnvironment manages the envs.mu internally + _ = envs.TeardownEnvironment(env.Id(), true /*force*/) + + killedTasks, _, rlsErr := envs.taskman.KillTasks(envTasks.GetTaskIds()) + if rlsErr != nil { + log.WithError(rlsErr).Warn("task teardown error") + } + log.WithFields(logrus.Fields{ + "killedCount": len(killedTasks), + "lastEnvState": envState, + "level": infologger.IL_Support, + "partition": env.Id().String(), + }). + Infof("auto-environment failed at %s, tasks were cleaned up", op) + log.WithField("partition", env.Id().String()).Info("environment teardown complete") + } + + // now we have the environment we should transition to start + trans := NewStartActivityTransition(envs.taskman) + if trans == nil { + goErrorKillDestroy("transition START_ACTIVITY") + return + } + + err = env.TryTransition(trans) + if err != nil { + goErrorKillDestroy("transition START_ACTIVITY") + return + } + + for { + // we know we performed START_ACTIVITY, so we poll at 1Hz for the run to finish + time.Sleep(1 * time.Second) + + if env == nil { + // must've died during the loop + return + } + + envState := env.CurrentState() + switch envState { + case "CONFIGURED": + // RUN finished so we can reset and delete the environment + err = env.TryTransition(NewResetTransition(envs.taskman)) + if err != nil { + goErrorKillDestroy("transition RESET") + return + } + + err = envs.TeardownEnvironment(env.id, false) + if err != nil { + goErrorKillDestroy("teardown") + return + } + tasksForEnv := env.Workflow().GetTasks().GetTaskIds() + _, _, err = envs.taskman.KillTasks(tasksForEnv) + if err != nil { + return + } + + return + case "ERROR": + fallthrough + case "STANDBY": + fallthrough + case "DEPLOYED": + goErrorKillDestroy("transition STOP_ACTIVITY") + return + case "MIXED": + continue + case "": + continue + } + } + }() + } + + resultEnvId = env.id + resultErr = err + return + } + + // Deployment/configuration failure code path starts here + + envState := env.CurrentState() + log.WithField("partition", env.Id().String()). + Errorf("environment deployment and configuration failed (%s)", workflowPath) + log.WithField("state", envState). + WithField("partition", env.Id().String()). + WithError(err). + WithField("level", infologger.IL_Devel). + Error("environment deployment and configuration error, cleanup in progress") + + the.EventWriterWithTopic(topic.Environment).WriteEvent( + NewEnvGoErrorEvent(env, fmt.Sprintf("deployment or configuration failed: %v", err)), + ) + errTxErr := env.TryTransition(NewGoErrorTransition( + envs.taskman), + ) + if errTxErr != nil { + HandleFailedGoError(errTxErr, env) + } + envTasks := env.Workflow().GetTasks() + // TeardownEnvironment manages the envs.mu internally + // We do not get the error here cause it overwrites the failed deployment error + // with which results to server.go to report back + // cannot get newly created environment: no environment with id + _ = envs.TeardownEnvironment(env.Id(), true /*force*/) + + killedTasks, _, rlsErr := envs.taskman.KillTasks(envTasks.GetTaskIds()) + if rlsErr != nil { + log.WithError(rlsErr).Warn("task teardown error") + } + log.WithFields(logrus.Fields{ + "killedCount": len(killedTasks), + "lastEnvState": envState, + "level": infologger.IL_Support, + "partition": env.Id().String(), + }). + Info("environment deployment failed, tasks were cleaned up") + log.WithField("partition", env.Id().String()).Info("environment teardown complete") + return env.id, err } -func (envs *Manager) TeardownEnvironment(environmentId uuid.UUID) error { - envs.mu.Lock() - defer envs.mu.Unlock() +func (envs *Manager) TeardownEnvironment(environmentId uid.ID, force bool) error { + log.WithFields(logrus.Fields{ + "partition": environmentId.String(), + }).Info("tearing down environment") + envs.mu.RLock() env, err := envs.environment(environmentId) + envs.mu.RUnlock() + if err != nil { return err } - if env.CurrentState() != "STANDBY" { + if !env.transitionMutex.TryLock() { + log.WithField("partition", environmentId.String()). + Warnf("environment teardown attempt delayed: transition '%s' in progress. waiting for completion or failure", env.currentTransition) + env.transitionMutex.Lock() + log.WithField("level", infologger.IL_Support). + WithField("partition", environmentId.String()). + Infof("environment teardown attempt resumed") + } + defer env.transitionMutex.Unlock() + + if env.CurrentState() == "DONE" { + return errors.New("attempting to teardown an environment which is already in DONE, doing nothing") + } + if env.CurrentState() != "STANDBY" && env.CurrentState() != "DEPLOYED" && !force { return errors.New(fmt.Sprintf("cannot teardown environment in state %s", env.CurrentState())) } - err = envs.taskman.ReleaseTasks(environmentId.Array(), env.Workflow().GetTasks()) + the.EventWriterWithTopic(topic.Environment).WriteEvent(&evpb.Ev_EnvironmentEvent{ + EnvironmentId: environmentId.String(), + State: env.CurrentState(), + Transition: "DESTROY", + TransitionStep: "before_DESTROY", + TransitionStatus: evpb.OpStatus_STARTED, + Message: "workflow teardown started", + LastRequestUser: env.GetLastRequestUser(), + WorkflowTemplateInfo: env.GetWorkflowInfo(), + }) + + env.Mu.Lock() + env.currentTransition = "DESTROY" + env.Mu.Unlock() + + the.EventWriterWithTopic(topic.Environment).WriteEvent(&evpb.Ev_EnvironmentEvent{ + EnvironmentId: environmentId.String(), + State: env.CurrentState(), + Transition: "DESTROY", + TransitionStep: "leave_" + env.CurrentState(), + TransitionStatus: evpb.OpStatus_ONGOING, + Message: "workflow teardown ongoing", + LastRequestUser: env.GetLastRequestUser(), + WorkflowTemplateInfo: env.GetWorkflowInfo(), + }) + + err = env.handleAllHooks(env.Workflow(), "leave_"+env.CurrentState()) if err != nil { + log.WithFields(logrus.Fields{ + "partition": environmentId.String(), + }).Error(fmt.Errorf("could not handle hooks for the trigger leave_%s, error: %w", env.CurrentState(), err)) + } + + if env.CurrentState() == "RUNNING" { + endTime, ok := env.workflow.GetUserVars().Get("run_end_time_ms") + if ok && endTime == "" { + runEndTime := time.Now() + runEndTimeS := strconv.FormatInt(runEndTime.UnixMilli(), 10) + env.workflow.SetRuntimeVar("run_end_time_ms", runEndTimeS) + + the.EventWriterWithTopic(topic.Run).WriteEventWithTimestamp(&evpb.Ev_RunEvent{ + EnvironmentId: environmentId.String(), + RunNumber: env.GetCurrentRunNumber(), + State: env.Sm.Current(), + Error: "", + Transition: "TEARDOWN", + TransitionStatus: evpb.OpStatus_STARTED, + }, runEndTime) + } else { + log.WithField("partition", environmentId.String()). + Debug("O2 End time already set before DESTROY") + } + + endCompletionTime, ok := env.workflow.GetUserVars().Get("run_end_completion_time_ms") + if ok && endCompletionTime == "" { + runEndCompletionTime := time.Now() + runEndCompletionTimeS := strconv.FormatInt(runEndCompletionTime.UnixMilli(), 10) + env.workflow.SetRuntimeVar("run_end_completion_time_ms", runEndCompletionTimeS) + + the.EventWriterWithTopic(topic.Run).WriteEventWithTimestamp(&evpb.Ev_RunEvent{ + EnvironmentId: environmentId.String(), + RunNumber: env.GetCurrentRunNumber(), + State: env.Sm.Current(), + Error: "", + Transition: "TEARDOWN", + TransitionStatus: evpb.OpStatus_STARTED, + }, runEndCompletionTime) + } else { + log.WithField("partition", environmentId.String()). + Debug("O2 End Completion time already set before DESTROY") + } + } + + tasksToRelease := env.Workflow().GetTasks() + + // we gather all DESTROY/after_DESTROY hooks, as these require special treatment + hooksMapForDestroy := env.Workflow().GetHooksMapForTrigger("DESTROY") + for k, v := range env.Workflow().GetHooksMapForTrigger("after_DESTROY") { + hooksMapForDestroy[k] = v + } + + allWeights := hooksMapForDestroy.GetWeights() + + // for each weight within DESTROY + // for each found DESTROY hook, + // for each of *all* tasks last-to-first + // if the pointed task is one of the known cleanup hooks, remove it + for _, weight := range allWeights { + hooksForWeight, ok := hooksMapForDestroy[weight] + if ok { + for _, hook := range hooksForWeight { + for i := len(tasksToRelease) - 1; i >= 0; i-- { + if hook == tasksToRelease[i] { + tasksToRelease = append(tasksToRelease[:i], tasksToRelease[i+1:]...) + } + } + } + } + } + + the.EventWriterWithTopic(topic.Environment).WriteEvent(&evpb.Ev_EnvironmentEvent{ + EnvironmentId: environmentId.String(), + State: env.CurrentState(), + Transition: "DESTROY", + TransitionStep: "DESTROY", + TransitionStatus: evpb.OpStatus_ONGOING, + Message: "releasing tasks", + LastRequestUser: env.GetLastRequestUser(), + WorkflowTemplateInfo: env.GetWorkflowInfo(), + }) + + log.WithField("method", "TeardownEnvironment"). + WithField("level", infologger.IL_Devel). + Debug("envman write lock") + envs.mu.Lock() + // we kill all tasks that aren't cleanup hooks + taskmanMessage := task.NewEnvironmentMessage(taskop.ReleaseTasks, environmentId, tasksToRelease, nil) + // close state channel + if ch := envs.pendingStateChangeCh[environmentId]; ch != nil { + close(envs.pendingStateChangeCh[environmentId]) + } + delete(envs.pendingStateChangeCh, environmentId) + envs.mu.Unlock() + log.WithField("method", "TeardownEnvironment"). + WithField("level", infologger.IL_Devel). + Debug("envman write lock") + + // We set all callRoles to INACTIVE right now, because there's no task activation for them. + // This is the callRole equivalent of AcquireTasks, which only pushes updates to taskRoles. + allHooks := env.Workflow().GetAllHooks() + callHooks := allHooks.FilterCalls() // get the calls + if len(callHooks) > 0 { + for _, h := range callHooks { + pr, ok := h.GetParentRole().(workflow.PublicUpdatable) + if !ok { + continue + } + go pr.UpdateStatus(task.INACTIVE) + } + } + + pendingCh := make(chan *event.TasksReleasedEvent) + log.WithField("method", "TeardownEnvironment"). + WithField("level", infologger.IL_Devel). + Debug("envman write lock") + envs.mu.Lock() + envs.pendingTeardownsCh[environmentId] = pendingCh + envs.mu.Unlock() + log.WithField("method", "TeardownEnvironment"). + WithField("level", infologger.IL_Devel). + Debug("envman write lock") + + envs.taskman.MessageChannel <- taskmanMessage + + incomingEv := <-pendingCh + + // If some tasks failed to release + if taskReleaseErrors := incomingEv.GetTaskReleaseErrors(); len(taskReleaseErrors) > 0 { + for taskId, err := range taskReleaseErrors { + log.WithFields(logrus.Fields{ + "taskId": taskId, + "partition": environmentId, + }). + WithError(err). + Warn("task failed to release") + } + err = fmt.Errorf("%d tasks failed to release for environment %s", + len(taskReleaseErrors), environmentId) + + the.EventWriterWithTopic(topic.Environment).WriteEvent(&evpb.Ev_EnvironmentEvent{ + EnvironmentId: environmentId.String(), + State: "DONE", + Transition: "DESTROY", + TransitionStep: "after_DESTROY", + TransitionStatus: evpb.OpStatus_DONE_ERROR, + Message: "environment teardown finished with error", + Error: err.Error(), + LastRequestUser: env.GetLastRequestUser(), + WorkflowTemplateInfo: env.GetWorkflowInfo(), + }) + + return err + } + + the.EventWriterWithTopic(topic.Environment).WriteEvent(&evpb.Ev_EnvironmentEvent{ + EnvironmentId: environmentId.String(), + State: env.CurrentState(), + Transition: "DESTROY", + TransitionStep: "after_DESTROY", + TransitionStatus: evpb.OpStatus_ONGOING, + Message: "running DESTROY hooks", + LastRequestUser: env.GetLastRequestUser(), + WorkflowTemplateInfo: env.GetWorkflowInfo(), + }) + + // we trigger all cleanup hooks, first calls, then tasks immediately after + for _, weight := range allWeights { + hooksForWeight, ok := hooksMapForDestroy[weight] + if ok { + hooksForWeight.FilterCalls().CallAll() + + // calls done, we start the task hooks... + cleanupTaskHooks := hooksForWeight.FilterTasks() + + // ...but only if their parent role is still ACTIVE (i.e. not killed or executor failed) + cleanupTaskHooks = cleanupTaskHooks.Filtered(func(t *task.Task) bool { + if pr, prOk := t.GetParentRole().(workflow.Role); prOk { + return pr.GetStatus() == task.ACTIVE + } + return false + }) + err = envs.taskman.TriggerHooks(environmentId, cleanupTaskHooks) + if err != nil { + log.WithField("partition", environmentId.String()). + WithError(err). + Warn("environment post-destroy hooks failed") + } + + // and then we kill them too + taskmanMessage = task.NewEnvironmentMessage(taskop.ReleaseTasks, environmentId, cleanupTaskHooks, nil) + } + } + + envs.cancelCallsPendingAwait(env) + + // we remake the pending teardown channel too, because each completed TasksReleasedEvent + // automatically closes it + pendingCh = make(chan *event.TasksReleasedEvent) + log.WithField("method", "TeardownEnvironment"). + WithField("level", infologger.IL_Devel). + Debug("envman write lock") + envs.mu.Lock() + envs.pendingTeardownsCh[environmentId] = pendingCh + envs.mu.Unlock() + log.WithField("method", "TeardownEnvironment"). + WithField("level", infologger.IL_Devel). + Debug("envman write lock") + + envs.taskman.MessageChannel <- taskmanMessage + + incomingEv = <-pendingCh + + // If some cleanup hooks failed to release + if taskReleaseErrors := incomingEv.GetTaskReleaseErrors(); len(taskReleaseErrors) > 0 { + for taskId, err := range taskReleaseErrors { + log.WithFields(logrus.Fields{ + "taskId": taskId, + "partition": environmentId, + }). + WithError(err). + Warn("task failed to release") + } + err = fmt.Errorf("%d tasks failed to release for environment %s", + len(taskReleaseErrors), environmentId) + + the.EventWriterWithTopic(topic.Environment).WriteEvent(&evpb.Ev_EnvironmentEvent{ + EnvironmentId: environmentId.String(), + State: "DONE", + Transition: "DESTROY", + TransitionStep: "after_DESTROY", + TransitionStatus: evpb.OpStatus_DONE_ERROR, + Message: "environment teardown finished with error", + Error: err.Error(), + LastRequestUser: env.GetLastRequestUser(), + WorkflowTemplateInfo: env.GetWorkflowInfo(), + }) + return err } - delete(envs.m, environmentId.Array()) + env.setState("DONE") + env.sendEnvironmentEvent(&event.EnvironmentEvent{EnvironmentID: env.Id().String(), Message: "teardown complete", State: "DONE"}) + + log.WithField("method", "TeardownEnvironment"). + WithField("level", infologger.IL_Devel). + Debug("envman write lock") + envs.mu.Lock() + defer envs.mu.Unlock() + delete(envs.m, environmentId) + env.unsubscribeFromWfState() + log.WithField("method", "TeardownEnvironment"). + WithField("level", infologger.IL_Devel). + Debug("envman write lock") + + the.EventWriterWithTopic(topic.Environment).WriteEvent(&evpb.Ev_EnvironmentEvent{ + EnvironmentId: environmentId.String(), + State: "DONE", + Transition: "DESTROY", + TransitionStep: "after_DESTROY", + TransitionStatus: evpb.OpStatus_DONE_OK, + Message: "environment teardown complete", + LastRequestUser: env.GetLastRequestUser(), + WorkflowTemplateInfo: env.GetWorkflowInfo(), + }) + + log.WithFields(logrus.Fields{ + "partition": environmentId.String(), + infologger.Level: infologger.IL_Ops, + }).Info("environment teardown complete") return err } +func (envs *Manager) cancelCallsPendingAwait(env *Environment) { + // unblock all calls which are stuck waiting for an await trigger which never happened + if env == nil { + return + } + for _, callMapForAwait := range env.callsPendingAwait { + for _, callsForWeight := range callMapForAwait { + for _, call := range callsForWeight { + if call != nil { + call.Cancel() + } + } + } + } +} + /*func (envs *Manager) Configuration(environmentId uuid.UUID) EnvironmentCfg { envs.mu.RLock() defer envs.mu.RUnlock() return envs.m[environmentId.Array()].cfg }*/ -func (envs *Manager) Ids() (keys []uuid.UUID) { +func (envs *Manager) Ids() (keys []uid.ID) { envs.mu.RLock() defer envs.mu.RUnlock() - keys = make([]uuid.UUID, len(envs.m)) + keys = make([]uid.ID, len(envs.m)) i := 0 for k := range envs.m { - keys[i] = k.UUID() + keys[i] = k i++ } return } -func (envs *Manager) Environment(environmentId uuid.UUID) (env *Environment, err error) { - envs.mu.RLock() - defer envs.mu.RUnlock() +func (envs *Manager) Environment(environmentId uid.ID) (env *Environment, err error) { return envs.environment(environmentId) } -func (envs *Manager) environment(environmentId uuid.UUID) (env *Environment, err error) { - if len(environmentId.String()) == 0 { // invalid uuid - return nil, fmt.Errorf("invalid uuid: %s", environmentId) +func (envs *Manager) environment(environmentId uid.ID) (env *Environment, err error) { + if len(environmentId) == 0 { // invalid id + return nil, fmt.Errorf("empty env ID") } - env, ok := envs.m[environmentId.Array()] + envs.mu.RLock() + defer envs.mu.RUnlock() + env, ok := envs.m[environmentId] if !ok { err = errors.New(fmt.Sprintf("no environment with id %s", environmentId)) } return } -func (envs *Manager) loadWorkflow(workflowPath string, parent workflow.Updatable) (root workflow.Role, err error) { +func (envs *Manager) loadWorkflow(workflowPath string, parent workflow.Updatable, workflowUserVars map[string]string, baseConfigStack map[string]string) (root workflow.Role, err error) { if strings.Contains(workflowPath, "://") { return nil, errors.New("workflow loading from file not implemented yet") } - return workflow.Load(the.ConfSvc().GetROSource(), workflowPath, parent, envs.taskman) + return workflow.Load(workflowPath, parent, envs.taskman, workflowUserVars, baseConfigStack) +} + +func (envs *Manager) handleIntegratedServiceEvent(evt event.IntegratedServiceEvent) { + if evt == nil { + log.Error("cannot handle null IntegratedServiceEvent") + return + } + + // for now we only handle ODC events + if evt.GetServiceName() == "ODC" { + if odcEvent, ok := evt.(*event2.OdcPartitionStateChangeEvent); ok && odcEvent.GetState() == "ERROR" { + envId := odcEvent.GetEnvironmentId() + env, err := envs.environment(envId) + if err != nil { + log.WithPrefix("scheduler"). + WithField("partition", envId.String()). + WithField("odcState", odcEvent.GetState()). + WithError(err). + Error("cannot find environment for OdcPartitionStateChangeEvent") + } else { + log.WithPrefix("scheduler"). + WithField("partition", envId.String()). + WithField("odcState", odcEvent.GetState()). + WithField("envState", env.CurrentState()). + Debug("received ODC_PARTITION_STATE_CHANGE event from ODC, trying to stop the run") + if env.CurrentState() == "RUNNING" { + go func() { + log.WithPrefix("scheduler"). + WithField("partition", envId.String()). + Log(logrus.FatalLevel, "ODC partition state changed to ERROR, the current run will be stopped") + + err = env.TryTransition(NewStopActivityTransition(envs.taskman)) + if err != nil { + log.WithPrefix("scheduler"). + WithField("partition", envId.String()). + WithError(err). + Error("cannot stop run after ODC_PARTITION_STATE_CHANGE ERROR event") + } + + if env.CurrentState() != "ERROR" { + the.EventWriterWithTopic(topic.Environment).WriteEvent( + NewEnvGoErrorEvent(env, "ODC partition went to ERROR during RUNNING"), + ) + err = env.TryTransition(NewGoErrorTransition(envs.taskman)) + if err != nil { + HandleFailedGoError(err, env) + } + } + }() + } + } + } + } else if evt.GetServiceName() == "LHC" { + envs.handleLhcEvents(evt) + } +} + +func (envs *Manager) handleLhcEvents(evt event.IntegratedServiceEvent) { + + lhcEvent, ok := evt.(*lhcevent.LhcStateChangeEvent) + if !ok { + return + } + + // stop all relevant environments when beams are dumped + beamMode := lhcEvent.GetBeamInfo().BeamMode + beamsDumped := beamMode == evpb.BeamMode_BEAM_DUMP || beamMode == evpb.BeamMode_LOST_BEAMS || beamMode == evpb.BeamMode_NO_BEAM + if !beamsDumped { + return + } + + for envId, env := range envs.m { + shouldStopAtBeamDump, _ := strconv.ParseBool(env.GetKV("", "stop_at_beam_dump")) + if shouldStopAtBeamDump && env.CurrentState() == "RUNNING" { + if currentTransition := env.CurrentTransition(); currentTransition != "" { + log.WithPrefix("scheduler"). + WithField(infologger.Level, infologger.IL_Support). + WithField("partition", envId.String()). + WithField("run", env.currentRunNumber). + Infof("run was supposed to be stopped at beam dump, but transition '%s' is already in progress, skipping (probably the operator was faster)", currentTransition) + continue + } + + go func(env *Environment) { + log.WithPrefix("scheduler"). + WithField(infologger.Level, infologger.IL_Ops). + WithField("partition", envId.String()). + WithField("run", env.currentRunNumber). + Info("stopping the run due to beam dump") + + env.SetLastRequestUser(evpb.SpecialUser(evpb.SpecialUserId_LHC)) + err := env.TryTransition(NewStopActivityTransition(envs.taskman)) + if err != nil { + log.WithPrefix("scheduler"). + WithField("partition", envId.String()). + WithField("run", env.currentRunNumber). + WithError(err). + Error("could not stop the run upon beam dump") + + if env.CurrentState() != "ERROR" { + err = env.TryTransition(NewGoErrorTransition(envs.taskman)) + if err != nil { + HandleFailedGoError(err, env) + } + } + } + }(env) + } + } +} + +func (envs *Manager) handleDeviceEvent(evt event.DeviceEvent) { + if evt == nil { + log.Error("cannot handle null DeviceEvent") + return + } + + envId := common.GetEnvironmentIdFromLabelerType(evt) + + switch evt.GetType() { + case pb.DeviceEventType_BASIC_TASK_TERMINATED: + if btt, ok := evt.(*event.BasicTaskTerminated); ok { + log.WithPrefix("scheduler"). + WithFields(logrus.Fields{ + "exitCode": btt.ExitCode, + "stdout": btt.Stdout, + "stderr": btt.Stderr, + "finalMesosState": btt.FinalMesosState.String(), + "level": infologger.IL_Devel, + "partition": envId.String(), + }). + Debug("basic task terminated") + + // Propagate this information to the task/role + taskId := evt.GetOrigin().TaskId + t := envs.taskman.GetTask(taskId.Value) + isHook := false + if t != nil { + t.SendEvent(&event.TaskEvent{Name: t.GetName(), TaskID: taskId.Value, Status: btt.FinalMesosState.String(), Hostname: t.GetHostname(), ClassName: t.GetClassName()}) + if parentRole, ok := t.GetParentRole().(workflow.Role); ok { + parentRole.SetRuntimeVars(map[string]string{ + "taskResult.exitCode": strconv.Itoa(btt.ExitCode), + "taskResult.stdout": btt.Stdout, + "taskResult.stderr": btt.Stderr, + "taskResult.finalStatus": btt.FinalMesosState.String(), + "taskResult.timestamp": utils.NewUnixTimestamp(), + }) + + // If it's an update following a HOOK execution + if t.GetControlMode() == controlmode.HOOK { + isHook = true + env, err := envs.environment(t.GetEnvironmentId()) + if err != nil { + log.WithPrefix("scheduler"). + WithField(infologger.Level, infologger.IL_Devel). + WithError(err). + Error("cannot find environment for DeviceEvent") + } + if env != nil { + env.NotifyEvent(evt) + } + } + } else { + // Task has no parent role - this can happen during environment teardown + // when tasks are released before termination events are processed + log.WithPrefix("scheduler"). + WithField("partition", envId.String()). + WithField("taskId", taskId.Value). + WithField(infologger.Level, infologger.IL_Devel). + Debug("DeviceEvent BASIC_TASK_TERMINATED received for task with no parent role, likely due to environment teardown") + } + } else { + log.WithPrefix("scheduler"). + WithField("partition", envId.String()). + WithField(infologger.Level, infologger.IL_Devel). + Debug("cannot find task for DeviceEvent BASIC_TASK_TERMINATED") + } + + // If the task hasn't already been killed + // AND it's not a hook + if !isHook { + goto doFallthrough + } + } + return + doFallthrough: + fallthrough + case pb.DeviceEventType_END_OF_STREAM: + taskId := evt.GetOrigin().TaskId + t := envs.taskman.GetTask(taskId.Value) + if t == nil { + log.WithPrefix("scheduler"). + WithField("partition", envId.String()). + WithField("taskId", taskId.Value). + WithField(infologger.Level, infologger.IL_Devel). + Debug("cannot find task for DeviceEvent END_OF_STREAM") + return + } + env, err := envs.environment(t.GetEnvironmentId()) + if err != nil { + log.WithPrefix("scheduler"). + WithField("partition", envId.String()). + WithField("taskId", taskId.Value). + WithField(infologger.Level, infologger.IL_Devel). + WithError(err). + Error("cannot find environment for DeviceEvent") + } else { + log.WithPrefix("scheduler"). + WithField("partition", envId.String()). + WithField("taskId", taskId.Value). + WithField("role", t.GetParent().GetName()). + WithField("envState", env.CurrentState()). + Debug("received END_OF_STREAM event from task, trying to stop the run") + if env.CurrentState() == "RUNNING" { + t.SetSafeToStop(true) // we mark this specific task as ok to STOP + go func() { + if env.IsSafeToStop() { // but then we ask the env whether *all* of them are + err = env.TryTransition(NewStopActivityTransition(envs.taskman)) + if err != nil { + log.WithPrefix("scheduler"). + WithField("partition", envId.String()). + WithError(err). + Error("cannot stop run after END_OF_STREAM event") + } + } + }() + } + } + + case pb.DeviceEventType_TASK_INTERNAL_ERROR: + // a task has already internally transitioned to ERROR state + taskId := evt.GetOrigin().TaskId + t := envs.taskman.GetTask(taskId.Value) + if t == nil { + log.WithPrefix("scheduler"). + WithField("partition", envId.String()). + WithField("taskId", taskId.Value). + Debug("cannot find task for DeviceEvent TASK_INTERNAL_ERROR") + return + } + env, err := envs.environment(t.GetEnvironmentId()) + if err != nil { + log.WithPrefix("scheduler"). + WithField("partition", envId.String()). + WithField("taskId", taskId.Value). + WithField(infologger.Level, infologger.IL_Devel). + WithError(err). + Error("cannot find environment for DeviceEvent") + } else { + log.WithPrefix("scheduler"). + WithField("partition", envId.String()). + WithField("taskId", taskId.Value). + WithField("taskRole", t.GetParentRolePath()). + WithField("envState", env.CurrentState()). + WithField(infologger.Level, infologger.IL_Support). + Debug("received TASK_INTERNAL_ERROR event from task, trying to stop the run") + if env.CurrentState() == "RUNNING" { + go func() { + t.GetParent().UpdateState(sm.ERROR) + err = env.TryTransition(NewStopActivityTransition(envs.taskman)) + if err != nil { + log.WithPrefix("scheduler"). + WithField("partition", envId.String()). + WithError(err). + Error("cannot stop run after END_OF_STREAM event") + } + }() + } + } + + } +} + +// FIXME: this function should be deduplicated with CreateEnvironment so detector resource matching works correctly +func (envs *Manager) CreateAutoEnvironment(workflowPath string, userVars map[string]string, newId uid.ID, sub Subscription) { + envUserVars := make(map[string]string) + workflowUserVars := make(map[string]string) + for k, v := range userVars { + if strings.ContainsRune(k, task.TARGET_SEPARATOR_RUNE) { + workflowUserVars[k] = v + } else { + envUserVars[k] = v + } + } + + lastRequestUser := &evpb.User{} + lastRequestUserJ, ok := userVars["last_request_user"] + if ok { + _ = json.Unmarshal([]byte(lastRequestUserJ), lastRequestUser) + } + + // in case of err==nil, env will be false unless user + // set it to True which will be overwritten in server.go + workflowPublicInfo, err := parseWorkflowPublicInfo(workflowPath) + if err != nil { + log.WithField("public info", workflowPublicInfo.IsPublic). + WithField("environment", newId.String()). + WithError(err). + Warn("parse workflow public info failed.") + } + + the.EventWriterWithTopic(topic.Environment).WriteEvent(&evpb.Ev_EnvironmentEvent{ + EnvironmentId: newId.String(), + State: "PENDING", + Transition: "CREATE", + TransitionStep: "before_CREATE", + TransitionStatus: evpb.OpStatus_STARTED, + Message: "instantiating", + LastRequestUser: lastRequestUser, + WorkflowTemplateInfo: &evpb.WorkflowTemplateInfo{ + Path: workflowPath, + Public: workflowPublicInfo.IsPublic, + Name: workflowPublicInfo.Name, + Description: workflowPublicInfo.Description, + }, + }) + + env, err := newEnvironment(envUserVars, newId) + newEnvId := uid.NilID() + if err == nil && env != nil { + newEnvId = env.Id() + } else { + log.WithError(err).Logf(logrus.FatalLevel, "environment creation failed") + env.sendEnvironmentEvent(&event.EnvironmentEvent{EnvironmentID: newEnvId.String(), Error: err}) + the.EventWriterWithTopic(topic.Environment).WriteEvent(&evpb.Ev_EnvironmentEvent{ + EnvironmentId: newId.String(), + State: "ERROR", + Error: err.Error(), + Message: "cannot create new environment", // GUI listens for this concrete string + LastRequestUser: lastRequestUser, + WorkflowTemplateInfo: &evpb.WorkflowTemplateInfo{ + Path: workflowPath, + Public: workflowPublicInfo.IsPublic, + Name: workflowPublicInfo.Name, + Description: workflowPublicInfo.Description, + }, + }) + return + } + + log.WithFields(logrus.Fields{ + "workflow": workflowPath, + "partition": newEnvId.String(), + }).Info("creating new automatic environment") + + env.Public = workflowPublicInfo.IsPublic + env.name = workflowPublicInfo.Name + env.Description = workflowPublicInfo.Description + env.WorkflowPath = workflowPath + + the.EventWriterWithTopic(topic.Environment).WriteEvent(&evpb.Ev_EnvironmentEvent{ + EnvironmentId: newId.String(), + State: "PENDING", + Transition: "CREATE", + TransitionStep: "before_CREATE", + TransitionStatus: evpb.OpStatus_ONGOING, + Message: "running hooks", + LastRequestUser: lastRequestUser, + WorkflowTemplateInfo: env.GetWorkflowInfo(), + }) + + env.addSubscription(sub) + defer env.closeStream() + + env.hookHandlerF = func(hooks task.Tasks) error { + return envs.taskman.TriggerHooks(newEnvId, hooks) + } + + // Ensure the environment_id is available to all + env.UserVars.Set("environment_id", env.id.String()) + + the.EventWriterWithTopic(topic.Environment).WriteEvent(&evpb.Ev_EnvironmentEvent{ + EnvironmentId: newId.String(), + State: "PENDING", + Transition: "CREATE", + TransitionStep: "CREATE", + TransitionStatus: evpb.OpStatus_ONGOING, + Message: "loading workflow", + LastRequestUser: lastRequestUser, + WorkflowTemplateInfo: env.GetWorkflowInfo(), + }) + + env.workflow, err = envs.loadWorkflow(workflowPath, env.wfAdapter, workflowUserVars, env.BaseConfigStack) + if err != nil { + err = fmt.Errorf("cannot load workflow template: %w", err) + env.sendEnvironmentEvent(&event.EnvironmentEvent{EnvironmentID: env.Id().String(), Error: err}) + + log.WithError(err).Error("failed to load workflow") + + the.EventWriterWithTopic(topic.Environment).WriteEvent(&evpb.Ev_EnvironmentEvent{ + EnvironmentId: newId.String(), + State: "ERROR", + Error: err.Error(), + Message: "cannot load workflow", // GUI listens for this concrete string + LastRequestUser: lastRequestUser, + WorkflowTemplateInfo: &evpb.WorkflowTemplateInfo{ + Path: workflowPath, + Public: workflowPublicInfo.IsPublic, + Name: workflowPublicInfo.Name, + Description: workflowPublicInfo.Description, + }, + }) + + return + } + + cvs, _ := env.Workflow().ConsolidatedVarStack() + the.EventWriterWithTopic(topic.Environment).WriteEvent(&evpb.Ev_EnvironmentEvent{ + EnvironmentId: newId.String(), + State: env.CurrentState(), + Transition: "CREATE", + TransitionStep: "after_CREATE", + TransitionStatus: evpb.OpStatus_DONE_OK, + Message: "workflow loaded", + Vars: cvs, // we push the full var stack of the root role in the workflow loaded event + LastRequestUser: lastRequestUser, + WorkflowTemplateInfo: env.GetWorkflowInfo(), + }) + + log.WithField("method", "CreateAutoEnvironment"). + WithField("level", infologger.IL_Devel). + Debug("envman write lock") + envs.mu.Lock() + envs.m[env.id] = env + envs.pendingStateChangeCh[env.id] = env.stateChangedCh + envs.mu.Unlock() + log.WithField("method", "CreateAutoEnvironment"). + WithField("level", infologger.IL_Devel). + Debug("envman write unlock") + + err = env.TryTransition(NewDeployTransition( + envs.taskman, + nil, // roles, + nil), + ) + if err == nil { + err = env.TryTransition(NewConfigureTransition( + envs.taskman), + ) + } + + goErrorKillDestroy := func(op string) { + envState := env.CurrentState() + env.sendEnvironmentEvent(&event.EnvironmentEvent{Message: fmt.Sprintf("environment is in %s, handling ERROR", envState), EnvironmentID: env.Id().String(), Error: err}) + log.WithField("state", envState). + WithField("partition", env.Id().String()). + WithError(err). + Errorf("auto-transitioning environment failed %s, cleanup in progress", op) + + the.EventWriterWithTopic(topic.Environment).WriteEvent( + NewEnvGoErrorEvent(env, fmt.Sprintf("%s failed: %v", op, err)), + ) + err := env.TryTransition(NewGoErrorTransition( + envs.taskman), + ) + if err != nil { + HandleFailedGoError(err, env) + env.sendEnvironmentEvent(&event.EnvironmentEvent{Message: "transition ERROR failed, forcing", EnvironmentID: env.Id().String(), Error: err}) + } + + envTasks := env.Workflow().GetTasks() + // TeardownEnvironment manages the envs.mu internally + err = envs.TeardownEnvironment(env.Id(), true /*force*/) + if err != nil { + env.sendEnvironmentEvent(&event.EnvironmentEvent{Message: "force teardown failed, handling", EnvironmentID: env.Id().String(), Error: err}) + } + + killedTasks, _, rlsErr := envs.taskman.KillTasks(envTasks.GetTaskIds()) + if rlsErr != nil { + log.WithError(rlsErr).Warn("task teardown error") + } + log.WithFields(logrus.Fields{ + "killedCount": len(killedTasks), + "lastEnvState": envState, + "level": infologger.IL_Support, + "partition": env.Id().String(), + }). + Infof("auto-environment failed at %s, tasks were cleaned up", op) + log.WithField("partition", env.Id().String()).Info("environment teardown complete") + + env.sendEnvironmentEvent(&event.EnvironmentEvent{Message: "environment teardown complete", State: "DONE", EnvironmentID: env.Id().String()}) + } + + if err != nil { + goErrorKillDestroy("transition CONFIGURE") + + return + } + + env.subscribeToWfState(envs.taskman) + defer env.unsubscribeFromWfState() + + // now we have the environment we should transition to start + trans := NewStartActivityTransition(envs.taskman) + if trans == nil { + env.sendEnvironmentEvent(&event.EnvironmentEvent{EnvironmentID: env.Id().String(), Error: err, Message: "transition START_ACTIVITY failed, handling"}) + goErrorKillDestroy("transition START_ACTIVITY") + + return + } + + err = env.TryTransition(trans) + if err != nil { + goErrorKillDestroy("transition START_ACTIVITY") + return + } + + for { + // we know we performed START_ACTIVITY, so we poll at 1Hz for the run to finish + time.Sleep(1 * time.Second) + + if env == nil { + // must've died during the loop + return + } + + envState := env.CurrentState() + switch envState { + case "CONFIGURED": + // RUN finished so we can reset and delete the environment + err = env.TryTransition(NewResetTransition(envs.taskman)) + if err != nil { + env.sendEnvironmentEvent(&event.EnvironmentEvent{Message: "transition RESET failed, handling", EnvironmentID: env.Id().String(), Error: err}) + goErrorKillDestroy("transition RESET") + return + } + err = envs.TeardownEnvironment(env.id, false) + if err != nil { + env.sendEnvironmentEvent(&event.EnvironmentEvent{Message: "teardown failed, handling", EnvironmentID: env.Id().String(), Error: err}) + goErrorKillDestroy("teardown") + return + } + tasksForEnv := env.Workflow().GetTasks().GetTaskIds() + _, _, err = envs.taskman.KillTasks(tasksForEnv) + if err != nil { + env.sendEnvironmentEvent(&event.EnvironmentEvent{Message: "task kill failed, handling", EnvironmentID: env.Id().String(), Error: err}) + return + } + + env.sendEnvironmentEvent(&event.EnvironmentEvent{Message: "environment teardown complete", State: "DONE", EnvironmentID: env.Id().String()}) + return + case "ERROR": + fallthrough + case "STANDBY": + fallthrough + case "DEPLOYED": + goErrorKillDestroy("transition STOP_ACTIVITY") + return + case "MIXED": + continue + case "": + continue + } + } } diff --git a/core/environment/transition.go b/core/environment/transition.go index 87a0d0ac9..0cf342635 100644 --- a/core/environment/transition.go +++ b/core/environment/transition.go @@ -22,12 +22,13 @@ * Intergovernmental Organization or submit itself to any jurisdiction. */ - package environment import ( "errors" - "github.com/AliceO2Group/Control/core/protos" + + "github.com/AliceO2Group/Control/common/monitoring" + pb "github.com/AliceO2Group/Control/core/protos" "github.com/AliceO2Group/Control/core/task" ) @@ -39,8 +40,10 @@ type Transition interface { func MakeTransition(taskman *task.Manager, optype pb.ControlEnvironmentRequest_Optype) Transition { switch optype { + case pb.ControlEnvironmentRequest_DEPLOY: + return NewDeployTransition(taskman, nil, nil) case pb.ControlEnvironmentRequest_CONFIGURE: - return NewConfigureTransition(taskman, nil, nil, true) + return NewConfigureTransition(taskman) case pb.ControlEnvironmentRequest_START_ACTIVITY: return NewStartActivityTransition(taskman) case pb.ControlEnvironmentRequest_STOP_ACTIVITY: @@ -58,13 +61,13 @@ func MakeTransition(taskman *task.Manager, optype pb.ControlEnvironmentRequest_O } type baseTransition struct { - taskman *task.Manager - name string + taskman *task.Manager + name string } func (t baseTransition) check() (err error) { if t.taskman == nil { - err = errors.New("cannot configure environment with nil roleman") + err = errors.New("cannot transition environment with nil taskman") } return } @@ -73,3 +76,6 @@ func (t baseTransition) eventName() string { return t.name } +func (t baseTransition) transitionDoMetric(env *Environment) monitoring.Metric { + return transitionMetric(env, t.eventName(), "do") +} diff --git a/core/environment/transition_configure.go b/core/environment/transition_configure.go index 83373f921..09d93eca9 100644 --- a/core/environment/transition_configure.go +++ b/core/environment/transition_configure.go @@ -26,30 +26,26 @@ package environment import ( "errors" - "time" + "github.com/AliceO2Group/Control/core/workflow" + + "github.com/AliceO2Group/Control/common/event" + "github.com/AliceO2Group/Control/common/monitoring" "github.com/AliceO2Group/Control/core/task" - "github.com/pborman/uuid" - "github.com/sirupsen/logrus" + "github.com/AliceO2Group/Control/core/task/taskop" ) -func NewConfigureTransition(taskman *task.Manager, addRoles []string, removeRoles []string, reconfigureAll bool) Transition { +func NewConfigureTransition(taskman *task.Manager) Transition { return &ConfigureTransition{ baseTransition: baseTransition{ - name: "CONFIGURE", + name: "CONFIGURE", taskman: taskman, }, - addRoles: addRoles, - removeRoles: removeRoles, - reconfigureAll: reconfigureAll, } } type ConfigureTransition struct { baseTransition - addRoles []string - removeRoles []string - reconfigureAll bool } func (t ConfigureTransition) do(env *Environment) (err error) { @@ -57,128 +53,26 @@ func (t ConfigureTransition) do(env *Environment) (err error) { return errors.New("cannot transition in NIL environment") } - wf := env.Workflow() - - - // Role tree operations go here, and afterwards we'll generally get a role tree which - // has - // - some TaskRoles already deployed with Tasks - // - some TaskRoles with no Tasks but with matching Tasks in the roster - // - some TaskRoles with no Tasks and no matching running Tasks in the roster - -/* - // First we free the relevant roles, if any - if len(t.removeRoles) != 0 { - rolesThatStay := env.roles[:0] - rolesToRelease := make([]string, 0) - - for _, role := range env.roles { - for _, removeRole := range t.removeRoles { - if role == removeRole { - rolesToRelease = append(rolesToRelease, role) - break - } - rolesThatStay = append(rolesThatStay, role) - } - } - err = t.roleman.ReleaseRoles(env.Id().Array(), rolesToRelease) - if err != nil { - return - } - env.roles = rolesThatStay - } - // IDEA: instead of passing around m.state or roleman, pass around one or more of - // roleman's channels. This way roleman could potentially be lockless, and we just pipe - // him a list of rolenames to remove/add, or even a function or a struct that does so. - // This struct would implement an interface of the type of his channel, and he could - // use type assertion to check whether he needs to add, remove or do something else. - - // Alright, so now we have freed some roles (if required). - // We proceed by deduplicating and attempting an acquire. - if len(t.addRoles) != 0 { - rolesToAcquire := make([]string, 0) + metric := t.transitionDoMetric(env) + defer monitoring.TimerSendSingle(&metric, monitoring.Millisecond)() - for _, addRole := range t.addRoles { - alreadyInEnv := false - for _, role := range env.roles { - if role == addRole { - alreadyInEnv = true - break - } - } - if !alreadyInEnv { - rolesToAcquire = append(rolesToAcquire, addRole) - } - } - err = t.roleman.AcquireRoles(env.Id().Array(), rolesToAcquire) - if err != nil { - return - } - - // We complete a move to CONFIGURED for all roles and we're done. - err = t.roleman.ConfigureRoles(env.Id().Array(), rolesToAcquire) - if err != nil { - return - } - - env.roles = append(env.roles, rolesToAcquire...) - } - - // Finally, we configure. - if t.reconfigureAll { - err = t.roleman.ConfigureRoles(env.Id().Array(), env.roles) - if err != nil { - return - } - } - - return*/ - - notify := make(chan task.Status) - subscriptionId := uuid.NewUUID().String() - env.wfAdapter.SubscribeToStatusChange(subscriptionId, notify) - defer env.wfAdapter.UnsubscribeFromStatusChange(subscriptionId) - - taskDescriptors := wf.GenerateTaskDescriptors() - if len(taskDescriptors) != 0 { - err = t.taskman.AcquireTasks(env.Id().Array(), taskDescriptors) - } - if err != nil { - return - } + wf := env.Workflow() - deploymentTimeout := 90 * time.Second - wfStatus := wf.GetStatus() - if wfStatus != task.ACTIVE { - WORKFLOW_ACTIVE_LOOP: - for { - log.Debug("waiting for workflow to become active") - select { - case wfStatus = <-notify: - log.WithField("status", wfStatus.String()). - Debug("workflow status change") - if wfStatus == task.ACTIVE { - break WORKFLOW_ACTIVE_LOOP - } - continue - case <-time.After(deploymentTimeout): - err = errors.New("workflow deployment timed out") - break WORKFLOW_ACTIVE_LOOP - } - } - } + activeTasks := workflow.GetActiveTasks(wf) - if err != nil { - log.WithFields(logrus.Fields{"error": err.Error(), "timeout": deploymentTimeout.String()}). - Error("workflow deployment error") - return + if len(activeTasks) != 0 { + // err = t.taskman.ConfigureTasks(env.Id().Array(), tasks) + taskmanMessage := task.NewEnvironmentMessage(taskop.ConfigureTasks, env.Id(), activeTasks, nil) + t.taskman.MessageChannel <- taskmanMessage } - - tasks := wf.GetTasks() - - if len(tasks) != 0 { - err = t.taskman.ConfigureTasks(env.Id().Array(), tasks) + incomingEv := <-env.stateChangedCh + // If some tasks failed to transition + if tasksStateErrors := incomingEv.GetTasksStateChangedError(); tasksStateErrors != nil { + metric.AddResult(monitoring.ERROR) + return tasksStateErrors } + env.sendEnvironmentEvent(&event.EnvironmentEvent{EnvironmentID: env.Id().String(), State: "CONFIGURED"}) + metric.AddResult(monitoring.SUCCESS) return } diff --git a/core/environment/transition_deploy.go b/core/environment/transition_deploy.go new file mode 100644 index 000000000..28e938dd7 --- /dev/null +++ b/core/environment/transition_deploy.go @@ -0,0 +1,402 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2018 CERN and copyright holders of ALICE O². + * Author: Teo Mrnjavac + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package environment + +import ( + "context" + "errors" + "fmt" + "os/exec" + "sort" + "strings" + "sync" + "time" + + "github.com/AliceO2Group/Control/common/event" + "github.com/AliceO2Group/Control/common/logger/infologger" + "github.com/AliceO2Group/Control/common/monitoring" + "github.com/AliceO2Group/Control/core/task" + "github.com/AliceO2Group/Control/core/task/sm" + "github.com/AliceO2Group/Control/core/task/taskop" + "github.com/AliceO2Group/Control/core/workflow" + "github.com/hashicorp/go-multierror" + "github.com/pborman/uuid" +) + +func NewDeployTransition(taskman *task.Manager, addRoles []string, removeRoles []string) Transition { + return &DeployTransition{ + baseTransition: baseTransition{ + name: "DEPLOY", + taskman: taskman, + }, + addRoles: addRoles, + removeRoles: removeRoles, + } +} + +type DeployTransition struct { + baseTransition + addRoles []string + removeRoles []string +} + +func (t DeployTransition) do(env *Environment) (err error) { + if env == nil { + return errors.New("cannot transition in NIL environment") + } + + metric := t.transitionDoMetric(env) + defer monitoring.TimerSendSingle(&metric, monitoring.Millisecond)() + + wf := env.Workflow() + + // Skip cleanup for anything other than readout-dataflow + if wf.GetName() == "readout-dataflow" { + + flps := env.GetFLPs() + var wg sync.WaitGroup + wg.Add(len(flps)) + + var scriptErrors *multierror.Error + // Execute the cleanup script on all FLPs before the DEPLOY transition commences + for _, flp := range flps { + go func(flp string) { + defer wg.Done() + + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + defer cancel() + + cmdString := "ssh -o StrictHostKeyChecking=no root@" + flp + " \"source /etc/profile.d/o2.sh && O2_PARTITION=" + env.id.String() + " O2_FACILITY=core/shmcleaner O2_ROLE=" + flp + " /opt/o2/bin/o2-aliecs-shmcleaner\"" + log.WithField("partition", env.Id().String()). + Traceln("[cleanup binary] Executing: " + cmdString) + cmd := exec.CommandContext(ctx, "/bin/bash", "-c", cmdString) + var localErr error + localErr = cmd.Run() + if localErr != nil { + log.WithField("partition", env.Id().String()). + WithField("level", infologger.IL_Support). + Warnf("cleanup script failed on %s : %s\n", flp, localErr.Error()) + scriptErrors = multierror.Append(scriptErrors, fmt.Errorf("cleanup unsuccessful on %s: %w", flp, localErr)) + } else { + log.WithField("partition", env.Id().String()). + Tracef("cleanup script executed successfully on %s\n", flp) + } + }(flp) + } + + wg.Wait() + err = scriptErrors.ErrorOrNil() + } + + // Role tree operations go here, and afterwards we'll generally get a role tree which + // has + // - some TaskRoles already deployed with Tasks + // - some TaskRoles with no Tasks but with matching Tasks in the roster + // - some TaskRoles with no Tasks and no matching running Tasks in the roster + + /* + // First we free the relevant roles, if any + if len(t.removeRoles) != 0 { + rolesThatStay := env.roles[:0] + rolesToRelease := make([]string, 0) + + for _, role := range env.roles { + for _, removeRole := range t.removeRoles { + if role == removeRole { + rolesToRelease = append(rolesToRelease, role) + break + } + rolesThatStay = append(rolesThatStay, role) + } + } + err = t.roleman.ReleaseRoles(env.Id().Array(), rolesToRelease) + if err != nil { + return + } + env.roles = rolesThatStay + } + // IDEA: instead of passing around m.state or roleman, pass around one or more of + // roleman's channels. This way roleman could potentially be lockless, and we just pipe + // him a list of rolenames to remove/add, or even a function or a struct that does so. + // This struct would implement an interface of the type of his channel, and he could + // use type assertion to check whether he needs to add, remove or do something else. + + // Alright, so now we have freed some roles (if required). + // We proceed by deduplicating and attempting an acquire. + if len(t.addRoles) != 0 { + rolesToAcquire := make([]string, 0) + + for _, addRole := range t.addRoles { + alreadyInEnv := false + for _, role := range env.roles { + if role == addRole { + alreadyInEnv = true + break + } + } + if !alreadyInEnv { + rolesToAcquire = append(rolesToAcquire, addRole) + } + } + err = t.roleman.AcquireRoles(env.Id().Array(), rolesToAcquire) + if err != nil { + return + } + + // We complete a move to CONFIGURED for all roles and we're done. + err = t.roleman.ConfigureRoles(env.Id().Array(), rolesToAcquire) + if err != nil { + return + } + + env.roles = append(env.roles, rolesToAcquire...) + } + + // Finally, we configure. + if t.reconfigureAll { + err = t.roleman.ConfigureRoles(env.Id().Array(), env.roles) + if err != nil { + return + } + } + + return*/ + + // notifyStatusCh is used only as a wake-up signal. To be sure that updates are not missed we need to + // query the status wia wf.GetStatus() because senders is sending asynchronously so the value + // inside the channel does not have to be the most recent. I didn't change the channel type to + // not break something else. + notifyStatusCh := make(chan task.Status, 1) + subscriptionId := uuid.NewUUID().String() + env.wfAdapter.SubscribeToStatusChange(subscriptionId, notifyStatusCh) + defer env.wfAdapter.UnsubscribeFromStatusChange(subscriptionId) + + // listen to workflow State changes + notifyState := make(chan sm.State) + env.wfAdapter.SubscribeToStateChange(subscriptionId, notifyState) + defer env.wfAdapter.UnsubscribeFromStateChange(subscriptionId) + + taskDescriptors := wf.GenerateTaskDescriptors() + if len(taskDescriptors) != 0 { + // err = t.taskman.AcquireTasks(env.Id().Array(), taskDescriptors) + taskmanMessage := task.NewEnvironmentMessage(taskop.AcquireTasks, env.Id(), nil, taskDescriptors) + t.taskman.MessageChannel <- taskmanMessage + } + if err != nil { + log.WithField("partition", env.Id().String()). + WithField("level", infologger.IL_Ops). + Warnf("pre-deployment cleanup, %s", err.Error()) + err = nil // we don't want to fail the deployment because of pre-deploy cleanup issues + } + + // We set all callRoles to ACTIVE right now, because there's no task activation for them. + // This is the callRole equivalent of AcquireTasks, which only pushes updates to taskRoles. + allHooks := wf.GetAllHooks() + callHooks := allHooks.FilterCalls() // get the calls + if len(callHooks) > 0 { + for _, h := range callHooks { + pr, ok := h.GetParentRole().(workflow.PublicUpdatable) + if !ok { + continue + } + go pr.UpdateStatus(task.ACTIVE) + } + } + + deploymentTimeout := acquireDeploymentTimeout(wf) + + wfStatus := wf.GetStatus() + if wfStatus != task.ACTIVE { + log.WithField("partition", env.Id().String()). + Infof("waiting %s for workflow to become active", deploymentTimeout.String()) + WORKFLOW_ACTIVE_LOOP: + for { + select { + case <-notifyStatusCh: + wfStatus = wf.GetStatus() + log.WithField("status", wfStatus.String()). + WithField("partition", env.Id().String()). + Debug("workflow status change") + if wfStatus == task.ACTIVE { + break WORKFLOW_ACTIVE_LOOP + } else if wfStatus == task.UNDEPLOYABLE { + err = errors.New("workflow is undeployable") + undeployableTaskRoles := make([]string, 0) + workflow.LeafWalk(wf, func(role workflow.Role) { + if roleStatus := role.GetStatus(); roleStatus == task.UNDEPLOYABLE { + detector, ok := role.GetUserVars().Get("detector") + if !ok { + detector = "" + } + log.WithField("state", role.GetState().String()). + WithField("status", roleStatus.String()). + WithField("role", role.GetPath()). + WithField("partition", env.Id().String()). + WithField("timeout", deploymentTimeout). + WithField("detector", detector). + WithField("level", infologger.IL_Devel). + Error("role failed to deploy within timeout") + undeployableTaskRoles = append(undeployableTaskRoles, role.GetPath()) + } + }) + + sort.Strings(undeployableTaskRoles) + inactiveTaskRolesS := strings.Join(undeployableTaskRoles, ", ") + + err = fmt.Errorf("workflow deployment failed (one or more roles undeployable), aborting and cleaning up [undeployable roles: %s]", inactiveTaskRolesS) + + break WORKFLOW_ACTIVE_LOOP + } + continue + + case <-time.After(deploymentTimeout): + wfStatus = wf.GetStatus() + // just a safety check in case we missed the final update + if wfStatus == task.ACTIVE { + break WORKFLOW_ACTIVE_LOOP + } + inactiveTaskRoles := make([]string, 0) + undeployableTaskRoles := make([]string, 0) + workflow.LeafWalk(wf, func(role workflow.Role) { + if roleStatus := role.GetStatus(); roleStatus == task.UNDEPLOYABLE { + detector, ok := role.GetUserVars().Get("detector") + if !ok { + detector = "" + } + log.WithField("state", role.GetState().String()). + WithField("status", roleStatus.String()). + WithField("role", role.GetPath()). + WithField("partition", env.Id().String()). + WithField("timeout", deploymentTimeout). + WithField("detector", detector). + WithField("level", infologger.IL_Devel). + Error("role failed to deploy because of timeout") + undeployableTaskRoles = append(undeployableTaskRoles, role.GetPath()) + } else if roleStatus != task.ACTIVE { + detector, ok := role.GetUserVars().Get("detector") + if !ok { + detector = "" + } + log.WithField("state", role.GetState().String()). + WithField("status", roleStatus.String()). + WithField("role", role.GetPath()). + WithField("partition", env.Id().String()). + WithField("timeout", deploymentTimeout). + WithField("detector", detector). + WithField("level", infologger.IL_Devel). + Error("role failed to deploy because of timeout") + inactiveTaskRoles = append(inactiveTaskRoles, role.GetPath()) + } + }) + + sort.Strings(inactiveTaskRoles) + sort.Strings(undeployableTaskRoles) + inactiveTaskRolesS := strings.Join(inactiveTaskRoles, ", ") + undeployableTaskRolesS := strings.Join(undeployableTaskRoles, ", ") + + err = fmt.Errorf("workflow deployment timed out (%s), aborting and cleaning up [%d undeployable roles: %s; %d inactive roles: %s]", + deploymentTimeout.String(), + len(undeployableTaskRoles), + undeployableTaskRolesS, + len(inactiveTaskRoles), + inactiveTaskRolesS) + break WORKFLOW_ACTIVE_LOOP + // This is needed for when the workflow fails during the STAGING state(mesos status),mesos responds with the `REASON_COMMAND_EXECUTOR_FAILED`, + // By listening to workflow state ERROR we can break the loop before reaching the timeout (1m30s), we can trigger the cleanup faster + // in the CreateEnvironment (environment/manager.go) and the lock in the `envman` is reserved for a sorter period, which allows operations like + // `environment list` to be done almost immediately after mesos informs with TASK_FAILED. + case wfState := <-notifyState: + if wfState == sm.ERROR { + failedRoles := make([]string, 0) + workflow.LeafWalk(wf, func(role workflow.Role) { + if st := role.GetState(); st == sm.ERROR { + detector, ok := role.GetUserVars().Get("detector") + if !ok { + detector = "" + } + + log.WithField("state", st). + WithField("role", role.GetPath()). + WithField("partition", env.Id().String()). + WithField("detector", detector). + WithField("level", infologger.IL_Trace). + Error("environment reached invalid state") + failedRoles = append(failedRoles, role.GetPath()) + } + }) + log.WithField("state", wfState.String()). + WithField("partition", env.Id().String()). + Debug("workflow state change") + err = fmt.Errorf("workflow deployment failed, aborting and cleaning up [failed roles: %s]", strings.Join(failedRoles, ", ")) + break WORKFLOW_ACTIVE_LOOP + } + } + } + } + + if err != nil { + log.WithField("level", infologger.IL_Ops). + WithField("partition", env.Id().String()). + Error(err) + metric.AddResult(monitoring.ERROR) + return + } + + env.sendEnvironmentEvent(&event.EnvironmentEvent{EnvironmentID: env.Id().String(), State: "DEPLOYED"}) + metric.AddResult(monitoring.SUCCESS) + return +} + +func acquireDeploymentTimeout(wf workflow.Role) time.Duration { + envId := wf.GetEnvironmentId() + + var ( + cws map[string]string + err error + ) + deploymentTimeout := 90 * time.Second + cws, err = wf.ConsolidatedVarStack() + if err != nil { + log.WithField("partition", envId). + WithError(err). + Warnf("could not get consolidated variable stack, deploy_timeout defaulting to %s", deploymentTimeout.String()) + err = nil + } else { + deploymentTimeoutS, ok := cws["deploy_timeout"] + if ok { + var timeout time.Duration + timeout, err = time.ParseDuration(deploymentTimeoutS) + if err != nil { + log.WithField("partition", envId). + WithError(err). + Warnf("variable deploy_timeout (%s) could not be parsed, defaulting to %s", deploymentTimeoutS, deploymentTimeout.String()) + err = nil + } else { + deploymentTimeout = timeout + } + } + } + return deploymentTimeout +} diff --git a/core/environment/transition_goerror.go b/core/environment/transition_goerror.go new file mode 100644 index 000000000..e0844f6d5 --- /dev/null +++ b/core/environment/transition_goerror.go @@ -0,0 +1,80 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2022 CERN and copyright holders of ALICE O². + * Author: Piotr Konopka + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package environment + +import ( + "github.com/AliceO2Group/Control/common/monitoring" + "github.com/AliceO2Group/Control/core/controlcommands" + "github.com/AliceO2Group/Control/core/task" + "github.com/AliceO2Group/Control/core/task/sm" +) + +func NewGoErrorTransition(taskman *task.Manager) Transition { + return &GoErrorTransition{ + baseTransition: baseTransition{ + name: "GO_ERROR", + taskman: taskman, + }, + } +} + +type GoErrorTransition struct { + baseTransition +} + +func (t GoErrorTransition) do(env *Environment) (err error) { + metric := t.transitionDoMetric(env) + defer monitoring.TimerSendSingle(&metric, monitoring.Millisecond)() + + // we stop all tasks which are in RUNNING + toStop := env.Workflow().GetTasks().Filtered(func(t *task.Task) bool { + t.SetSafeToStop(true) + return t.IsSafeToStop() + }) + if len(toStop) > 0 { + args := controlcommands.PropertyMap{} + if cvs, err := env.Workflow().ConsolidatedVarStack(); err == nil { + for _, key := range StopActivityParameterKeys { + if value, ok := cvs[key]; ok { + args[key] = value + } + } + } + + taskmanMessage := task.NewTransitionTaskMessage( + toStop, + sm.RUNNING.String(), + sm.STOP.String(), + sm.CONFIGURED.String(), + args, + env.Id(), + ) + t.taskman.MessageChannel <- taskmanMessage + <-env.stateChangedCh + } + + metric.AddResult(monitoring.SUCCESS) + return +} diff --git a/core/environment/transition_reset.go b/core/environment/transition_reset.go index 841af9a04..84bfd44da 100644 --- a/core/environment/transition_reset.go +++ b/core/environment/transition_reset.go @@ -26,7 +26,12 @@ package environment import ( "errors" + + "github.com/AliceO2Group/Control/common/event" + "github.com/AliceO2Group/Control/common/monitoring" "github.com/AliceO2Group/Control/core/task" + "github.com/AliceO2Group/Control/core/task/sm" + "github.com/AliceO2Group/Control/core/workflow" ) func NewResetTransition(taskman *task.Manager) Transition { @@ -47,16 +52,27 @@ func (t ResetTransition) do(env *Environment) (err error) { return errors.New("cannot transition in NIL environment") } - err = t.taskman.TransitionTasks( - env.Workflow().GetTasks(), - task.CONFIGURED.String(), - task.RESET.String(), - task.STANDBY.String(), + metric := t.transitionDoMetric(env) + defer monitoring.TimerSendSingle(&metric, monitoring.Millisecond)() + + taskmanMessage := task.NewTransitionTaskMessage( + workflow.GetActiveTasks(env.Workflow()), + sm.CONFIGURED.String(), + sm.RESET.String(), + sm.STANDBY.String(), nil, + env.Id(), ) - if err != nil { - return + t.taskman.MessageChannel <- taskmanMessage + + incomingEv := <-env.stateChangedCh + // If some tasks failed to transition + if tasksStateErrors := incomingEv.GetTasksStateChangedError(); tasksStateErrors != nil { + metric.AddResult(monitoring.ERROR) + return tasksStateErrors } + env.sendEnvironmentEvent(&event.EnvironmentEvent{EnvironmentID: env.Id().String(), State: "RESET"}) + metric.AddResult(monitoring.SUCCESS) return } diff --git a/core/environment/transition_startactivity.go b/core/environment/transition_startactivity.go index 2002a4604..47a547c1b 100644 --- a/core/environment/transition_startactivity.go +++ b/core/environment/transition_startactivity.go @@ -28,12 +28,33 @@ import ( "errors" "strconv" + "github.com/AliceO2Group/Control/core/task/sm" + "github.com/AliceO2Group/Control/core/workflow" + + "github.com/AliceO2Group/Control/common/event" "github.com/AliceO2Group/Control/common/logger/infologger" + "github.com/AliceO2Group/Control/common/monitoring" "github.com/AliceO2Group/Control/core/controlcommands" "github.com/AliceO2Group/Control/core/task" - "github.com/AliceO2Group/Control/core/the" + "github.com/iancoleman/strcase" ) +var StartActivityParameterKeys = []string{ + "fill_info_fill_number", + "fill_info_filling_scheme", + "fill_info_beam_type", + "fill_info_stable_beams_start_ms", + "fill_info_stable_beams_end_ms", + "run_number", + "run_type", + "run_start_time_ms", + "run_end_time_ms", // included to ensure that a cleared SOEOR timestamp is propagated to all tasks during START-STOP-START + "lhc_period", + "pdp_beam_type", + "pdp_override_run_start_time", + "original_run_number", +} + func NewStartActivityTransition(taskman *task.Manager) Transition { return &StartActivityTransition{ baseTransition: baseTransition{ @@ -52,32 +73,71 @@ func (t StartActivityTransition) do(env *Environment) (err error) { return errors.New("cannot transition in NIL environment") } - var runNumber uint32 - runNumber, err = the.ConfSvc().NewRunNumber() - if err != nil { - return - } + metric := t.transitionDoMetric(env) + defer monitoring.TimerSendSingle(&metric, monitoring.Millisecond)() + + runNumber := env.currentRunNumber - log.WithField(infologger.Run, runNumber).Info("starting new run") + log.WithField(infologger.Run, runNumber). + WithField("partition", env.Id().String()). + WithField(infologger.Level, infologger.IL_Support). + Info("starting new run") + + cleanupCount := 0 + cleanupCountS, ok := env.GlobalVars.Get("__fmq_cleanup_count") + if ok && len(cleanupCountS) > 0 { + var parseErr error + cleanupCount, parseErr = strconv.Atoi(cleanupCountS) + if parseErr != nil { + cleanupCount = 1 // something was there, even though non-parsable, so we signal to clean up + } + } - env.currentRunNumber = runNumber args := controlcommands.PropertyMap{ - "runNumber": strconv.FormatUint(uint64(runNumber), 10 ), + "runNumber": strconv.FormatUint(uint64(runNumber), 10), + "cleanup": strconv.Itoa(cleanupCount), } - err = t.taskman.TransitionTasks( - env.Workflow().GetTasks(), - task.CONFIGURED.String(), - task.START.String(), - task.RUNNING.String(), + // Get a handle to the consolidated var stack of the root role of the env's workflow + if wf := env.Workflow(); wf != nil { + if cvs, cvsErr := wf.ConsolidatedVarStack(); cvsErr == nil { + for _, key := range StartActivityParameterKeys { + if value, ok := cvs[key]; ok { + // we push the above parameters with both camelCase and snake_case identifiers for convenience + args[strcase.ToLowerCamel(key)] = value + args[key] = value + } + } + } + } + + taskmanMessage := task.NewTransitionTaskMessage( + workflow.GetActiveTasks(env.Workflow()), + sm.CONFIGURED.String(), + sm.START.String(), + sm.RUNNING.String(), args, + env.Id(), ) + t.taskman.MessageChannel <- taskmanMessage - if err != nil { - env.currentRunNumber = 0 - return + incomingEv := <-env.stateChangedCh + // If some tasks failed to transition + if tasksStateErrors := incomingEv.GetTasksStateChangedError(); tasksStateErrors != nil { + metric.AddResult(monitoring.ERROR) + return tasksStateErrors } - log.WithField(infologger.Run, env.currentRunNumber).Info("run started") + log.WithField(infologger.Run, env.currentRunNumber). + WithField("partition", env.Id().String()). + WithField(infologger.Level, infologger.IL_Support). + Info("run started") + env.sendEnvironmentEvent(&event.EnvironmentEvent{ + EnvironmentID: env.Id().String(), + State: "RUNNING", + Run: env.currentRunNumber, + }) + + metric.AddResult(monitoring.SUCCESS) return } diff --git a/core/environment/transition_stopactivity.go b/core/environment/transition_stopactivity.go index 5a438b5b5..7e55fbe62 100644 --- a/core/environment/transition_stopactivity.go +++ b/core/environment/transition_stopactivity.go @@ -26,10 +26,26 @@ package environment import ( "errors" + + "github.com/AliceO2Group/Control/common/event" "github.com/AliceO2Group/Control/common/logger/infologger" + "github.com/AliceO2Group/Control/common/monitoring" + "github.com/AliceO2Group/Control/core/controlcommands" "github.com/AliceO2Group/Control/core/task" + "github.com/AliceO2Group/Control/core/task/sm" + "github.com/AliceO2Group/Control/core/workflow" + "github.com/iancoleman/strcase" ) +var StopActivityParameterKeys = []string{ + "fill_info_fill_number", + "fill_info_filling_scheme", + "fill_info_beam_type", + "fill_info_stable_beams_start_ms", + "fill_info_stable_beams_end_ms", + "run_end_time_ms", +} + func NewStopActivityTransition(taskman *task.Manager) Transition { return &StopActivityTransition{ baseTransition: baseTransition{ @@ -48,21 +64,54 @@ func (t StopActivityTransition) do(env *Environment) (err error) { return errors.New("cannot transition in NIL environment") } - log.WithField(infologger.Run, env.currentRunNumber).Info("stopping run") + metric := t.transitionDoMetric(env) + defer monitoring.TimerSendSingle(&metric, monitoring.Millisecond)() + + log.WithField(infologger.Run, env.currentRunNumber). + WithField("partition", env.Id().String()). + WithField(infologger.Level, infologger.IL_Support). + Info("stopping run") - env.currentRunNumber = 0 + args := controlcommands.PropertyMap{} - err = t.taskman.TransitionTasks( - env.Workflow().GetTasks(), - task.RUNNING.String(), - task.STOP.String(), - task.CONFIGURED.String(), - nil, + // Get a handle to the consolidated var stack of the root role of the env's workflow + if wf := env.Workflow(); wf != nil { + if cvs, cvsErr := wf.ConsolidatedVarStack(); cvsErr == nil { + // in principle, only stable beams end should change among fill info vars in a typical scenario, + // but just in case of more creative uses, we push all of them again. + for _, key := range StopActivityParameterKeys { + if value, ok := cvs[key]; ok { + // we push the above parameters with both camelCase and snake_case identifiers for convenience + args[strcase.ToLowerCamel(key)] = value + args[key] = value + } + } + } + } + + taskmanMessage := task.NewTransitionTaskMessage( + workflow.GetActiveTasks(env.Workflow()), + sm.RUNNING.String(), + sm.STOP.String(), + sm.CONFIGURED.String(), + args, + env.Id(), ) + t.taskman.MessageChannel <- taskmanMessage - if err != nil { - return + incomingEv := <-env.stateChangedCh + // If some tasks failed to transition + if tasksStateErrors := incomingEv.GetTasksStateChangedError(); tasksStateErrors != nil { + metric.AddResult(monitoring.ERROR) + return tasksStateErrors } + env.sendEnvironmentEvent(&event.EnvironmentEvent{EnvironmentID: env.Id().String(), State: "CONFIGURED"}) + + log.WithField(infologger.Run, env.currentRunNumber). + WithField("partition", env.Id().String()). + WithField(infologger.Level, infologger.IL_Support). + Info("run stopped") + metric.AddResult(monitoring.SUCCESS) return } diff --git a/core/environment/utils.go b/core/environment/utils.go new file mode 100644 index 000000000..e705dcaff --- /dev/null +++ b/core/environment/utils.go @@ -0,0 +1,177 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2021 CERN and copyright holders of ALICE O². + * Author: Miltiadis Alexis + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package environment + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "os" + "sort" + + "github.com/AliceO2Group/Control/common/logger/infologger" + "github.com/AliceO2Group/Control/common/monitoring" + pb "github.com/AliceO2Group/Control/common/protos" + "github.com/AliceO2Group/Control/core/task" + "github.com/AliceO2Group/Control/core/task/sm" + "github.com/AliceO2Group/Control/core/workflow" + "github.com/looplab/fsm" + + "github.com/AliceO2Group/Control/core/the" + "gopkg.in/yaml.v3" +) + +type WorkflowPublicInfo struct { + IsPublic bool + Name string + Description string +} + +func parseWorkflowPublicInfo(workflowExpr string) (WorkflowPublicInfo, error) { + repoManager := the.RepoManager() + + resolvedWorkflowPath, _, err := repoManager.GetWorkflow(workflowExpr) // Will fail if repo unknown + if err != nil { + return WorkflowPublicInfo{}, err + } + + yamlFile, err := os.ReadFile(resolvedWorkflowPath) + if err != nil { + return WorkflowPublicInfo{}, err + } + + nodes := make(map[string]yaml.Node) + err = yaml.Unmarshal(yamlFile, &nodes) + if err != nil { + return WorkflowPublicInfo{}, err + } + + name := nodes["name"].Value + + description := "" + isPublic := nodes["name"].Tag == "!public" + if nodes["description"].Tag == "!public" { + description = nodes["description"].Value + } + + return WorkflowPublicInfo{IsPublic: isPublic, Name: name, Description: description}, nil +} + +func JSONSliceToSlice(payload string) (slice []string, err error) { + slice = make([]string, 0) + err = json.Unmarshal([]byte(payload), &slice) + return +} + +func SliceToJSONSlice(slice []string) (payload string, err error) { + var payloadStr []byte + payloadStr, err = json.Marshal(slice) + payload = string(payloadStr) + return +} + +func sortMapToString(m map[string]string) string { + keys := make([]string, 0, len(m)) + for k := range m { + keys = append(keys, k) + } + sort.Strings(keys) + b := new(bytes.Buffer) + + for _, k := range keys { + _, err := fmt.Fprintf(b, "%s=\"%s\"\n", k, m[k]) + if err != nil { + log.WithField(infologger.Level, infologger.IL_Devel).Errorf("Error formatting or buffering string for key %s: %v", k, err) + } + } + return b.String() +} + +func NewEnvGoErrorEvent(env *Environment, err string) *pb.Ev_EnvironmentEvent { + return &pb.Ev_EnvironmentEvent{ + EnvironmentId: env.GetId().String(), + State: env.Sm.Current(), + RunNumber: env.GetCurrentRunNumber(), + Error: err, + Message: "a critical error occurred, GO_ERROR transition imminent", + LastRequestUser: env.GetLastRequestUser(), + WorkflowTemplateInfo: env.GetWorkflowInfo(), + } +} + +func newCriticalTasksErrorMessage(env *Environment) string { + criticalTasksInError := env.workflow.GetTasks().Filtered(func(t *task.Task) bool { + return t.GetTraits().Critical && t.GetState() == sm.ERROR + }) + + if len(criticalTasksInError) == 0 { + return "no critical tasks in ERROR" + } else if len(criticalTasksInError) == 1 { + t := criticalTasksInError[0] + name := t.GetName() + + // if available, we prefer role name, because it does not have a long hash for JIT-generated DPL tasks + role, ok := t.GetParentRole().(workflow.Role) + if ok { + name = role.GetName() + } + return fmt.Sprintf("critical task '%s' on host '%s' transitioned to ERROR", name, t.GetHostname()) + } else { + return fmt.Sprintf("%d critical tasks transitioned to ERROR, could not determine the first one to fail", len(criticalTasksInError)) + } +} + +func HandleFailedGoError(err error, env *Environment) { + var invalidEventErr fsm.InvalidEventError + if errors.As(err, &invalidEventErr) { + // this case can occur if the environment is in either: + // - ERROR (env already transitioned to ERROR for another reason) + // - DONE (an error might have occurred during teardown, but it's already over, no point in spreading panic) + log.WithError(invalidEventErr). + WithField("partition", env.Id().String()). + WithField("run", env.currentRunNumber). + WithField("state", env.CurrentState()). + WithField(infologger.Level, infologger.IL_Support). + Warn("did not perform GO_ERROR transition") + } else { + // in principle this should never happen, so we log it accordingly and force the ERROR state just in case + log.WithError(err). + WithField("partition", env.Id().String()). + WithField("run", env.currentRunNumber). + WithField("state", env.CurrentState()). + WithField(infologger.Level, infologger.IL_Ops). + Error("could not perform GO_ERROR transition due to unexpected error, forcing...") + env.setState("ERROR") + } +} + +func transitionMetric(env *Environment, transition string, funName string) monitoring.Metric { + metric := monitoring.NewMetric("transition") + metric.AddTag("function", funName) + metric.AddTag("transition", transition) + metric.AddTag("envId", env.Id().String()) + return metric +} diff --git a/core/environment/utils_test.go b/core/environment/utils_test.go new file mode 100644 index 000000000..bf914be83 --- /dev/null +++ b/core/environment/utils_test.go @@ -0,0 +1,50 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2025 CERN and copyright holders of ALICE O². + * Author: Piotr Konopka + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package environment + +import ( + "github.com/looplab/fsm" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +var _ = Describe("HandleFailedGoError", func() { + It("does not overwrite state for InvalidEventError", func() { + env := &Environment{} + env.Sm = fsm.NewFSM("DONE", fsm.Events{}, fsm.Callbacks{}) + Expect(env.Sm.Current()).To(Equal("DONE")) + + HandleFailedGoError(fsm.InvalidEventError{Event: "GO_ERROR", State: "DONE"}, env) + Expect(env.Sm.Current()).To(Equal("DONE")) + }) + + It("overwrites state to ERROR for other errors", func() { + env := &Environment{} + env.Sm = fsm.NewFSM("CONFIGURED", fsm.Events{}, fsm.Callbacks{}) + + HandleFailedGoError(fsm.UnknownEventError{Event: "BOOM"}, env) + Expect(env.Sm.Current()).To(Equal("ERROR")) + }) +}) diff --git a/core/env.go b/core/envutil.go similarity index 76% rename from core/env.go rename to core/envutil.go index 4b004282f..de1edd83d 100644 --- a/core/env.go +++ b/core/envutil.go @@ -30,31 +30,31 @@ import ( "time" ) -func env(key, defaultValue string) (value string) { +func getenv(key, defaultValue string) (value string) { if value = os.Getenv(key); value == "" { value = defaultValue } return } -func envInt(key, defaultValue string) int { - value, err := strconv.Atoi(env(key, defaultValue)) +func getenvInt(key, defaultValue string) int { + value, err := strconv.Atoi(getenv(key, defaultValue)) if err != nil { panic(err.Error()) } return value } -func envDuration(key, defaultValue string) time.Duration { - value, err := time.ParseDuration(env(key, defaultValue)) +func getenvDuration(key, defaultValue string) time.Duration { + value, err := time.ParseDuration(getenv(key, defaultValue)) if err != nil { panic(err.Error()) } return value } -func envFloat(key, defaultValue string) float64 { - value, err := strconv.ParseFloat(env(key, defaultValue), 64) +func getenvFloat(key, defaultValue string) float64 { + value, err := strconv.ParseFloat(getenv(key, defaultValue), 64) if err != nil { panic(err.Error()) } diff --git a/core/globalstate.go b/core/globalstate.go new file mode 100644 index 000000000..d8fd256be --- /dev/null +++ b/core/globalstate.go @@ -0,0 +1,76 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2017-2018 CERN and copyright holders of ALICE O². + * Author: Teo Mrnjavac + * + * Portions from examples in : + * Copyright 2013-2015, Mesosphere, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + *ù + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package core + +import ( + "sync" + + "github.com/AliceO2Group/Control/common/event" + "github.com/AliceO2Group/Control/core/environment" + + "github.com/AliceO2Group/Control/core/task" + "github.com/AliceO2Group/Control/core/the" + "github.com/spf13/viper" +) + +func newGlobalState(shutdown func()) (*globalState, error) { + if viper.GetBool("veryVerbose") { + dump, err := the.ConfSvc().RawGetRecursive(viper.GetString("consulBasePath")) + if err != nil { + return nil, err + } + log.WithField("data", dump).Trace("configuration dump") + } + + state := &globalState{ + shutdown: shutdown, + environments: nil, + } + + internalEventCh := make(chan event.Event) + taskman, err := task.NewManager(shutdown, internalEventCh) + if err != nil { + return nil, err + } + + state.taskman = taskman + //state.taskman.Start() + state.environments = environment.NewEnvManager(state.taskman, internalEventCh) + + return state, nil +} + +type globalState struct { + sync.RWMutex + + shutdown func() + + // uses locks, so thread safe + environments *environment.Manager + taskman *task.Manager +} diff --git a/core/integration/README.md b/core/integration/README.md new file mode 100644 index 000000000..41996b2b9 --- /dev/null +++ b/core/integration/README.md @@ -0,0 +1,195 @@ +# Integration plugins + +The integration plugins allow AliECS to communicate with other ALICE services. +A plugin can register a set of callback which can be invoked upon defined environment events (state transitions). + +## Plugin system overview + +All plugins should implement the [`Plugin`](https://github.com/AliceO2Group/Control/blob/master/core/integration/plugin.go) interface. +See the existing plugins for examples. + +In order to have the plugin loaded by the AliECS, one has to: + +- add `RegisterPlugin` to the `init()` function in [AliECS core main source](https://github.com/AliceO2Group/Control/blob/master/cmd/o2-aliecs-core/main.go) +- add plugin name in the `integrationPlugins` list and set the endpoint in the AliECS configuration file (typically at `/o2/components/aliecs/ANY/any/settings` in the configuration store) + +# Integrated service operations + +In this chapter we list and describe the integrated service plugins. + +## Bookkeeping + +The legacy Bookkeeping plugin sends updates to Bookkeeping about the state of data taking runs. +As of May 2025, Bookkeeping has transitioned into consuming input from the Kafka event service and the only call in use is "FillInfo", which allows ECS to retrieve LHC fill information. + +## CCDB + +CCDB plugin calls PDP-provided executable which creates a General Run Parameters (GRP) object at each run start and stop. + +## DCS + +DCS plugin communicates with the ALICE Detector Control System (DCS). +In particular, it communicates with a gRPC endpoint exposed by `ecs2dcsgateway` (developed by the DCS team), which then passes the requests to the DCS. + +### DCS operations + +The DCS integration plugin exposes to the workflow template (WFT) context the +following operations. Their associated transitions in this table refer +to the [readout-dataflow](https://github.com/AliceO2Group/ControlWorkflows/blob/master/workflows/readout-dataflow.yaml) workflow template. + +| **DCS operation** | **WFT call** | **Call timing** | **Critical** | **Contingent on detector state** | +|-----------------------|---------------------|---------------------------|--------------|----------------------------------| +| Prepare For Run (PFR) | `dcs.PrepareForRun` | during `CONFIGURE` | `false` | yes | +| Start Of Run (SOR) | `dcs.StartOfRun` | early in `START_ACTIVITY` | `true` | yes | +| End Of Run (EOR) | `dcs.EndOfRun` | late in `STOP_ACTIVITY` | `true` | no | + +The DCS integration plugin subscribes to the [DCS service](https://github.com/AliceO2Group/Control/blob/master/core/integration/dcs/protos/dcs.proto) and continually +receives information on operation-state compatibility for all +detectors. +When a given environment reaches a DCS call, the relevant DCS operation +will be called only if the DCS service reports that all detectors in that +environment are compatible with this operation, except EOR, which is +always called. + +### DCS PrepareForRun behaviour + +Unlike SOR and EOR, which are mandatory if `dcs_enabled` is set to `true`, +an impossibility to run PFR or a PFR failure will not prevent the +environment from transitioning forward. + +#### DCS PFR incompatibility + +When `dcs.PrepareForRun` is called, if at least one detector is in a +state that is incompatible with PFR as reported by the DCS service, +a grace period of 10 seconds is given for the detector(s) to become +compatible with PFR, with 1Hz polling frequency. As soon as all +detectors become compatible with PFR, the PFR operation is requested +to the DCS service. + +If the grace period ends and at least one detector +included in the environment is still incompatible with PFR, the PFR +operation will be performed for the PFR-compatible detectors. + +Despite some detectors not having performed PFR, the environment +can still transition forward towards the `RUNNING` state, and any DCS +activities that would have taken place in PFR will instead happen +during SOR. Only at that point, if at least one detector is not +compatible with SOR (or if it is but SOR fails), will the environment +declare a failure. + +#### DCS PFR failure + +When `dcs.PrepareForRun` is called, if all detectors are compatible +with PFR as reported by the DCS service (or become compatible during +the grace period), the PFR operation is immediately requested to the +DCS service. + +`dcs.PrepareForRun` call fails if no detectors are PFR-compatible +or PFR fails for all those which were PFR-compatible, +but since it is non-critical the environment may still reach the +`CONFIGURED` state and transition forward towards `RUNNING`. + +As in the case of an impossibility to run PFR, any DCS activities that +would have taken place in PFR will instead be done during SOR. + +### DCS StartOfRun behaviour + +The SOR operation is mandatory if `dcs_enabled` is set to `true` +(AliECS GUI "DCS" switched on). + +#### DCS SOR incompatibility + +When `dcs.StartOfRun` is called, if at least one detector is in a +state that is incompatible with SOR as reported by the DCS service, +or if after a grace period of 10 seconds at least one detector is +still incompatible with SOR, the SOR operation **will not run for any +detector**. + +The environment will then declare a **failure**, the +`START_ACTIVITY` transition will be blocked and the environment +will move to `ERROR`. + +#### DCS SOR failure + +When `dcs.StartOfRun` is called, if all detectors are compatible +with SOR as reported by the DCS service (or become compatible during +the grace period), the SOR operation is immediately requested to the +DCS service. + +If this operation fails for one or more detectors, the +`dcs.StartOfRun` call as a whole is considered to have failed. + +The environment will then declare a **failure**, the +`START_ACTIVITY` transition will be blocked and the environment +will move to `ERROR` + +### DCS EndOfRun behaviour + +The EOR operation is mandatory if `dcs_enabled` is set to `true` +(AliECS GUI "DCS" switched on). However, unlike with PFR and SOR, there +is **no check for compatibility** with the EOR operation. The EOR +request will always be sent to the DCS service during `STOP_ACTIVITY`. + +#### DCS EOR failure + +If this operation fails for one or more detectors, the +`dcs.EndOfRun` call as a whole is considered to have failed. + +The environment will then declare a **failure**, the +`STOP_ACTIVITY` transition will be blocked and the environment +will move to `ERROR`. + +### ECS2DCS2ECS mock server + +ECS2DCS2ECS is a simple server that mimics the DCS service and is developed by the DCS team. +In particular, it sends heartbeats, reports detectors as PFR or SOR available and replies with requested state sequences. + +The state sequences should be added as extra arguments to the DCS calls with key `"STATE_SEQUENCE"`, while the value is a comma-separated list of `MILLIS_DELAY:DETECTOR_STATE` values. + +From the AliECS user point of view, state sequences can be defined as the following user-vars (Advanced Configuration panel in COG), which are then passed to the corresponding PFR/SOR/EOR calls for all detectors: + +- `dcs_pfr_state_sequence` +- `dcs_sor_state_sequence` +- `dcs_eor_state_sequence` + +#### Testing tips + +Typical valid sequences are: + +``` +{ + "dcs_pfr_state_sequence": "1000:RUN_OK", + "dcs_sor_state_sequence": "1000:SOR_PROGRESSING,3000:RUN_OK", + "dcs_eor_state_sequence": "1000:EOR_PROGRESSING,3000:RUN_OK" +} +``` + +`PFR_AVAILABLE` and `SOR_AVAILABLE` are typically send by DCS via the heartbeat stream, so requesting them as an answer to an operation request does not reflect the behaviour of the production system. + +To trigger a `TIMEOUT` event, one should not request a `TIMEOUT` state in the sequence, but rather put a too long delay compared to the provided gRPC timeout. +Both in the production and the mock setup, `TIMEOUT` events are generated by `ecs2dcsgateway` when a detector does not respond to a request within the specified timeout. + +## DD Scheduler + +DD scheduler plugin informs the Data Distribution software about the pool of FLPs taking part in data taking. + +## Kafka (legacy) + +See [Legacy events: Kafka plugin](/docs/kafka.md#legacy-events-kafka-plugin) + +## LHC + +This plugin listens to Kafka messages coming from the LHC DIP Client and pushes any relevant internal notifications to the AliECS core. +Its main purpose is to provide basic information about ongoing LHC activity (e.g. fill information) to affected parties and allow AliECS to react upon them (e.g. by automatically stopping a physics run when stable beams are over). + +## ODC + +ODC plugin communicates with the [Online Device Control (ODC)](https://github.com/FairRootGroup/ODC) instance of the ALICE experiment, which controls the event processing farm used in data taking and offline processing. + +## Test plugin + +Test plugin serves as an example of a plugin and is used for testing the plugin system. + +## Trigger + +Trigger plugin communicates with the ALICE trigger system. diff --git a/core/integration/bookkeeping/client.go b/core/integration/bookkeeping/client.go new file mode 100644 index 000000000..6cc155912 --- /dev/null +++ b/core/integration/bookkeeping/client.go @@ -0,0 +1,141 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2022 CERN and copyright holders of ALICE O². + * Author: Claire Guyot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package bookkeeping + +import ( + "context" + "time" + + "github.com/AliceO2Group/Control/common/logger" + bkpb "github.com/AliceO2Group/Control/core/integration/bookkeeping/protos" + "github.com/sirupsen/logrus" + "github.com/spf13/viper" + "google.golang.org/grpc" + "google.golang.org/grpc/backoff" + "google.golang.org/grpc/connectivity" + "google.golang.org/grpc/keepalive" +) + +var log = logger.New(logrus.StandardLogger(), "bookkeepingclient") + +type RpcClient struct { + bkpb.EnvironmentServiceClient + bkpb.FlpServiceClient + bkpb.LogServiceClient + bkpb.RunServiceClient + bkpb.LhcFillServiceClient + conn *grpc.ClientConn + cancel context.CancelFunc +} + +func NewClient(cxt context.Context, cancel context.CancelFunc, endpoint string) *RpcClient { + log.WithFields(logrus.Fields{ + "endpoint": endpoint, + }).Debug("dialing Bookkeeping endpoint") + + dialOptions := []grpc.DialOption{ + grpc.WithInsecure(), + grpc.WithConnectParams(grpc.ConnectParams{ + Backoff: backoff.Config{ + BaseDelay: backoff.DefaultConfig.BaseDelay, + Multiplier: backoff.DefaultConfig.Multiplier, + Jitter: backoff.DefaultConfig.Jitter, + MaxDelay: 20 * time.Second, + }, + MinConnectTimeout: 10 * time.Second, + }), + grpc.WithKeepaliveParams(keepalive.ClientParameters{ + Time: 5 * time.Second, + Timeout: time.Second, + PermitWithoutStream: true, + }), + } + if !viper.GetBool("bookkeepingUseSystemProxy") { + dialOptions = append(dialOptions, grpc.WithNoProxy()) + } + conn, err := grpc.DialContext(cxt, + endpoint, + dialOptions..., + ) + if err != nil { + log.WithField("error", err.Error()). + WithField("endpoint", endpoint). + Errorf("cannot dial RPC endpoint") + cancel() + return nil + } + + go func() { + connState := connectivity.Idle + stateChangedNotify := make(chan bool) + notifyFunc := func(st connectivity.State) { + ok := conn.WaitForStateChange(cxt, st) + stateChangedNotify <- ok + } + go notifyFunc(connState) + + for { + select { + case ok := <-stateChangedNotify: + if !ok { + return + } + connState = conn.GetState() + log.Debugf("Bookkeeping client %s", connState.String()) + go notifyFunc(connState) + case <-time.After(2 * time.Minute): + if conn.GetState() != connectivity.Ready { + conn.ResetConnectBackoff() + } + case <-cxt.Done(): + return + } + } + }() + + client := &RpcClient{ + EnvironmentServiceClient: bkpb.NewEnvironmentServiceClient(conn), + FlpServiceClient: bkpb.NewFlpServiceClient(conn), + LogServiceClient: bkpb.NewLogServiceClient(conn), + RunServiceClient: bkpb.NewRunServiceClient(conn), + LhcFillServiceClient: bkpb.NewLhcFillServiceClient(conn), + conn: conn, + cancel: cancel, + } + + return client +} + +func (m *RpcClient) GetConnState() connectivity.State { + if m.conn == nil { + return connectivity.Idle + } + return m.conn.GetState() +} + +func (m *RpcClient) Close() error { + m.cancel() + return m.conn.Close() +} diff --git a/core/integration/bookkeeping/plugin.go b/core/integration/bookkeeping/plugin.go new file mode 100644 index 000000000..49344a7af --- /dev/null +++ b/core/integration/bookkeeping/plugin.go @@ -0,0 +1,1568 @@ +/* +* === This file is part of ALICE O² === +* +* Copyright 2021-2024 CERN and copyright holders of ALICE O². +* Author: Claire Guyot +* Teo Mrnjavac +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* +* In applying this license CERN does not waive the privileges and +* immunities granted to it by virtue of its status as an +* Intergovernmental Organization or submit itself to any jurisdiction. + */ + +// Generate protofiles using the .protos imported from Bookkeeping in the Makefile +//go:generate protoc --go_out=. --go_opt=paths=source_relative protos/bkcommon.proto +//go:generate protoc --go_out=. --go_opt=paths=source_relative --go-grpc_opt=paths=source_relative --go-grpc_out=require_unimplemented_servers=false:. protos/environment.proto +//go:generate protoc --go_out=. --go_opt=paths=source_relative --go-grpc_opt=paths=source_relative --go-grpc_out=require_unimplemented_servers=false:. protos/flp.proto +//go:generate protoc --go_out=. --go_opt=paths=source_relative --go-grpc_opt=paths=source_relative --go-grpc_out=require_unimplemented_servers=false:. protos/log.proto +//go:generate protoc --go_out=. --go_opt=paths=source_relative --go-grpc_opt=paths=source_relative --go-grpc_out=require_unimplemented_servers=false:. protos/run.proto +//go:generate protoc --go_out=. --go_opt=paths=source_relative --go-grpc_opt=paths=source_relative --go-grpc_out=require_unimplemented_servers=false:. protos/lhcFill.proto + +// Package bookkeeping provides integration with the ALICE Bookkeeping system +// for tracking runs and retrieving LHC fill information. +package bookkeeping + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "net/url" + "strconv" + "strings" + "sync" + "time" + + bkpb "github.com/AliceO2Group/Control/core/integration/bookkeeping/protos" + "google.golang.org/grpc" + + "github.com/AliceO2Group/Control/common/logger/infologger" + "github.com/AliceO2Group/Control/common/utils/uid" + "github.com/AliceO2Group/Control/core/environment" + "github.com/AliceO2Group/Control/core/integration" + "github.com/AliceO2Group/Control/core/workflow/callable" + "github.com/spf13/viper" +) + +const ( + BKP_RUN_TIMEOUT = 30 * time.Second + BKP_ENV_TIMEOUT = 30 * time.Second + BKP_FILL_TIMEOUT = 30 * time.Second +) + +type Plugin struct { + bookkeepingHost string + bookkeepingPort int + + // Plugin client connecting to the Bookkeeping gRPC server + bookkeepingClient *RpcClient + + // Indicators linked to each ongoing environment that give some plugin-specific + // data about the status of the current run (of that environment) + missingUpdateRunStarts map[string] /*envId*/ bool + missingUpdateRunStartsMu sync.Mutex + pendingRunStops map[string] /*envId*/ int64 + pendingRunStopsMu sync.Mutex + pendingO2Starts map[string] /*envId*/ bool + pendingO2StartsMu sync.Mutex + pendingO2Stops map[string] /*envId*/ bool + pendingO2StopsMu sync.Mutex + pendingTrgStarts map[string] /*envId*/ bool + pendingTrgStartsMu sync.Mutex + pendingTrgStops map[string] /*envId*/ bool + pendingTrgStopsMu sync.Mutex +} + +/**********************************************/ +// Plugin and inherited methods Instanciation // +/**********************************************/ +func NewPlugin(endpoint string) integration.Plugin { + u, err := url.Parse(endpoint) + if err != nil { + log.WithField("endpoint", endpoint). + WithError(err). + Error("bad service endpoint") + return nil + } + + portNumber, _ := strconv.Atoi(u.Port()) + + return &Plugin{ + bookkeepingHost: u.Hostname(), + bookkeepingPort: portNumber, + bookkeepingClient: nil, + missingUpdateRunStarts: make(map[string]bool), + pendingRunStops: make(map[string]int64), + pendingO2Starts: make(map[string]bool), + pendingO2Stops: make(map[string]bool), + pendingTrgStarts: make(map[string]bool), + pendingTrgStops: make(map[string]bool), + } +} + +func (p *Plugin) GetName() string { + return "bookkeeping" +} + +func (p *Plugin) GetPrettyName() string { + return "Bookkeeping" +} + +func (p *Plugin) GetEndpoint() string { + return viper.GetString("bookkeepingBaseUri") +} + +func (p *Plugin) GetConnectionState() string { + return "READY" +} + +/*******************************************************/ +// Utility methods for GetData and GetEnvironmentsData // +/*******************************************************/ +func (p *Plugin) getMissingUpdateRunStartsForEnvs(envIds []uid.ID) map[uid.ID]bool { + p.missingUpdateRunStartsMu.Lock() + defer p.missingUpdateRunStartsMu.Unlock() + + if p.missingUpdateRunStarts == nil { + return nil + } + + out := make(map[uid.ID]bool) + + for _, envId := range envIds { + if missingStart, ok := p.missingUpdateRunStarts[envId.String()]; ok { + out[envId] = missingStart + } + } + return out +} + +func (p *Plugin) getPendingRunStopsForEnvs(envIds []uid.ID) map[uid.ID]string { + p.pendingRunStopsMu.Lock() + defer p.pendingRunStopsMu.Unlock() + + if p.pendingRunStops == nil { + return nil + } + + out := make(map[uid.ID]string) + + for _, envId := range envIds { + if runStop, ok := p.pendingRunStops[envId.String()]; ok { + out[envId] = strconv.FormatInt(runStop, 10) + } + } + return out +} + +func (p *Plugin) getPendingO2StartsForEnvs(envIds []uid.ID) map[uid.ID]bool { + p.pendingO2StartsMu.Lock() + defer p.pendingO2StartsMu.Unlock() + + if p.pendingO2Starts == nil { + return nil + } + + out := make(map[uid.ID]bool) + + for _, envId := range envIds { + if o2Start, ok := p.pendingO2Starts[envId.String()]; ok { + out[envId] = o2Start + } + } + return out +} + +func (p *Plugin) getPendingO2StopsForEnvs(envIds []uid.ID) map[uid.ID]bool { + p.pendingO2StopsMu.Lock() + defer p.pendingO2StopsMu.Unlock() + + if p.pendingO2Stops == nil { + return nil + } + + out := make(map[uid.ID]bool) + + for _, envId := range envIds { + if o2Stop, ok := p.pendingO2Stops[envId.String()]; ok { + out[envId] = o2Stop + } + } + return out +} + +func (p *Plugin) getPendingTrgStartsForEnvs(envIds []uid.ID) map[uid.ID]bool { + p.pendingTrgStartsMu.Lock() + defer p.pendingTrgStartsMu.Unlock() + + if p.pendingTrgStarts == nil { + return nil + } + + out := make(map[uid.ID]bool) + + for _, envId := range envIds { + if trgStart, ok := p.pendingTrgStarts[envId.String()]; ok { + out[envId] = trgStart + } + } + return out +} + +func (p *Plugin) getPendingTrgStopsForEnvs(envIds []uid.ID) map[uid.ID]bool { + p.pendingTrgStopsMu.Lock() + defer p.pendingTrgStopsMu.Unlock() + + if p.pendingTrgStops == nil { + return nil + } + + out := make(map[uid.ID]bool) + + for _, envId := range envIds { + if trgStop, ok := p.pendingTrgStops[envId.String()]; ok { + out[envId] = trgStop + } + } + return out +} + +/*******************************************/ +// GetData and GetEnvironmentsData methods // +/*******************************************/ + +// Make plugin-specific data across all instances available per data field +func (p *Plugin) GetData(_ []any) string { + if p == nil || p.bookkeepingClient == nil { + return "" + } + + envIds := environment.ManagerInstance().Ids() + + outMap := make(map[string]interface{}) + outMap["missingUpdateRunStarts"] = p.getMissingUpdateRunStartsForEnvs(envIds) + outMap["pendingRunStops"] = p.getPendingRunStopsForEnvs(envIds) + outMap["pendingO2Starts"] = p.getPendingO2StartsForEnvs(envIds) + outMap["pendingO2Stops"] = p.getPendingO2StopsForEnvs(envIds) + outMap["pendingTrgStarts"] = p.getPendingTrgStartsForEnvs(envIds) + outMap["pendingTrgStops"] = p.getPendingTrgStopsForEnvs(envIds) + + out, err := json.Marshal(outMap) + if err != nil { + return "" + } + return string(out[:]) +} + +// Make plugin-specific data across all instances available per environment +func (p *Plugin) GetEnvironmentsData(envIds []uid.ID) map[uid.ID]string { + if p == nil || p.bookkeepingClient == nil { + return nil + } + + inMissingStart := p.getMissingUpdateRunStartsForEnvs(envIds) + inRunStopMap := p.getPendingRunStopsForEnvs(envIds) + inO2StartMap := p.getPendingO2StartsForEnvs(envIds) + inO2StopMap := p.getPendingO2StopsForEnvs(envIds) + inTrgStartMap := p.getPendingTrgStartsForEnvs(envIds) + inTrgStopMap := p.getPendingTrgStopsForEnvs(envIds) + + envMap := make(map[string]interface{}) + out := make(map[uid.ID]string) + + for _, envId := range envIds { + if missingStart, ok := inMissingStart[envId]; ok { + envMap["missingUpdateRunStart"] = missingStart + } + if runStop, ok := inRunStopMap[envId]; ok { + envMap["runNumber"] = runStop + } + if o2Start, ok := inO2StartMap[envId]; ok { + envMap["pendingO2Start"] = o2Start + } + if o2Stop, ok := inO2StopMap[envId]; ok { + envMap["pendingO2Stop"] = o2Stop + } + if trgStart, ok := inTrgStartMap[envId]; ok { + envMap["pendingTrgStart"] = trgStart + } + if trgStop, ok := inTrgStopMap[envId]; ok { + envMap["pendingTrgStop"] = trgStop + } + outJson, err := json.Marshal(envMap) + if err == nil { + out[envId] = string(outJson[:]) + } + } + return out +} + +func (p *Plugin) GetEnvironmentsShortData(envIds []uid.ID) map[uid.ID]string { + return p.GetEnvironmentsData(envIds) +} + +/*************************************/ +// Initialize the Bookkeeping client // +/*************************************/ +func (p *Plugin) Init(instanceId string) error { + if p.bookkeepingClient == nil { + cxt, cancel := context.WithCancel(context.Background()) + p.bookkeepingClient = NewClient(cxt, cancel, p.GetEndpoint()) + if p.bookkeepingClient == nil { + return fmt.Errorf("failed to connect to Bookkeeping service on %s", p.GetEndpoint()) + } + log.Debug("Bookkeeping plugin initialized") + } + return nil +} + +/*************************************/ +// ObjectStack and CallStack methods // +/*************************************/ + +// ObjectStack method +func (p *Plugin) ObjectStack(_ map[string]string, _ map[string]string) (stack map[string]interface{}) { + stack = make(map[string]interface{}) + return stack +} + +// CallStack method, which adds functions to the call stack that can then be called +// under certain conditions via hooks and triggers in the readout-dataflow workflow +func (p *Plugin) CallStack(data interface{}) (stack map[string]interface{}) { + + /*** Set variables that are available to all functions in the call stack ***/ + call, ok := data.(*callable.Call) + if !ok { + return + } + // The varStack is a map of variables available throughout the core and is a collection + // of Defaults, Vars and UserVars from the environment, as well as runtime variables set + // in other parts of AliECS + varStack := call.VarStack + envId, ok := varStack["environment_id"] + if !ok { + err := errors.New("cannot acquire environment ID") + log.Error(err) + + // These two lines are necessary to get the error out of the scope of only that function call + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = "Bookkeeping plugin Call Stack failed" + return + } + trigger, ok := varStack["__call_trigger"] + if !ok { + err := errors.New("cannot acquire trigger from varStack") + log.WithField("call", call). + WithField("partition", envId). + Error(err) + + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = "Bookkeeping plugin Call Stack failed" + return + } + var err error + parsedEnvId, err := uid.FromString(envId) + if err != nil { + if strings.Contains(trigger, "DESTROY") || strings.Contains(trigger, "GO_ERROR") { + log.WithField("partition", envId). + Debug("cannot parse environment ID when DESTROY or GO_ERROR transition") + return + } + log.WithError(err). + WithField("partition", envId). + Error("cannot parse environment ID") + + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = "Bookkeeping plugin Call Stack failed" + return + } + envMan := environment.ManagerInstance() + env, err := envMan.Environment(parsedEnvId) + if err != nil { + if strings.Contains(trigger, "DESTROY") || strings.Contains(trigger, "GO_ERROR") { + log.WithField("partition", envId). + Debug("cannot acquire environment from parsed environment ID when DESTROY or GO_ERROR transition") + return + } + log.WithError(err). + WithField("partition", envId). + Error("cannot acquire environment from parsed environment ID") + + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = "Bookkeeping plugin Call Stack failed" + return + } + + stack = make(map[string]interface{}) + + /*****************************/ + /*** Run related functions ***/ + /*****************************/ + + // StartOfRun function that creates the run in Bookkeeping and initializes it with minimal information + stack["StartOfRun"] = func() (out string) { + + /*** Get variables that are available at run creation (before START_ACTIVITY) ***/ + callFailedStr := "Bookkeeping StartOfRun call failed" + + rn := varStack["run_number"] + var runNumber64 int64 + runNumber64, err = strconv.ParseInt(rn, 10, 32) + if err != nil { + log.WithField("partition", envId). + WithError(err). + Error("cannot acquire run number for Bookkeeping SOR") + + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + return + } + + if p.bookkeepingClient == nil { + err = fmt.Errorf("Bookkeeping plugin not initialized, StartOfRun impossible") + + log.WithError(err). + WithField("level", infologger.IL_Support). + WithField("endpoint", viper.GetString("bookkeepingBaseUri")). + WithField("run", runNumber64). + WithField("partition", envId). + WithField("call", "StartOfRun"). + Error("Bookkeeping plugin SOR error") + + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + + return + } + + runNumber := env.GetCurrentRunNumber() + runNumber32 := int32(runNumber64) + + rnString := strconv.FormatUint(uint64(runNumber), 10) + + flps := env.GetFLPs() + + // The enabled status of CTP Readout and its FLP is governed by the variable ctp_readout_enabled, so it requires + // special treatment to be included in the list of FLPs + ctpReadoutEnabled, err := strconv.ParseBool(env.GetKV("", "ctp_readout_enabled")) + if err != nil { + log.WithError(err). + WithField("run", runNumber64). + WithField("partition", envId). + WithField("call", "StartOfRun"). + Warning("cannot parse ctp_readout_enabled") + } + if ctpReadoutEnabled { + ctpReadoutFlp := env.GetKV("", "ctp_readout_host") + if len(ctpReadoutFlp) > 0 { + flps = append(flps, ctpReadoutFlp) + } + } + + epns, err := strconv.ParseInt(env.GetKV("", "odc_n_epns"), 10, 0) + if err != nil { + log.WithError(err). + WithField("run", runNumber64). + WithField("partition", envId). + WithField("call", "StartOfRun"). + Warning("cannot parse number of EPNs") + } + ddEnabled, err := strconv.ParseBool(env.GetKV("", "dd_enabled")) + if err != nil { + log.WithError(err). + WithField("run", runNumber64). + WithField("partition", envId). + WithField("call", "StartOfRun"). + Warning("cannot parse DD enabled") + } + dcsEnabled, err := strconv.ParseBool(env.GetKV("", "dcs_enabled")) + if err != nil { + log.WithError(err). + WithField("run", runNumber64). + WithField("partition", envId). + WithField("call", "StartOfRun"). + Warning("cannot parse DCS enabled") + } + epnEnabled, err := strconv.ParseBool(env.GetKV("", "epn_enabled")) + if err != nil { + log.WithError(err). + WithField("run", runNumber64). + WithField("partition", envId). + WithField("call", "StartOfRun"). + Warning("cannot parse EPN enabled") + } + odcTopology := env.GetKV("", "odc_topology") + odcTopologyFullname, ok := env.Workflow().GetVars().Get("odc_topology_fullname") + if !ok { + log.WithField("run", runNumber64). + WithField("partition", envId). + WithField("call", "UpdateRun"). + Warning("cannot acquire ODC topology fullname") + } + detectors := env.GetActiveDetectors().StringList() + var detectorsList = make([]bkpb.Detector, 0) + for _, name := range detectors { + detectorsList = append(detectorsList, bkpb.Detector(bkpb.Detector_value["DETECTOR_"+name])) + } + + /*** Create and send creation requests to Bookkeeping server ***/ + + inRun := bkpb.RunCreationRequest{ + RunNumber: runNumber32, + EnvironmentId: env.Id().String(), + NDetectors: int32(len(env.GetActiveDetectors())), + NEpns: int32(epns), + NFlps: int32(len(flps)), + RunType: bkpb.RunType(env.GetRunType()), + DdFlp: ddEnabled, + Dcs: dcsEnabled, + Epn: epnEnabled, + EpnTopology: odcTopology, + OdcTopologyFullName: &odcTopologyFullname, + Detectors: detectorsList, + } + + // Send the run creation request + timeout := callable.AcquireTimeout(BKP_RUN_TIMEOUT, varStack, "CreateRun", envId) + ctx, cancel := context.WithTimeout(context.Background(), timeout) + defer cancel() + _, err1 := p.bookkeepingClient.RunServiceClient.Create(ctx, &inRun, grpc.EmptyCallOption{}) + if err1 != nil { + // If the run creation request fails, we still send a log creation request + log.WithError(err1). + WithField("run", runNumber64). + WithField("partition", envId). + WithField("call", "RunServiceClient.Create"). + Error("Bookkeeping API RunServiceClient: Create error") + + runNumbers := make([]int32, 0) + inLog := bkpb.LogCreationRequest{ + RunNumbers: runNumbers, + Title: fmt.Sprintf("Log for run %s and environment %s", rnString, env.Id().String()), + Text: env.GetVarsAsString(), + ParentLogId: nil, + } + + timeout = callable.AcquireTimeout(BKP_RUN_TIMEOUT, varStack, "CreateLog", envId) + ctx, cancel = context.WithTimeout(context.Background(), timeout) + defer cancel() + _, err2 := p.bookkeepingClient.LogServiceClient.Create(ctx, &inLog, grpc.EmptyCallOption{}) + if err2 != nil { + log.WithError(err2). + WithField("run", runNumber64). + WithField("partition", envId). + WithField("call", "LogServiceClient.Create"). + Error("Bookkeeping API LogServiceClient: Create error") + err = errors.New(err1.Error() + err2.Error()) + + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + return + } else { + log.WithField("run", runNumber64). + WithField("partition", envId). + Debug("Bookkeeping API LogServiceClient: Create call successful") + } + return + } else { + // If the run creation request succeeds, we set the run status indicators + p.missingUpdateRunStartsMu.Lock() + p.missingUpdateRunStarts[envId] = true + p.missingUpdateRunStartsMu.Unlock() + p.pendingRunStopsMu.Lock() + p.pendingRunStops[envId] = runNumber64 + p.pendingRunStopsMu.Unlock() + p.pendingO2StartsMu.Lock() + p.pendingO2Starts[envId] = true + p.pendingO2StartsMu.Unlock() + p.pendingO2StopsMu.Lock() + p.pendingO2Stops[envId] = true + p.pendingO2StopsMu.Unlock() + p.pendingTrgStartsMu.Lock() + p.pendingTrgStarts[envId] = true + p.pendingTrgStartsMu.Unlock() + p.pendingTrgStopsMu.Lock() + p.pendingTrgStops[envId] = true + p.pendingTrgStopsMu.Unlock() + log.WithField("run", runNumber64). + WithField("partition", envId). + Debug("Bookkeeping API RunServiceClient: Create call successful") + } + + runNumbers := make([]int32, 0) + runNumbers = append(runNumbers, runNumber32) + inLog := bkpb.LogCreationRequest{ + RunNumbers: runNumbers, + Title: fmt.Sprintf("Log for run %s and environment %s", rnString, env.Id().String()), + Text: env.GetVarsAsString(), + ParentLogId: nil, + } + + // Send the log creation request + timeout = callable.AcquireTimeout(BKP_RUN_TIMEOUT, varStack, "CreateLog", envId) + ctx, cancel = context.WithTimeout(context.Background(), timeout) + defer cancel() + _, err = p.bookkeepingClient.LogServiceClient.Create(ctx, &inLog, grpc.EmptyCallOption{}) + if err != nil { + log.WithError(err). + WithField("run", runNumber64). + WithField("partition", envId). + WithField("call", "LogServiceClient.Create"). + Error("Bookkeeping API LogServiceClient: Create error") + + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + return + } else { + log.WithField("run", runNumber64). + WithField("partition", envId). + Debug("Bookkeeping API LogServiceClient: Create call successful") + } + + var inFlps = bkpb.ManyFlpsCreationRequest{ + Flps: make([]*bkpb.FlpCreationRequest, len(flps)), + } + + for _, flp := range flps { + inFlp := &bkpb.FlpCreationRequest{ + Name: flp, + Hostname: flp, + RunNumber: &runNumber32, + } + inFlps.Flps = append(inFlps.Flps, inFlp) + } + + // Send the flps creation request + timeout = callable.AcquireTimeout(BKP_RUN_TIMEOUT, varStack, "CreateFlp", envId) + ctx, cancel = context.WithTimeout(context.Background(), timeout) + defer cancel() + _, err = p.bookkeepingClient.FlpServiceClient.CreateMany(ctx, &inFlps, grpc.EmptyCallOption{}) + if err != nil { + log.WithError(err). + WithField("run", runNumber64). + WithField("partition", envId). + WithField("call", "FlpServiceClient.CreateMany"). + Error("Bookkeeping API FlpServiceClient: CreateMany error") + + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + return + } else { + log.WithField("run", runNumber64). + WithField("partition", envId). + Debug("Bookkeeping API FlpServiceClient: CreateMany call successful") + } + return + } + + // Update function common to UpdateRunStart and UpdateRunStop + updateRunFunc := func(runNumber64 int64, state string, timeO2StartInput string, timeO2EndInput string, timeTrgStartInput string, timeTrgEndInput string) (out string) { + + /*** Get variables that are available when the update is called ***/ + callFailedStr := "Bookkeeping UpdateRun call failed" + trgGlobalRunEnabled, err := strconv.ParseBool(env.GetKV("", "trg_global_run_enabled")) + if err != nil { + log.WithError(err). + WithField("run", runNumber64). + WithField("partition", envId). + WithField("call", "UpdateRun"). + Warning("cannot parse TRG global run enabled") + } + trgEnabled, err := strconv.ParseBool(env.GetKV("", "trg_enabled")) + if err != nil { + log.WithError(err). + WithField("run", runNumber64). + WithField("partition", envId). + WithField("call", "UpdateRun"). + Warning("cannot parse TRG enabled") + } + var trg = "" + if trgEnabled == false { + trg = "OFF" + } else { + if trgGlobalRunEnabled == false { + trg = "LTU" + } else { + trg = "CTP" + } + } + pdpConfig, ok := varStack["pdp_config_option"] + if !ok { + log.WithField("run", runNumber64). + WithField("partition", envId). + WithField("call", "UpdateRun"). + Warning("cannot acquire PDP workflow configuration mode") + } + pdpTopology, ok := varStack["pdp_topology_description_library_file"] + if !ok { + log.WithField("run", runNumber64). + WithField("partition", envId). + WithField("call", "UpdateRun"). + Warning("cannot acquire PDP topology description library file") + } + pdpParameters, ok := varStack["pdp_workflow_parameters"] + if !ok { + log.WithField("run", runNumber64). + WithField("partition", envId). + WithField("call", "UpdateRun"). + Warning("cannot acquire PDP workflow parameters") + } + pdpBeam, ok := varStack["pdp_beam_type"] + if !ok { + log.WithField("run", runNumber64). + WithField("partition", envId). + WithField("call", "UpdateRun"). + Warning("cannot acquire PDP beam type") + } + tfbMode := env.GetKV("", "tfb_dd_mode") + odcTopologyFullname, ok := env.Workflow().GetVars().Get("odc_topology_fullname") + if !ok { + log.WithField("run", runNumber64). + WithField("partition", envId). + WithField("call", "UpdateRun"). + Warning("cannot acquire ODC topology fullname") + } + lhcPeriod := env.GetKV("", "lhc_period") + readoutUri, ok := varStack["readout_cfg_uri"] + + // O2 timestamps formatting + var timeO2StartOutput *int64 = nil + timeO2StartTemp, err := strconv.ParseInt(timeO2StartInput, 10, 64) + if err != nil { + log.WithField("run", runNumber64). + WithField("time", timeO2StartInput). + Warning("cannot parse O2 start time") + timeO2StartTemp = -1 + } + if timeO2StartInput != "" || timeO2StartTemp > 0 { + timeO2StartOutput = &timeO2StartTemp + } + var timeO2EndTemp int64 = -1 + var timeO2EndOutput *int64 = nil + if timeO2EndInput != "" { + timeO2EndTemp, err = strconv.ParseInt(timeO2EndInput, 10, 64) + if err != nil { + log.WithField("run", runNumber64). + WithField("time", timeO2EndInput). + Warning("cannot parse O2 end time") + timeO2EndTemp = -1 + } + } + if timeO2EndInput != "" || timeO2EndTemp > 0 { + timeO2EndOutput = &timeO2EndTemp + } + + // Trg timestamps formatting + var timeTrgStartTemp int64 = -1 + var timeTrgStartOutput *int64 = nil + var timeTrgEndTemp int64 = -1 + var timeTrgEndOutput *int64 = nil + if trg == "LTU" || trg == "CTP" { + timeTrgStartTemp, err = strconv.ParseInt(timeTrgStartInput, 10, 64) + if err != nil { + log.WithField("run", runNumber64). + WithField("time", timeTrgStartInput). + Warning("cannot parse Trg start time") + timeTrgStartTemp = -1 + } + if timeTrgStartInput != "" || timeTrgStartTemp > 0 { + timeTrgStartOutput = &timeTrgStartTemp + } + // If UpdateRunStart was not called and the Trg start time is missing, + // it is set to the O2 start time. + p.missingUpdateRunStartsMu.Lock() + currentMissingUpdateRunStarts := p.missingUpdateRunStarts[envId] + p.missingUpdateRunStartsMu.Unlock() + if currentMissingUpdateRunStarts == true && trgEnabled && timeTrgStartOutput == nil { + p.pendingO2StartsMu.Lock() + currentPendingO2Starts := p.pendingO2Starts[envId] + p.pendingO2StartsMu.Unlock() + if currentPendingO2Starts == false { + timeTrgStartOutput = timeO2StartOutput + p.pendingTrgStartsMu.Lock() + p.pendingTrgStarts[envId] = false + p.pendingTrgStartsMu.Unlock() + log.WithField("run", runNumber64). + WithField("partition", envId). + Debug("Bookkeeping API RunServiceClient: Update call: completing missing Trg start timestamp after missing UpdateRunStart call") + } else { + log.WithField("run", runNumber64). + WithField("partition", envId). + Warning("Bookkeeping API RunServiceClient: Update call: run information incomplete, missing O2 start time after missing UpdateRunStart call") + } + } + timeTrgEndTemp, err = strconv.ParseInt(timeTrgEndInput, 10, 64) + if err != nil { + log.WithField("run", runNumber64). + WithField("time", timeTrgEndInput). + Warning("cannot parse Trg end time") + timeTrgEndTemp = -1 + } + if timeTrgEndInput != "" || timeTrgEndTemp > 0 { + timeTrgEndOutput = &timeTrgEndTemp + } + } + + // Create the run update struct + inRun := bkpb.RunUpdateRequest{ + RunNumber: int32(runNumber64), + TimeO2Start: timeO2StartOutput, + TimeO2End: timeO2EndOutput, + TimeTrgStart: timeTrgStartOutput, + TimeTrgEnd: timeTrgEndOutput, + TriggerValue: &trg, + PdpConfigOption: &pdpConfig, + PdpTopologyDescriptionLibraryFile: &pdpTopology, + TfbDdMode: &tfbMode, + LhcPeriod: &lhcPeriod, + OdcTopologyFullName: &odcTopologyFullname, + PdpWorkflowParameters: &pdpParameters, + PdpBeamType: &pdpBeam, + ReadoutCfgUri: &readoutUri, + } + + // If it is an UpdateRunStop call, then SOR timestamps can be updated in the request under certain conditions + if function, ok := varStack["__call_func"]; ok && strings.Contains(function, "UpdateRunStop") { + // If O2 start time is not available after UpdateRunStart was missing, + // we can't update it anymore as we have no way of knowing/approximating it. + // Else if O2 start time or Trg start time is available, we can update it safely + // because it means that even if they weren't missing during the UpdateRunStart call, + // they will be the same and can be overwritten. + p.missingUpdateRunStartsMu.Lock() + currentMissingUpdateRunStarts := p.missingUpdateRunStarts[envId] + p.missingUpdateRunStartsMu.Unlock() + if currentMissingUpdateRunStarts == true && timeO2StartOutput == nil { + log.WithField("run", runNumber64). + WithField("partition", envId). + Warning("Bookkeeping API RunServiceClient: Update call: run information incomplete, missing SOR timestamps after missing UpdateRunStart call") + } else if timeO2StartOutput == nil && timeTrgStartOutput != nil { + inRun = bkpb.RunUpdateRequest{ + RunNumber: int32(runNumber64), + TimeO2End: timeO2EndOutput, + TimeTrgStart: timeTrgStartOutput, + TimeTrgEnd: timeTrgEndOutput, + TriggerValue: &trg, + PdpConfigOption: &pdpConfig, + PdpTopologyDescriptionLibraryFile: &pdpTopology, + TfbDdMode: &tfbMode, + LhcPeriod: &lhcPeriod, + OdcTopologyFullName: &odcTopologyFullname, + PdpWorkflowParameters: &pdpParameters, + PdpBeamType: &pdpBeam, + ReadoutCfgUri: &readoutUri, + } + } else if timeO2StartOutput != nil && timeTrgStartOutput == nil { + inRun = bkpb.RunUpdateRequest{ + RunNumber: int32(runNumber64), + TimeO2Start: timeO2StartOutput, + TimeO2End: timeO2EndOutput, + TimeTrgEnd: timeTrgEndOutput, + TriggerValue: &trg, + PdpConfigOption: &pdpConfig, + PdpTopologyDescriptionLibraryFile: &pdpTopology, + TfbDdMode: &tfbMode, + LhcPeriod: &lhcPeriod, + OdcTopologyFullName: &odcTopologyFullname, + PdpWorkflowParameters: &pdpParameters, + PdpBeamType: &pdpBeam, + ReadoutCfgUri: &readoutUri, + } + } + } + + // Send the run update request + timeout := callable.AcquireTimeout(BKP_RUN_TIMEOUT, varStack, "UpdateRun", envId) + ctx, cancel := context.WithTimeout(context.Background(), timeout) + defer cancel() + _, err = p.bookkeepingClient.RunServiceClient.Update(ctx, &inRun, grpc.EmptyCallOption{}) + if err != nil { + log.WithError(err). + WithField("run", runNumber64). + WithField("partition", envId). + WithField("call", "RunServiceClient.Update"). + Error("Bookkeeping API RunServiceClient: Update error") + + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + return + } else { + var updatedRun string + if function, ok := varStack["__call_func"]; ok && strings.Contains(function, "UpdateRunStop") { + // If the update is successful and it is an UpdateRunStop call, we check if we are missing + // EOR timestamps, and if that is the case, we set them to Now() and send a new update request + p.missingUpdateRunStartsMu.Lock() + currentMissingUpdateRunStarts := p.missingUpdateRunStarts[envId] + p.missingUpdateRunStartsMu.Unlock() + if currentMissingUpdateRunStarts == true { + defer func() { + p.pendingO2StartsMu.Lock() + delete(p.pendingO2Starts, envId) + p.pendingO2StartsMu.Unlock() + }() + defer func() { + p.pendingTrgStartsMu.Lock() + delete(p.pendingTrgStarts, envId) + p.pendingTrgStartsMu.Unlock() + }() + } + defer func() { + p.pendingRunStopsMu.Lock() + delete(p.pendingRunStops, envId) + p.pendingRunStopsMu.Unlock() + }() + defer func() { + p.pendingO2StopsMu.Lock() + delete(p.pendingO2Stops, envId) + p.pendingO2StopsMu.Unlock() + }() + defer func() { + p.pendingTrgStopsMu.Lock() + delete(p.pendingTrgStops, envId) + p.pendingTrgStopsMu.Unlock() + }() + + p.pendingO2StopsMu.Lock() + currentPendingO2Stops := p.pendingO2Stops[envId] + p.pendingO2StopsMu.Unlock() + p.pendingTrgStopsMu.Lock() + currentPendingTrgStops := p.pendingTrgStops[envId] + p.pendingTrgStopsMu.Unlock() + if currentPendingO2Stops == true || (trgEnabled && currentPendingTrgStops == true) { + updatedRun = "INCOMPLETE STOP" + if currentPendingO2Stops == true { + timeO2EndTemp = time.Now().UnixMilli() + timeO2EndOutput = &timeO2EndTemp + log.WithField("run", runNumber64). + WithField("partition", envId). + Warning("Bookkeeping API RunServiceClient: Update call: run information incomplete, missing O2 end time") + } + if trgEnabled && currentPendingTrgStops == true { + timeTrgEndTemp = time.Now().UnixMilli() + timeTrgEndOutput = &timeO2EndTemp + log.WithField("run", runNumber64). + WithField("partition", envId). + Warning("Bookkeeping API RunServiceClient: Update call: run information incomplete, missing Trg end time") + } + inRun = bkpb.RunUpdateRequest{ + RunNumber: int32(runNumber64), + TimeO2End: timeO2EndOutput, + TimeTrgEnd: timeTrgEndOutput, + } + log.WithField("run", runNumber64). + WithField("partition", envId). + Debug("Bookkeeping API RunServiceClient: Update call: completing missing EOR timestamps") + + timeout = callable.AcquireTimeout(BKP_RUN_TIMEOUT, varStack, "UpdateRun", envId) + ctx, cancel = context.WithTimeout(context.Background(), timeout) + defer cancel() + _, err = p.bookkeepingClient.RunServiceClient.Update(ctx, &inRun, grpc.EmptyCallOption{}) + if err != nil { + log.WithError(err). + WithField("run", runNumber64). + WithField("partition", envId). + WithField("call", "RunServiceClient.Update"). + Error("Bookkeeping API RunServiceClient: Update error") + + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + return + } + } else { + updatedRun = "STOPPED" + } + } else if function, ok := varStack["__call_func"]; ok && strings.Contains(function, "UpdateRunStart") { + // If the update is successful and it is an UpdateRunStart call, we check if we are missing + // SOR timestamps, and if that is the case, we set them to Now() and send a new update request + defer func() { + p.pendingO2StartsMu.Lock() + delete(p.pendingO2Starts, envId) + p.pendingO2StartsMu.Unlock() + }() + defer func() { + p.pendingTrgStartsMu.Lock() + delete(p.pendingTrgStarts, envId) + p.pendingTrgStartsMu.Unlock() + }() + p.pendingO2StartsMu.Lock() + currentPendingO2Starts := p.pendingO2Starts[envId] + p.pendingO2StartsMu.Unlock() + p.pendingTrgStartsMu.Lock() + currentPendingTrgStarts := p.pendingTrgStarts[envId] + p.pendingTrgStartsMu.Unlock() + if currentPendingO2Starts == true || (trgEnabled && currentPendingTrgStarts == true) { + updatedRun = "INCOMPLETE START" + if currentPendingO2Starts == true { + timeO2StartTemp = time.Now().UnixMilli() + timeO2StartOutput = &timeO2StartTemp + log.WithField("run", runNumber64). + WithField("partition", envId). + Warning("Bookkeeping API RunServiceClient: Update call: run information incomplete, missing O2 start time") + } + if trgEnabled && currentPendingTrgStarts == true { + timeTrgStartTemp = time.Now().UnixMilli() + timeTrgStartOutput = &timeO2StartTemp + log.WithField("run", runNumber64). + WithField("partition", envId). + Warning("Bookkeeping API RunServiceClient: Update call: run information incomplete, missing Trg start time") + } + inRun = bkpb.RunUpdateRequest{ + RunNumber: int32(runNumber64), + TimeO2Start: timeO2StartOutput, + TimeTrgStart: timeTrgStartOutput, + } + log.WithField("run", runNumber64). + WithField("partition", envId). + Debug("Bookkeeping API RunServiceClient: Update call: completing missing SOR timestamps") + + timeout = callable.AcquireTimeout(BKP_RUN_TIMEOUT, varStack, "UpdateRun", envId) + ctx, cancel = context.WithTimeout(context.Background(), timeout) + defer cancel() + _, err = p.bookkeepingClient.RunServiceClient.Update(ctx, &inRun, grpc.EmptyCallOption{}) + if err != nil { + log.WithError(err). + WithField("run", runNumber64). + WithField("partition", envId). + WithField("call", "RunServiceClient.Update"). + Error("Bookkeeping API RunServiceClient: Update error") + + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + return + } + } else { + updatedRun = "STARTED" + } + } + log.WithField("run", runNumber64). + WithField("updated to", updatedRun). + WithField("partition", envId). + Debug("Bookkeeping API RunServiceClient: Update call successful") + } + return + } + + // UpdateRunStart function that updates the run in Bookkeeping with all available information + stack["UpdateRunStart"] = func() (out string) { + + /*** Get variables that are available at run start update (after START_ACTIVITY) ***/ + callFailedStr := "Bookkeeping UpdateRunStart call failed" + + rn := varStack["run_number"] + runNumber64, err := strconv.ParseInt(rn, 10, 32) + if err != nil { + log.WithField("partition", envId). + WithError(err). + Error("cannot acquire run number for Bookkeeping UpdateRunStart") + + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + return + } + + if p.bookkeepingClient == nil { + err = fmt.Errorf("Bookkeeping plugin not initialized, UpdateRunStart impossible") + + log.WithError(err). + WithField("level", infologger.IL_Support). + WithField("endpoint", viper.GetString("bookkeepingBaseUri")). + WithField("run", runNumber64). + WithField("partition", envId). + WithField("call", "UpdateRunStart"). + Error("Bookkeeping plugin UpdateRunStart error") + + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + + return + } + + O2StartTime := varStack["run_start_time_ms"] + if O2StartTime != "" { + p.pendingO2StartsMu.Lock() + p.pendingO2Starts[envId] = false + p.pendingO2StartsMu.Unlock() + } + TrgStartTime := varStack["trg_start_time_ms"] + if TrgStartTime != "" { + p.pendingTrgStartsMu.Lock() + p.pendingTrgStarts[envId] = false + p.pendingTrgStartsMu.Unlock() + } + + p.missingUpdateRunStartsMu.Lock() + p.missingUpdateRunStarts[envId] = false + p.missingUpdateRunStartsMu.Unlock() + + return updateRunFunc(runNumber64, "test", O2StartTime, "", TrgStartTime, "") + } + + // UpdateRunStop function that updates the run in Bookkeeping with all available information and deletes it from the plugin + stack["UpdateRunStop"] = func() (out string) { + + /*** Get variables that are available at run stop update (after STOP_ACTIVITY, GO_ERROR or DESTROY) ***/ + callFailedStr := "Bookkeeping UpdateRunStop call failed" + + rn := varStack["run_number"] + if len(rn) == 0 { + rn = varStack["last_run_number"] + log.WithField("partition", envId). + WithField(infologger.Run, rn). + Debug("run number no longer set, using last known run number for Bookkeeping UpdateRunStop") + } + runNumber64, err := strconv.ParseInt(rn, 10, 32) + if (strings.Contains(trigger, "DESTROY") || strings.Contains(trigger, "GO_ERROR")) && (rn == "" || runNumber64 == 0) { + log.WithField("partition", envId). + Debug("cannot update run on stop, no run for the environment") + return + } + if err != nil { + log.WithField("partition", envId). + WithError(err). + Error("cannot acquire run number for Bookkeeping UpdateRunStop") + + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + return + } + + if p.bookkeepingClient == nil { + err = fmt.Errorf("Bookkeeping plugin not initialized, UpdateRunStop impossible") + + log.WithError(err). + WithField("level", infologger.IL_Support). + WithField("endpoint", viper.GetString("bookkeepingBaseUri")). + WithField("run", runNumber64). + WithField("partition", envId). + WithField("call", "UpdateRunStop"). + Error("Bookkeeping plugin UpdateRunStop error") + + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + + return + } + + O2StartTime := varStack["run_start_time_ms"] + if O2StartTime != "" { + p.pendingO2StartsMu.Lock() + p.pendingO2Starts[envId] = false + p.pendingO2StartsMu.Unlock() + } + O2EndTime := varStack["run_end_time_ms"] + if O2EndTime != "" { + p.pendingO2StopsMu.Lock() + p.pendingO2Stops[envId] = false + p.pendingO2StopsMu.Unlock() + } + + TrgStartTime := varStack["trg_start_time_ms"] + if TrgStartTime != "" { + p.pendingTrgStartsMu.Lock() + p.pendingTrgStarts[envId] = false + p.pendingTrgStartsMu.Unlock() + } + TrgEndTime := varStack["trg_end_time_ms"] + if TrgEndTime != "" { + p.pendingTrgStopsMu.Lock() + p.pendingTrgStops[envId] = false + p.pendingTrgStopsMu.Unlock() + } + + p.pendingRunStopsMu.Lock() + _, hasPendingRunStops := p.pendingRunStops[envId] + p.pendingRunStopsMu.Unlock() + if hasPendingRunStops { + return updateRunFunc(runNumber64, "test", O2StartTime, O2EndTime, TrgStartTime, TrgEndTime) + } else { + log.WithField("partition", envId). + WithField("level", infologger.IL_Devel). + Debug("skipping UpdateRun call, no pending run number found") + return + } + } + + /*************************************/ + /*** Environment related functions ***/ + /*************************************/ + + // CreateEnv function that creates the environment in Bookkeeping + stack["CreateEnv"] = func() (out string) { + + /*** Get variables that are available at environment creation (before DEPLOY) ***/ + callFailedStr := "Bookkeeping CreateEnv call failed" + + if p.bookkeepingClient == nil { + err = fmt.Errorf("Bookkeeping plugin not initialized, CreateEnv impossible") + + log.WithError(err). + WithField("level", infologger.IL_Support). + WithField("endpoint", viper.GetString("bookkeepingBaseUri")). + WithField("partition", envId). + WithField("call", "CreateEnv"). + Error("Bookkeeping plugin CreateEnv error") + + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + + return + } + + var statusMessage = "" + envState := env.CurrentState() + if envState == "STANDBY" || envState == "DEPLOYED" { + statusMessage = "success: the environment is in " + envState + " state after creation" + } else { + statusMessage = "error: the environment is in " + envState + " state after creation" + } + + /*** Create and send environment creation request to Bookkeeping server ***/ + + inEnv := bkpb.EnvironmentCreationRequest{ + Id: env.Id().String(), + Status: &envState, + StatusMessage: &statusMessage, + } + + timeout := callable.AcquireTimeout(BKP_ENV_TIMEOUT, varStack, "UpdateRun", envId) + ctx, cancel := context.WithTimeout(context.Background(), timeout) + defer cancel() + response, err := p.bookkeepingClient.EnvironmentServiceClient.Create(ctx, &inEnv, grpc.EmptyCallOption{}) + if response != nil { + response.StatusMessage = "response not nil" + } + if err != nil { + log.WithError(err). + WithField("partition", envId). + WithField("call", "EnvironmentServiceClient.Create"). + Error("Bookkeeping API EnvironmentServiceClient: Create error") + + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + return + } else { + log.WithField("partition", envId). + Debug("Bookkeeping API EnvironmentServiceClient: Create call successful") + } + return + } + + // Update function for UpdateEnv + updateEnvFunc := func(envId string, toredownAt int64, status string, statusMessage string) (out string) { + callFailedStr := "Bookkeeping UpdateEnv call failed" + + /*** Create and send environment update request to Bookkeeping server ***/ + + inEnv := bkpb.EnvironmentUpdateRequest{ + Id: env.Id().String(), + Status: &status, + StatusMessage: &statusMessage, + } + + timeout := callable.AcquireTimeout(BKP_ENV_TIMEOUT, varStack, "UpdateRun", envId) + ctx, cancel := context.WithTimeout(context.Background(), timeout) + defer cancel() + _, err = p.bookkeepingClient.EnvironmentServiceClient.Update(ctx, &inEnv, grpc.EmptyCallOption{}) + if err != nil { + log.WithError(err). + WithField("partition", envId). + WithField("call", "EnvironmentServiceClient.Update"). + Error("Bookkeeping API EnvironmentServiceClient: Update error") + + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + return + } else { + log.WithField("partition", envId). + WithField("state", status). + Debug("Bookkeeping API EnvironmentServiceClient: Update call successful") + } + return + } + + // UpdateEnv function that updates the environment in Bookkeeping with all available information + stack["UpdateEnv"] = func() (out string) { + callFailedStr := "Bookkeeping UpdateEnv call failed" + + if p.bookkeepingClient == nil { + err = fmt.Errorf("Bookkeeping plugin not initialized, UpdateEnv impossible") + + log.WithError(err). + WithField("level", infologger.IL_Support). + WithField("endpoint", viper.GetString("bookkeepingBaseUri")). + WithField("partition", envId). + WithField("call", "UpdateEnv"). + Error("Bookkeeping plugin UpdateEnv error") + + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + + return + } + + // We update the environment status depending on the current environment state machine status + envState := env.CurrentState() + + if strings.Contains(trigger, "DESTROY") { + envState = "DESTROYED" + return updateEnvFunc(env.Id().String(), time.Time{}.UnixMilli(), envState, "the environment is DESTROYED after DESTROY transition") + } + if strings.Contains(trigger, "DEPLOY") { + if envState == "DEPLOYED" { + return updateEnvFunc(env.Id().String(), time.Time{}.UnixMilli(), envState, "success: the environment is in DEPLOYED state after DEPLOY transition") + } else { + return updateEnvFunc(env.Id().String(), time.Time{}.UnixMilli(), envState, "error: the environment is in "+envState+" state after DEPLOY transition") + } + } + if strings.Contains(trigger, "CONFIGURE") { + if envState == "CONFIGURED" { + return updateEnvFunc(env.Id().String(), time.Time{}.UnixMilli(), envState, "success: the environment is in CONFIGURED state after CONFIGURE transition") + } else { + return updateEnvFunc(env.Id().String(), time.Time{}.UnixMilli(), envState, "error: the environment is in "+envState+" state after CONFIGURE transition") + } + } + if strings.Contains(trigger, "RESET") { + if envState == "DEPLOYED" { + return updateEnvFunc(env.Id().String(), time.Time{}.UnixMilli(), envState, "success: the environment is in DEPLOYED state after RESET transition") + } else { + return updateEnvFunc(env.Id().String(), time.Time{}.UnixMilli(), envState, "error: the environment is in "+envState+" state after RESET transition") + } + } + if strings.Contains(trigger, "START_ACTIVITY") { + if envState == "RUNNING" { + return updateEnvFunc(env.Id().String(), time.Time{}.UnixMilli(), envState, "success: the environment is in RUNNING state after START_ACTIVITY transition") + } else { + return updateEnvFunc(env.Id().String(), time.Time{}.UnixMilli(), envState, "error: the environment is in "+envState+" state after START_ACTIVITY transition") + } + } + if strings.Contains(trigger, "STOP_ACTIVITY") { + if envState == "CONFIGURED" { + return updateEnvFunc(env.Id().String(), time.Time{}.UnixMilli(), envState, "success: the environment is in CONFIGURED state after STOP_ACTIVITY transition") + } else { + return updateEnvFunc(env.Id().String(), time.Time{}.UnixMilli(), envState, "error: the environment is in "+envState+" state after STOP_ACTIVITY transition") + } + } + if strings.Contains(trigger, "EXIT") { + if envState == "DONE" { + return updateEnvFunc(env.Id().String(), time.Now().UnixMilli(), envState, "success: the environment is in DONE state after EXIT transition") + } else { + return updateEnvFunc(env.Id().String(), time.Now().UnixMilli(), envState, "error: the environment is in "+envState+" state after EXIT transition") + } + } + if strings.Contains(trigger, "GO_ERROR") { + if envState == "ERROR" { + return updateEnvFunc(env.Id().String(), time.Time{}.UnixMilli(), envState, "success: the environment is in ERROR state after GO_ERROR transition") + } else { + return updateEnvFunc(env.Id().String(), time.Time{}.UnixMilli(), envState, "error: the environment is in "+envState+" state after GO_ERROR transition") + } + } + if strings.Contains(trigger, "RECOVER") { + if envState == "DEPLOYED" { + return updateEnvFunc(env.Id().String(), time.Time{}.UnixMilli(), envState, "success: the environment is in DEPLOYED state after RECOVER transition") + } else { + return updateEnvFunc(env.Id().String(), time.Time{}.UnixMilli(), envState, "error: the environment is in "+envState+" state after RECOVER transition") + } + } + log.WithField("partition", envId). + WithField("call", call). + Error("could not obtain transition in UpdateEnv from trigger: ", trigger) + + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + return + } + + /*************************************/ + /******** Fill info functions ********/ + /*************************************/ + + fetchLHCInfoForRun := func(runNumber int32) (out *bkpb.LHCFill, err error) { + runFetchRequest := bkpb.RunFetchRequest{RunNumber: runNumber} + + timeout := callable.AcquireTimeout(BKP_FILL_TIMEOUT, varStack, "RetrieveFillInfo", envId) + ctx, cancel := context.WithTimeout(context.Background(), timeout) + defer cancel() + log.WithField("partition", envId). + WithField("level", infologger.IL_Devel). + WithField("endpoint", viper.GetString("bookkeepingBaseUri")). + WithField("call", "RetrieveFillInfo"). + Debugf("requesting BK the run info for run %d", runNumber) + runWithRelations, err := p.bookkeepingClient.RunServiceClient.Get(ctx, &runFetchRequest, grpc.EmptyCallOption{}) + if err != nil { + return nil, err + } + lhcFill := runWithRelations.GetLhcFill() + if lhcFill == nil { + return nil, fmt.Errorf("lhcFill for run %d is nil", runNumber) + } + return lhcFill, nil + } + + fetchLatestLHCInfo := func() (out *bkpb.LHCFill, err error) { + lhcFillFetchRequest := bkpb.LastLhcFillFetchRequest{} + + timeout := callable.AcquireTimeout(BKP_FILL_TIMEOUT, varStack, "RetrieveFillInfo", envId) + ctx, cancel := context.WithTimeout(context.Background(), timeout) + defer cancel() + log.WithField("partition", envId). + WithField("level", infologger.IL_Devel). + WithField("endpoint", viper.GetString("bookkeepingBaseUri")). + WithField("call", "RetrieveFillInfo"). + Debug("requesting BK the latest LHC fill info") + LhcFillWithRelations, err := p.bookkeepingClient.LhcFillServiceClient.GetLast(ctx, &lhcFillFetchRequest, grpc.EmptyCallOption{}) + if err != nil { + return nil, err + } + lhcFill := LhcFillWithRelations.GetLhcFill() + if lhcFill == nil { + return nil, fmt.Errorf("lhcFill is nil") + } + return lhcFill, nil + } + + propagateLHCInfoToVarStack := func(lhcInfo *bkpb.LHCFill, varStack map[string]string) { + parentRole, ok := call.GetParentRole().(callable.ParentRole) + if !ok { + log.WithField("partition", envId). + WithField("level", infologger.IL_Devel). + WithField("endpoint", viper.GetString("bookkeepingBaseUri")). + WithField("call", "RetrieveFillInfo"). + Errorf("could not access the parent role when trying to propagate the LHC fill info") + return + } + parentRole.SetGlobalRuntimeVar("fill_info_fill_number", strconv.FormatInt(int64(lhcInfo.FillNumber), 10)) + parentRole.SetGlobalRuntimeVar("fill_info_filling_scheme", lhcInfo.FillingSchemeName) + parentRole.SetGlobalRuntimeVar("fill_info_beam_type", lhcInfo.BeamType) + if lhcInfo.StableBeamsStart != nil { + parentRole.SetGlobalRuntimeVar("fill_info_stable_beams_start_ms", strconv.FormatInt(*lhcInfo.StableBeamsStart, 10)) + } + if lhcInfo.StableBeamsEnd != nil { + parentRole.SetGlobalRuntimeVar("fill_info_stable_beams_end_ms", strconv.FormatInt(*lhcInfo.StableBeamsEnd, 10)) + } + log.WithField("partition", envId). + WithField("level", infologger.IL_Devel). + WithField("endpoint", viper.GetString("bookkeepingBaseUri")). + WithField("call", "RetrieveFillInfo"). + Infof("successfully updated the LHC Fill info (number %d, scheme %s, beam type %s)", + lhcInfo.FillNumber, lhcInfo.FillingSchemeName, lhcInfo.BeamType) + } + deleteLHCInfoInVarStack := func(varStack map[string]string) { + parentRole, ok := call.GetParentRole().(callable.ParentRole) + if !ok { + log.WithField("partition", envId). + WithField("level", infologger.IL_Devel). + WithField("endpoint", viper.GetString("bookkeepingBaseUri")). + WithField("call", "RetrieveFillInfo"). + Errorf("could not access the parent role when trying to clean up LHC fill info") + return + } + parentRole.DeleteGlobalRuntimeVar("fill_info_fill_number") + parentRole.DeleteGlobalRuntimeVar("fill_info_filling_scheme") + parentRole.DeleteGlobalRuntimeVar("fill_info_beam_type") + parentRole.DeleteGlobalRuntimeVar("fill_info_stable_beams_start_ms") + parentRole.DeleteGlobalRuntimeVar("fill_info_stable_beams_end_ms") + } + + stack["RetrieveFillInfo"] = func() (out string) { + callFailedStr := "Bookkeeping RetrieveFillInfo call failed" + if p.bookkeepingClient == nil { + log.WithError(err). + WithField("level", infologger.IL_Support). + WithField("endpoint", viper.GetString("bookkeepingBaseUri")). + WithField("partition", envId). + WithField("call", "RetrieveFillInfo"). + Error("bookkeeping plugin RetrieveFillInfo error") + call.VarStack["__call_error_reason"] = "bookkeeping plugin not initialized, RetrieveFillInfo impossible" + call.VarStack["__call_error"] = callFailedStr + return + } + + // First, we try to get fill info associated to a run. + // At the time of writing, it is not correctly associated for SYNTHETIC runs, + // but it might be in the future. + // Only if there is no fill info associated with a run (e.g. because we ask BK too early during SOR), + // we ask for the latest LHC fill and will use if the end time of stable beams is not set. + rn := varStack["run_number"] + runNumber64, err := strconv.ParseInt(rn, 10, 32) + if err != nil { + log.WithField("partition", envId). + WithField("level", infologger.IL_Devel). + WithField("endpoint", viper.GetString("bookkeepingBaseUri")). + WithField("call", "RetrieveFillInfo"). + WithError(err). + Info("cannot acquire run number for Bookkeeping fill info fetch, perhaps we are not in RUNNING") + + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + } else { + lhcFill, err := fetchLHCInfoForRun(int32(runNumber64)) + if err != nil { + log.WithField("partition", envId). + WithField("level", infologger.IL_Devel). + WithField("endpoint", viper.GetString("bookkeepingBaseUri")). + WithField("call", "RetrieveFillInfo"). + Infof("could not get LHC fill info associated to run %d, will try to get the latest fill. Details: %s", runNumber64, err.Error()) + } else { + log.WithField("partition", envId). + WithField("level", infologger.IL_Devel). + WithField("endpoint", viper.GetString("bookkeepingBaseUri")). + WithField("call", "RetrieveFillInfo"). + Infof("received a reply about fill info associated to run %d, filling", runNumber64) + propagateLHCInfoToVarStack(lhcFill, varStack) + log.WithField("partition", envId). + WithField("level", infologger.IL_Devel). + WithField("endpoint", viper.GetString("bookkeepingBaseUri")). + WithField("call", "RetrieveFillInfo"). + Infof("successfully updated the LHC Fill info for run %d (number %d, scheme %s, beam type %s)", + runNumber64, lhcFill.FillNumber, lhcFill.FillingSchemeName, lhcFill.BeamType) + return + } + } + + lhcFill, err := fetchLatestLHCInfo() + if err != nil { + log.WithError(err). + WithField("level", infologger.IL_Support). + WithField("endpoint", viper.GetString("bookkeepingBaseUri")). + WithField("partition", envId). + WithField("call", "RetrieveFillInfo"). + Error("bookkeeping plugin RetrieveFillInfo error") + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + } else if (lhcFill.StableBeamsStart != nil && *lhcFill.StableBeamsStart != 0) && (lhcFill.StableBeamsEnd == nil || *lhcFill.StableBeamsEnd == 0) { + // we enter here only if stable beams started and are not over (stable beams start exists && stable beams end does not exist) + log.WithField("partition", envId). + WithField("level", infologger.IL_Devel). + WithField("endpoint", viper.GetString("bookkeepingBaseUri")). + WithField("call", "RetrieveFillInfo"). + Debug("received a reply about fill info, filling the var stack") + propagateLHCInfoToVarStack(lhcFill, varStack) + } else { + log.WithField("partition", envId). + WithField("level", infologger.IL_Devel). + WithField("endpoint", viper.GetString("bookkeepingBaseUri")). + WithField("call", "RetrieveFillInfo"). + Debug("received a reply about fill info, but the latest fill is over or stable beams are not started yet, will not read the fill info and will delete any existing") + deleteLHCInfoInVarStack(varStack) + } + return + } + + return +} + +func (p *Plugin) Destroy() error { + return nil +} diff --git a/core/integration/bookkeeping/protos/bkcommon.pb.go b/core/integration/bookkeeping/protos/bkcommon.pb.go new file mode 100644 index 000000000..ca870e26b --- /dev/null +++ b/core/integration/bookkeeping/protos/bkcommon.pb.go @@ -0,0 +1,1180 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.34.1 +// protoc v5.29.3 +// source: protos/bkcommon.proto + +package bkpb + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type Detector int32 + +const ( + Detector_DETECTOR_NULL Detector = 0 + Detector_DETECTOR_TPC Detector = 3 + Detector_DETECTOR_TRD Detector = 4 + Detector_DETECTOR_TOF Detector = 5 + Detector_DETECTOR_HMP Detector = 6 + Detector_DETECTOR_PHS Detector = 7 + Detector_DETECTOR_CPV Detector = 8 + Detector_DETECTOR_MCH Detector = 10 + Detector_DETECTOR_ZDC Detector = 15 + Detector_DETECTOR_EMC Detector = 18 + Detector_DETECTOR_TST Detector = 19 + Detector_DETECTOR_ITS Detector = 32 + Detector_DETECTOR_FDD Detector = 33 + Detector_DETECTOR_FT0 Detector = 34 + Detector_DETECTOR_FV0 Detector = 35 + Detector_DETECTOR_MFT Detector = 36 + Detector_DETECTOR_MID Detector = 37 +) + +// Enum value maps for Detector. +var ( + Detector_name = map[int32]string{ + 0: "DETECTOR_NULL", + 3: "DETECTOR_TPC", + 4: "DETECTOR_TRD", + 5: "DETECTOR_TOF", + 6: "DETECTOR_HMP", + 7: "DETECTOR_PHS", + 8: "DETECTOR_CPV", + 10: "DETECTOR_MCH", + 15: "DETECTOR_ZDC", + 18: "DETECTOR_EMC", + 19: "DETECTOR_TST", + 32: "DETECTOR_ITS", + 33: "DETECTOR_FDD", + 34: "DETECTOR_FT0", + 35: "DETECTOR_FV0", + 36: "DETECTOR_MFT", + 37: "DETECTOR_MID", + } + Detector_value = map[string]int32{ + "DETECTOR_NULL": 0, + "DETECTOR_TPC": 3, + "DETECTOR_TRD": 4, + "DETECTOR_TOF": 5, + "DETECTOR_HMP": 6, + "DETECTOR_PHS": 7, + "DETECTOR_CPV": 8, + "DETECTOR_MCH": 10, + "DETECTOR_ZDC": 15, + "DETECTOR_EMC": 18, + "DETECTOR_TST": 19, + "DETECTOR_ITS": 32, + "DETECTOR_FDD": 33, + "DETECTOR_FT0": 34, + "DETECTOR_FV0": 35, + "DETECTOR_MFT": 36, + "DETECTOR_MID": 37, + } +) + +func (x Detector) Enum() *Detector { + p := new(Detector) + *p = x + return p +} + +func (x Detector) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (Detector) Descriptor() protoreflect.EnumDescriptor { + return file_protos_bkcommon_proto_enumTypes[0].Descriptor() +} + +func (Detector) Type() protoreflect.EnumType { + return &file_protos_bkcommon_proto_enumTypes[0] +} + +func (x Detector) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use Detector.Descriptor instead. +func (Detector) EnumDescriptor() ([]byte, []int) { + return file_protos_bkcommon_proto_rawDescGZIP(), []int{0} +} + +type RunQuality int32 + +const ( + RunQuality_RUN_QUALITY_NULL RunQuality = 0 + RunQuality_RUN_QUALITY_BAD RunQuality = 1 + RunQuality_RUN_QUALITY_GOOD RunQuality = 2 + RunQuality_RUN_QUALITY_TEST RunQuality = 3 + RunQuality_RUN_QUALITY_NONE RunQuality = 4 +) + +// Enum value maps for RunQuality. +var ( + RunQuality_name = map[int32]string{ + 0: "RUN_QUALITY_NULL", + 1: "RUN_QUALITY_BAD", + 2: "RUN_QUALITY_GOOD", + 3: "RUN_QUALITY_TEST", + 4: "RUN_QUALITY_NONE", + } + RunQuality_value = map[string]int32{ + "RUN_QUALITY_NULL": 0, + "RUN_QUALITY_BAD": 1, + "RUN_QUALITY_GOOD": 2, + "RUN_QUALITY_TEST": 3, + "RUN_QUALITY_NONE": 4, + } +) + +func (x RunQuality) Enum() *RunQuality { + p := new(RunQuality) + *p = x + return p +} + +func (x RunQuality) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (RunQuality) Descriptor() protoreflect.EnumDescriptor { + return file_protos_bkcommon_proto_enumTypes[1].Descriptor() +} + +func (RunQuality) Type() protoreflect.EnumType { + return &file_protos_bkcommon_proto_enumTypes[1] +} + +func (x RunQuality) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use RunQuality.Descriptor instead. +func (RunQuality) EnumDescriptor() ([]byte, []int) { + return file_protos_bkcommon_proto_rawDescGZIP(), []int{1} +} + +type RunType int32 + +const ( + RunType_RUN_TYPE_NULL RunType = 0 + RunType_RUN_TYPE_PHYSICS RunType = 1 + RunType_RUN_TYPE_TECHNICAL RunType = 2 + RunType_RUN_TYPE_PEDESTAL RunType = 3 + RunType_RUN_TYPE_PULSER RunType = 4 + RunType_RUN_TYPE_LASER RunType = 5 + RunType_RUN_TYPE_CALIBRATION_ITHR_TUNING RunType = 6 + RunType_RUN_TYPE_CALIBRATION_VCASN_TUNING RunType = 7 + RunType_RUN_TYPE_CALIBRATION_THR_SCAN RunType = 8 + RunType_RUN_TYPE_CALIBRATION_DIGITAL_SCAN RunType = 9 + RunType_RUN_TYPE_CALIBRATION_ANALOG_SCAN RunType = 10 + RunType_RUN_TYPE_CALIBRATION_FHR RunType = 11 + RunType_RUN_TYPE_CALIBRATION_ALPIDE_SCAN RunType = 12 + RunType_RUN_TYPE_CALIBRATION RunType = 13 + RunType_RUN_TYPE_COSMICS RunType = 14 + RunType_RUN_TYPE_SYNTHETIC RunType = 15 + RunType_RUN_TYPE_NOISE RunType = 16 + RunType_RUN_TYPE_CALIBRATION_PULSE_LENGTH RunType = 17 + RunType_RUN_TYPE_CALIBRATION_VRESETD RunType = 18 +) + +// Enum value maps for RunType. +var ( + RunType_name = map[int32]string{ + 0: "RUN_TYPE_NULL", + 1: "RUN_TYPE_PHYSICS", + 2: "RUN_TYPE_TECHNICAL", + 3: "RUN_TYPE_PEDESTAL", + 4: "RUN_TYPE_PULSER", + 5: "RUN_TYPE_LASER", + 6: "RUN_TYPE_CALIBRATION_ITHR_TUNING", + 7: "RUN_TYPE_CALIBRATION_VCASN_TUNING", + 8: "RUN_TYPE_CALIBRATION_THR_SCAN", + 9: "RUN_TYPE_CALIBRATION_DIGITAL_SCAN", + 10: "RUN_TYPE_CALIBRATION_ANALOG_SCAN", + 11: "RUN_TYPE_CALIBRATION_FHR", + 12: "RUN_TYPE_CALIBRATION_ALPIDE_SCAN", + 13: "RUN_TYPE_CALIBRATION", + 14: "RUN_TYPE_COSMICS", + 15: "RUN_TYPE_SYNTHETIC", + 16: "RUN_TYPE_NOISE", + 17: "RUN_TYPE_CALIBRATION_PULSE_LENGTH", + 18: "RUN_TYPE_CALIBRATION_VRESETD", + } + RunType_value = map[string]int32{ + "RUN_TYPE_NULL": 0, + "RUN_TYPE_PHYSICS": 1, + "RUN_TYPE_TECHNICAL": 2, + "RUN_TYPE_PEDESTAL": 3, + "RUN_TYPE_PULSER": 4, + "RUN_TYPE_LASER": 5, + "RUN_TYPE_CALIBRATION_ITHR_TUNING": 6, + "RUN_TYPE_CALIBRATION_VCASN_TUNING": 7, + "RUN_TYPE_CALIBRATION_THR_SCAN": 8, + "RUN_TYPE_CALIBRATION_DIGITAL_SCAN": 9, + "RUN_TYPE_CALIBRATION_ANALOG_SCAN": 10, + "RUN_TYPE_CALIBRATION_FHR": 11, + "RUN_TYPE_CALIBRATION_ALPIDE_SCAN": 12, + "RUN_TYPE_CALIBRATION": 13, + "RUN_TYPE_COSMICS": 14, + "RUN_TYPE_SYNTHETIC": 15, + "RUN_TYPE_NOISE": 16, + "RUN_TYPE_CALIBRATION_PULSE_LENGTH": 17, + "RUN_TYPE_CALIBRATION_VRESETD": 18, + } +) + +func (x RunType) Enum() *RunType { + p := new(RunType) + *p = x + return p +} + +func (x RunType) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (RunType) Descriptor() protoreflect.EnumDescriptor { + return file_protos_bkcommon_proto_enumTypes[2].Descriptor() +} + +func (RunType) Type() protoreflect.EnumType { + return &file_protos_bkcommon_proto_enumTypes[2] +} + +func (x RunType) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use RunType.Descriptor instead. +func (RunType) EnumDescriptor() ([]byte, []int) { + return file_protos_bkcommon_proto_rawDescGZIP(), []int{2} +} + +type Run struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + RunNumber int32 `protobuf:"varint,1,opt,name=runNumber,proto3" json:"runNumber,omitempty"` + EnvironmentId *string `protobuf:"bytes,2,opt,name=environmentId,proto3,oneof" json:"environmentId,omitempty"` + BytesReadOut *int32 `protobuf:"varint,3,opt,name=bytesReadOut,proto3,oneof" json:"bytesReadOut,omitempty"` + // Unix timestamp when this entity was created + CreatedAt int64 `protobuf:"varint,4,opt,name=createdAt,proto3" json:"createdAt,omitempty"` + Id int32 `protobuf:"varint,5,opt,name=Id,proto3" json:"Id,omitempty"` + NDetectors *int32 `protobuf:"varint,6,opt,name=nDetectors,proto3,oneof" json:"nDetectors,omitempty"` + NEpns *int32 `protobuf:"varint,7,opt,name=nEpns,proto3,oneof" json:"nEpns,omitempty"` + NFlps *int32 `protobuf:"varint,8,opt,name=nFlps,proto3,oneof" json:"nFlps,omitempty"` + NSubtimeframes *int32 `protobuf:"varint,9,opt,name=nSubtimeframes,proto3,oneof" json:"nSubtimeframes,omitempty"` + // Repository hash + PdpConfigOption *string `protobuf:"bytes,10,opt,name=pdpConfigOption,proto3,oneof" json:"pdpConfigOption,omitempty"` + // Library file location of the pdp topology description + PdpTopologyDescriptionLibraryFile *string `protobuf:"bytes,11,opt,name=pdpTopologyDescriptionLibraryFile,proto3,oneof" json:"pdpTopologyDescriptionLibraryFile,omitempty"` + // Parameters given to the pdp workflow + PdpWorkflowParameters *string `protobuf:"bytes,12,opt,name=pdpWorkflowParameters,proto3,oneof" json:"pdpWorkflowParameters,omitempty"` + // Beam type of the pdp + PdpBeamType *string `protobuf:"bytes,13,opt,name=pdpBeamType,proto3,oneof" json:"pdpBeamType,omitempty"` + // Config uri of readout. + ReadoutCfgUri *string `protobuf:"bytes,14,opt,name=readoutCfgUri,proto3,oneof" json:"readoutCfgUri,omitempty"` + RunQuality RunQuality `protobuf:"varint,15,opt,name=runQuality,proto3,enum=o2.bookkeeping.RunQuality" json:"runQuality,omitempty"` + RunType *RunType `protobuf:"varint,16,opt,name=runType,proto3,enum=o2.bookkeeping.RunType,oneof" json:"runType,omitempty"` + TfbDdMode *string `protobuf:"bytes,17,opt,name=tfbDdMode,proto3,oneof" json:"tfbDdMode,omitempty"` + TimeO2End *int64 `protobuf:"varint,18,opt,name=timeO2End,proto3,oneof" json:"timeO2End,omitempty"` + TimeO2Start *int64 `protobuf:"varint,19,opt,name=timeO2Start,proto3,oneof" json:"timeO2Start,omitempty"` + TimeTrgEnd *int64 `protobuf:"varint,20,opt,name=timeTrgEnd,proto3,oneof" json:"timeTrgEnd,omitempty"` + TimeTrgStart *int64 `protobuf:"varint,21,opt,name=timeTrgStart,proto3,oneof" json:"timeTrgStart,omitempty"` + // Trigger value + TriggerValue *string `protobuf:"bytes,22,opt,name=triggerValue,proto3,oneof" json:"triggerValue,omitempty"` + RawCtpTriggerConfiguration *string `protobuf:"bytes,32,opt,name=rawCtpTriggerConfiguration,proto3,oneof" json:"rawCtpTriggerConfiguration,omitempty"` + // The full name or file location of the odcTopology + OdcTopologyFullName *string `protobuf:"bytes,23,opt,name=odcTopologyFullName,proto3,oneof" json:"odcTopologyFullName,omitempty"` + DdFlp *bool `protobuf:"varint,24,opt,name=ddFlp,proto3,oneof" json:"ddFlp,omitempty"` + Dcs *bool `protobuf:"varint,25,opt,name=dcs,proto3,oneof" json:"dcs,omitempty"` + Epn *bool `protobuf:"varint,26,opt,name=epn,proto3,oneof" json:"epn,omitempty"` + EpnTopology *string `protobuf:"bytes,27,opt,name=epnTopology,proto3,oneof" json:"epnTopology,omitempty"` + Detectors []Detector `protobuf:"varint,28,rep,packed,name=detectors,proto3,enum=o2.bookkeeping.Detector" json:"detectors,omitempty"` + // Unix timestamp when this entity was last updated + UpdatedAt int64 `protobuf:"varint,30,opt,name=updatedAt,proto3" json:"updatedAt,omitempty"` + // A string that marks the period of the lhc + LhcPeriod *string `protobuf:"bytes,31,opt,name=lhcPeriod,proto3,oneof" json:"lhcPeriod,omitempty"` +} + +func (x *Run) Reset() { + *x = Run{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_bkcommon_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Run) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Run) ProtoMessage() {} + +func (x *Run) ProtoReflect() protoreflect.Message { + mi := &file_protos_bkcommon_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Run.ProtoReflect.Descriptor instead. +func (*Run) Descriptor() ([]byte, []int) { + return file_protos_bkcommon_proto_rawDescGZIP(), []int{0} +} + +func (x *Run) GetRunNumber() int32 { + if x != nil { + return x.RunNumber + } + return 0 +} + +func (x *Run) GetEnvironmentId() string { + if x != nil && x.EnvironmentId != nil { + return *x.EnvironmentId + } + return "" +} + +func (x *Run) GetBytesReadOut() int32 { + if x != nil && x.BytesReadOut != nil { + return *x.BytesReadOut + } + return 0 +} + +func (x *Run) GetCreatedAt() int64 { + if x != nil { + return x.CreatedAt + } + return 0 +} + +func (x *Run) GetId() int32 { + if x != nil { + return x.Id + } + return 0 +} + +func (x *Run) GetNDetectors() int32 { + if x != nil && x.NDetectors != nil { + return *x.NDetectors + } + return 0 +} + +func (x *Run) GetNEpns() int32 { + if x != nil && x.NEpns != nil { + return *x.NEpns + } + return 0 +} + +func (x *Run) GetNFlps() int32 { + if x != nil && x.NFlps != nil { + return *x.NFlps + } + return 0 +} + +func (x *Run) GetNSubtimeframes() int32 { + if x != nil && x.NSubtimeframes != nil { + return *x.NSubtimeframes + } + return 0 +} + +func (x *Run) GetPdpConfigOption() string { + if x != nil && x.PdpConfigOption != nil { + return *x.PdpConfigOption + } + return "" +} + +func (x *Run) GetPdpTopologyDescriptionLibraryFile() string { + if x != nil && x.PdpTopologyDescriptionLibraryFile != nil { + return *x.PdpTopologyDescriptionLibraryFile + } + return "" +} + +func (x *Run) GetPdpWorkflowParameters() string { + if x != nil && x.PdpWorkflowParameters != nil { + return *x.PdpWorkflowParameters + } + return "" +} + +func (x *Run) GetPdpBeamType() string { + if x != nil && x.PdpBeamType != nil { + return *x.PdpBeamType + } + return "" +} + +func (x *Run) GetReadoutCfgUri() string { + if x != nil && x.ReadoutCfgUri != nil { + return *x.ReadoutCfgUri + } + return "" +} + +func (x *Run) GetRunQuality() RunQuality { + if x != nil { + return x.RunQuality + } + return RunQuality_RUN_QUALITY_NULL +} + +func (x *Run) GetRunType() RunType { + if x != nil && x.RunType != nil { + return *x.RunType + } + return RunType_RUN_TYPE_NULL +} + +func (x *Run) GetTfbDdMode() string { + if x != nil && x.TfbDdMode != nil { + return *x.TfbDdMode + } + return "" +} + +func (x *Run) GetTimeO2End() int64 { + if x != nil && x.TimeO2End != nil { + return *x.TimeO2End + } + return 0 +} + +func (x *Run) GetTimeO2Start() int64 { + if x != nil && x.TimeO2Start != nil { + return *x.TimeO2Start + } + return 0 +} + +func (x *Run) GetTimeTrgEnd() int64 { + if x != nil && x.TimeTrgEnd != nil { + return *x.TimeTrgEnd + } + return 0 +} + +func (x *Run) GetTimeTrgStart() int64 { + if x != nil && x.TimeTrgStart != nil { + return *x.TimeTrgStart + } + return 0 +} + +func (x *Run) GetTriggerValue() string { + if x != nil && x.TriggerValue != nil { + return *x.TriggerValue + } + return "" +} + +func (x *Run) GetRawCtpTriggerConfiguration() string { + if x != nil && x.RawCtpTriggerConfiguration != nil { + return *x.RawCtpTriggerConfiguration + } + return "" +} + +func (x *Run) GetOdcTopologyFullName() string { + if x != nil && x.OdcTopologyFullName != nil { + return *x.OdcTopologyFullName + } + return "" +} + +func (x *Run) GetDdFlp() bool { + if x != nil && x.DdFlp != nil { + return *x.DdFlp + } + return false +} + +func (x *Run) GetDcs() bool { + if x != nil && x.Dcs != nil { + return *x.Dcs + } + return false +} + +func (x *Run) GetEpn() bool { + if x != nil && x.Epn != nil { + return *x.Epn + } + return false +} + +func (x *Run) GetEpnTopology() string { + if x != nil && x.EpnTopology != nil { + return *x.EpnTopology + } + return "" +} + +func (x *Run) GetDetectors() []Detector { + if x != nil { + return x.Detectors + } + return nil +} + +func (x *Run) GetUpdatedAt() int64 { + if x != nil { + return x.UpdatedAt + } + return 0 +} + +func (x *Run) GetLhcPeriod() string { + if x != nil && x.LhcPeriod != nil { + return *x.LhcPeriod + } + return "" +} + +type LHCFill struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + FillNumber int32 `protobuf:"varint,1,opt,name=fillNumber,proto3" json:"fillNumber,omitempty"` + // Timestamp of the start of the stable beam (if it applies) + StableBeamsStart *int64 `protobuf:"varint,7,opt,name=stableBeamsStart,proto3,oneof" json:"stableBeamsStart,omitempty"` + // Timestamp of the end of the stable beam (if it applies) + StableBeamsEnd *int64 `protobuf:"varint,8,opt,name=stableBeamsEnd,proto3,oneof" json:"stableBeamsEnd,omitempty"` + // Duration of the stable beam (if it applies) + StableBeamsDuration *int64 `protobuf:"varint,9,opt,name=stableBeamsDuration,proto3,oneof" json:"stableBeamsDuration,omitempty"` + BeamType string `protobuf:"bytes,5,opt,name=beamType,proto3" json:"beamType,omitempty"` + FillingSchemeName string `protobuf:"bytes,6,opt,name=fillingSchemeName,proto3" json:"fillingSchemeName,omitempty"` +} + +func (x *LHCFill) Reset() { + *x = LHCFill{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_bkcommon_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *LHCFill) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*LHCFill) ProtoMessage() {} + +func (x *LHCFill) ProtoReflect() protoreflect.Message { + mi := &file_protos_bkcommon_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use LHCFill.ProtoReflect.Descriptor instead. +func (*LHCFill) Descriptor() ([]byte, []int) { + return file_protos_bkcommon_proto_rawDescGZIP(), []int{1} +} + +func (x *LHCFill) GetFillNumber() int32 { + if x != nil { + return x.FillNumber + } + return 0 +} + +func (x *LHCFill) GetStableBeamsStart() int64 { + if x != nil && x.StableBeamsStart != nil { + return *x.StableBeamsStart + } + return 0 +} + +func (x *LHCFill) GetStableBeamsEnd() int64 { + if x != nil && x.StableBeamsEnd != nil { + return *x.StableBeamsEnd + } + return 0 +} + +func (x *LHCFill) GetStableBeamsDuration() int64 { + if x != nil && x.StableBeamsDuration != nil { + return *x.StableBeamsDuration + } + return 0 +} + +func (x *LHCFill) GetBeamType() string { + if x != nil { + return x.BeamType + } + return "" +} + +func (x *LHCFill) GetFillingSchemeName() string { + if x != nil { + return x.FillingSchemeName + } + return "" +} + +type User struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The unique CERN identifier of this user. + ExternalId *int32 `protobuf:"varint,1,opt,name=externalId,proto3,oneof" json:"externalId,omitempty"` + // The unique identifier of this entity. + Id *int32 `protobuf:"varint,2,opt,name=id,proto3,oneof" json:"id,omitempty"` + // Name of the user. + Name string `protobuf:"bytes,3,opt,name=name,proto3" json:"name,omitempty"` +} + +func (x *User) Reset() { + *x = User{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_bkcommon_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *User) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*User) ProtoMessage() {} + +func (x *User) ProtoReflect() protoreflect.Message { + mi := &file_protos_bkcommon_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use User.ProtoReflect.Descriptor instead. +func (*User) Descriptor() ([]byte, []int) { + return file_protos_bkcommon_proto_rawDescGZIP(), []int{2} +} + +func (x *User) GetExternalId() int32 { + if x != nil && x.ExternalId != nil { + return *x.ExternalId + } + return 0 +} + +func (x *User) GetId() int32 { + if x != nil && x.Id != nil { + return *x.Id + } + return 0 +} + +func (x *User) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +// End of run reason +type EorReason struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The unique identifier + Id *int32 `protobuf:"varint,1,opt,name=id,proto3,oneof" json:"id,omitempty"` + // Category of the eor (as described in reason_types table) + Category string `protobuf:"bytes,2,opt,name=category,proto3" json:"category,omitempty"` + // Title of the eor (as described in reason_types table) + Title string `protobuf:"bytes,3,opt,name=title,proto3" json:"title,omitempty"` + Description *string `protobuf:"bytes,4,opt,name=description,proto3,oneof" json:"description,omitempty"` +} + +func (x *EorReason) Reset() { + *x = EorReason{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_bkcommon_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *EorReason) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*EorReason) ProtoMessage() {} + +func (x *EorReason) ProtoReflect() protoreflect.Message { + mi := &file_protos_bkcommon_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use EorReason.ProtoReflect.Descriptor instead. +func (*EorReason) Descriptor() ([]byte, []int) { + return file_protos_bkcommon_proto_rawDescGZIP(), []int{3} +} + +func (x *EorReason) GetId() int32 { + if x != nil && x.Id != nil { + return *x.Id + } + return 0 +} + +func (x *EorReason) GetCategory() string { + if x != nil { + return x.Category + } + return "" +} + +func (x *EorReason) GetTitle() string { + if x != nil { + return x.Title + } + return "" +} + +func (x *EorReason) GetDescription() string { + if x != nil && x.Description != nil { + return *x.Description + } + return "" +} + +// Generic empty message when services return no data +type Empty struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *Empty) Reset() { + *x = Empty{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_bkcommon_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Empty) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Empty) ProtoMessage() {} + +func (x *Empty) ProtoReflect() protoreflect.Message { + mi := &file_protos_bkcommon_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Empty.ProtoReflect.Descriptor instead. +func (*Empty) Descriptor() ([]byte, []int) { + return file_protos_bkcommon_proto_rawDescGZIP(), []int{4} +} + +var File_protos_bkcommon_proto protoreflect.FileDescriptor + +var file_protos_bkcommon_proto_rawDesc = []byte{ + 0x0a, 0x15, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x62, 0x6b, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, + 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0e, 0x6f, 0x32, 0x2e, 0x62, 0x6f, 0x6f, 0x6b, + 0x6b, 0x65, 0x65, 0x70, 0x69, 0x6e, 0x67, 0x22, 0xa0, 0x0d, 0x0a, 0x03, 0x52, 0x75, 0x6e, 0x12, + 0x1c, 0x0a, 0x09, 0x72, 0x75, 0x6e, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x05, 0x52, 0x09, 0x72, 0x75, 0x6e, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x29, 0x0a, + 0x0d, 0x65, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0d, 0x65, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, + 0x65, 0x6e, 0x74, 0x49, 0x64, 0x88, 0x01, 0x01, 0x12, 0x27, 0x0a, 0x0c, 0x62, 0x79, 0x74, 0x65, + 0x73, 0x52, 0x65, 0x61, 0x64, 0x4f, 0x75, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x48, 0x01, + 0x52, 0x0c, 0x62, 0x79, 0x74, 0x65, 0x73, 0x52, 0x65, 0x61, 0x64, 0x4f, 0x75, 0x74, 0x88, 0x01, + 0x01, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, + 0x0e, 0x0a, 0x02, 0x49, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, 0x49, 0x64, 0x12, + 0x23, 0x0a, 0x0a, 0x6e, 0x44, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x18, 0x06, 0x20, + 0x01, 0x28, 0x05, 0x48, 0x02, 0x52, 0x0a, 0x6e, 0x44, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, + 0x73, 0x88, 0x01, 0x01, 0x12, 0x19, 0x0a, 0x05, 0x6e, 0x45, 0x70, 0x6e, 0x73, 0x18, 0x07, 0x20, + 0x01, 0x28, 0x05, 0x48, 0x03, 0x52, 0x05, 0x6e, 0x45, 0x70, 0x6e, 0x73, 0x88, 0x01, 0x01, 0x12, + 0x19, 0x0a, 0x05, 0x6e, 0x46, 0x6c, 0x70, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x05, 0x48, 0x04, + 0x52, 0x05, 0x6e, 0x46, 0x6c, 0x70, 0x73, 0x88, 0x01, 0x01, 0x12, 0x2b, 0x0a, 0x0e, 0x6e, 0x53, + 0x75, 0x62, 0x74, 0x69, 0x6d, 0x65, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x09, 0x20, 0x01, + 0x28, 0x05, 0x48, 0x05, 0x52, 0x0e, 0x6e, 0x53, 0x75, 0x62, 0x74, 0x69, 0x6d, 0x65, 0x66, 0x72, + 0x61, 0x6d, 0x65, 0x73, 0x88, 0x01, 0x01, 0x12, 0x2d, 0x0a, 0x0f, 0x70, 0x64, 0x70, 0x43, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, + 0x48, 0x06, 0x52, 0x0f, 0x70, 0x64, 0x70, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x4f, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x51, 0x0a, 0x21, 0x70, 0x64, 0x70, 0x54, 0x6f, 0x70, + 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, + 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x46, 0x69, 0x6c, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, + 0x09, 0x48, 0x07, 0x52, 0x21, 0x70, 0x64, 0x70, 0x54, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, + 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x62, 0x72, 0x61, + 0x72, 0x79, 0x46, 0x69, 0x6c, 0x65, 0x88, 0x01, 0x01, 0x12, 0x39, 0x0a, 0x15, 0x70, 0x64, 0x70, + 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, + 0x72, 0x73, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x48, 0x08, 0x52, 0x15, 0x70, 0x64, 0x70, 0x57, + 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, + 0x73, 0x88, 0x01, 0x01, 0x12, 0x25, 0x0a, 0x0b, 0x70, 0x64, 0x70, 0x42, 0x65, 0x61, 0x6d, 0x54, + 0x79, 0x70, 0x65, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x48, 0x09, 0x52, 0x0b, 0x70, 0x64, 0x70, + 0x42, 0x65, 0x61, 0x6d, 0x54, 0x79, 0x70, 0x65, 0x88, 0x01, 0x01, 0x12, 0x29, 0x0a, 0x0d, 0x72, + 0x65, 0x61, 0x64, 0x6f, 0x75, 0x74, 0x43, 0x66, 0x67, 0x55, 0x72, 0x69, 0x18, 0x0e, 0x20, 0x01, + 0x28, 0x09, 0x48, 0x0a, 0x52, 0x0d, 0x72, 0x65, 0x61, 0x64, 0x6f, 0x75, 0x74, 0x43, 0x66, 0x67, + 0x55, 0x72, 0x69, 0x88, 0x01, 0x01, 0x12, 0x3a, 0x0a, 0x0a, 0x72, 0x75, 0x6e, 0x51, 0x75, 0x61, + 0x6c, 0x69, 0x74, 0x79, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x6f, 0x32, 0x2e, + 0x62, 0x6f, 0x6f, 0x6b, 0x6b, 0x65, 0x65, 0x70, 0x69, 0x6e, 0x67, 0x2e, 0x52, 0x75, 0x6e, 0x51, + 0x75, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x52, 0x0a, 0x72, 0x75, 0x6e, 0x51, 0x75, 0x61, 0x6c, 0x69, + 0x74, 0x79, 0x12, 0x36, 0x0a, 0x07, 0x72, 0x75, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x18, 0x10, 0x20, + 0x01, 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x6f, 0x32, 0x2e, 0x62, 0x6f, 0x6f, 0x6b, 0x6b, 0x65, 0x65, + 0x70, 0x69, 0x6e, 0x67, 0x2e, 0x52, 0x75, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x48, 0x0b, 0x52, 0x07, + 0x72, 0x75, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x88, 0x01, 0x01, 0x12, 0x21, 0x0a, 0x09, 0x74, 0x66, + 0x62, 0x44, 0x64, 0x4d, 0x6f, 0x64, 0x65, 0x18, 0x11, 0x20, 0x01, 0x28, 0x09, 0x48, 0x0c, 0x52, + 0x09, 0x74, 0x66, 0x62, 0x44, 0x64, 0x4d, 0x6f, 0x64, 0x65, 0x88, 0x01, 0x01, 0x12, 0x21, 0x0a, + 0x09, 0x74, 0x69, 0x6d, 0x65, 0x4f, 0x32, 0x45, 0x6e, 0x64, 0x18, 0x12, 0x20, 0x01, 0x28, 0x03, + 0x48, 0x0d, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x4f, 0x32, 0x45, 0x6e, 0x64, 0x88, 0x01, 0x01, + 0x12, 0x25, 0x0a, 0x0b, 0x74, 0x69, 0x6d, 0x65, 0x4f, 0x32, 0x53, 0x74, 0x61, 0x72, 0x74, 0x18, + 0x13, 0x20, 0x01, 0x28, 0x03, 0x48, 0x0e, 0x52, 0x0b, 0x74, 0x69, 0x6d, 0x65, 0x4f, 0x32, 0x53, + 0x74, 0x61, 0x72, 0x74, 0x88, 0x01, 0x01, 0x12, 0x23, 0x0a, 0x0a, 0x74, 0x69, 0x6d, 0x65, 0x54, + 0x72, 0x67, 0x45, 0x6e, 0x64, 0x18, 0x14, 0x20, 0x01, 0x28, 0x03, 0x48, 0x0f, 0x52, 0x0a, 0x74, + 0x69, 0x6d, 0x65, 0x54, 0x72, 0x67, 0x45, 0x6e, 0x64, 0x88, 0x01, 0x01, 0x12, 0x27, 0x0a, 0x0c, + 0x74, 0x69, 0x6d, 0x65, 0x54, 0x72, 0x67, 0x53, 0x74, 0x61, 0x72, 0x74, 0x18, 0x15, 0x20, 0x01, + 0x28, 0x03, 0x48, 0x10, 0x52, 0x0c, 0x74, 0x69, 0x6d, 0x65, 0x54, 0x72, 0x67, 0x53, 0x74, 0x61, + 0x72, 0x74, 0x88, 0x01, 0x01, 0x12, 0x27, 0x0a, 0x0c, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, + 0x56, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x16, 0x20, 0x01, 0x28, 0x09, 0x48, 0x11, 0x52, 0x0c, 0x74, + 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x88, 0x01, 0x01, 0x12, 0x43, + 0x0a, 0x1a, 0x72, 0x61, 0x77, 0x43, 0x74, 0x70, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x20, 0x20, 0x01, + 0x28, 0x09, 0x48, 0x12, 0x52, 0x1a, 0x72, 0x61, 0x77, 0x43, 0x74, 0x70, 0x54, 0x72, 0x69, 0x67, + 0x67, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x88, 0x01, 0x01, 0x12, 0x35, 0x0a, 0x13, 0x6f, 0x64, 0x63, 0x54, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, + 0x67, 0x79, 0x46, 0x75, 0x6c, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x17, 0x20, 0x01, 0x28, 0x09, + 0x48, 0x13, 0x52, 0x13, 0x6f, 0x64, 0x63, 0x54, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x46, + 0x75, 0x6c, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x88, 0x01, 0x01, 0x12, 0x19, 0x0a, 0x05, 0x64, 0x64, + 0x46, 0x6c, 0x70, 0x18, 0x18, 0x20, 0x01, 0x28, 0x08, 0x48, 0x14, 0x52, 0x05, 0x64, 0x64, 0x46, + 0x6c, 0x70, 0x88, 0x01, 0x01, 0x12, 0x15, 0x0a, 0x03, 0x64, 0x63, 0x73, 0x18, 0x19, 0x20, 0x01, + 0x28, 0x08, 0x48, 0x15, 0x52, 0x03, 0x64, 0x63, 0x73, 0x88, 0x01, 0x01, 0x12, 0x15, 0x0a, 0x03, + 0x65, 0x70, 0x6e, 0x18, 0x1a, 0x20, 0x01, 0x28, 0x08, 0x48, 0x16, 0x52, 0x03, 0x65, 0x70, 0x6e, + 0x88, 0x01, 0x01, 0x12, 0x25, 0x0a, 0x0b, 0x65, 0x70, 0x6e, 0x54, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, + 0x67, 0x79, 0x18, 0x1b, 0x20, 0x01, 0x28, 0x09, 0x48, 0x17, 0x52, 0x0b, 0x65, 0x70, 0x6e, 0x54, + 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x88, 0x01, 0x01, 0x12, 0x36, 0x0a, 0x09, 0x64, 0x65, + 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x18, 0x1c, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x18, 0x2e, + 0x6f, 0x32, 0x2e, 0x62, 0x6f, 0x6f, 0x6b, 0x6b, 0x65, 0x65, 0x70, 0x69, 0x6e, 0x67, 0x2e, 0x44, + 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, 0x09, 0x64, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, + 0x72, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x18, + 0x1e, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, + 0x12, 0x21, 0x0a, 0x09, 0x6c, 0x68, 0x63, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x18, 0x1f, 0x20, + 0x01, 0x28, 0x09, 0x48, 0x18, 0x52, 0x09, 0x6c, 0x68, 0x63, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, + 0x88, 0x01, 0x01, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x65, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, + 0x65, 0x6e, 0x74, 0x49, 0x64, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x52, + 0x65, 0x61, 0x64, 0x4f, 0x75, 0x74, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x6e, 0x44, 0x65, 0x74, 0x65, + 0x63, 0x74, 0x6f, 0x72, 0x73, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x6e, 0x45, 0x70, 0x6e, 0x73, 0x42, + 0x08, 0x0a, 0x06, 0x5f, 0x6e, 0x46, 0x6c, 0x70, 0x73, 0x42, 0x11, 0x0a, 0x0f, 0x5f, 0x6e, 0x53, + 0x75, 0x62, 0x74, 0x69, 0x6d, 0x65, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x42, 0x12, 0x0a, 0x10, + 0x5f, 0x70, 0x64, 0x70, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, + 0x42, 0x24, 0x0a, 0x22, 0x5f, 0x70, 0x64, 0x70, 0x54, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, + 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x62, 0x72, 0x61, + 0x72, 0x79, 0x46, 0x69, 0x6c, 0x65, 0x42, 0x18, 0x0a, 0x16, 0x5f, 0x70, 0x64, 0x70, 0x57, 0x6f, + 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, + 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x70, 0x64, 0x70, 0x42, 0x65, 0x61, 0x6d, 0x54, 0x79, 0x70, 0x65, + 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x6f, 0x75, 0x74, 0x43, 0x66, 0x67, 0x55, + 0x72, 0x69, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x72, 0x75, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x42, 0x0c, + 0x0a, 0x0a, 0x5f, 0x74, 0x66, 0x62, 0x44, 0x64, 0x4d, 0x6f, 0x64, 0x65, 0x42, 0x0c, 0x0a, 0x0a, + 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x4f, 0x32, 0x45, 0x6e, 0x64, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x74, + 0x69, 0x6d, 0x65, 0x4f, 0x32, 0x53, 0x74, 0x61, 0x72, 0x74, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x74, + 0x69, 0x6d, 0x65, 0x54, 0x72, 0x67, 0x45, 0x6e, 0x64, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x74, 0x69, + 0x6d, 0x65, 0x54, 0x72, 0x67, 0x53, 0x74, 0x61, 0x72, 0x74, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x74, + 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x1d, 0x0a, 0x1b, 0x5f, + 0x72, 0x61, 0x77, 0x43, 0x74, 0x70, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x43, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x16, 0x0a, 0x14, 0x5f, 0x6f, + 0x64, 0x63, 0x54, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x46, 0x75, 0x6c, 0x6c, 0x4e, 0x61, + 0x6d, 0x65, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x64, 0x64, 0x46, 0x6c, 0x70, 0x42, 0x06, 0x0a, 0x04, + 0x5f, 0x64, 0x63, 0x73, 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x65, 0x70, 0x6e, 0x42, 0x0e, 0x0a, 0x0c, + 0x5f, 0x65, 0x70, 0x6e, 0x54, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x42, 0x0c, 0x0a, 0x0a, + 0x5f, 0x6c, 0x68, 0x63, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x22, 0xc8, 0x02, 0x0a, 0x07, 0x4c, + 0x48, 0x43, 0x46, 0x69, 0x6c, 0x6c, 0x12, 0x1e, 0x0a, 0x0a, 0x66, 0x69, 0x6c, 0x6c, 0x4e, 0x75, + 0x6d, 0x62, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x66, 0x69, 0x6c, 0x6c, + 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x2f, 0x0a, 0x10, 0x73, 0x74, 0x61, 0x62, 0x6c, 0x65, + 0x42, 0x65, 0x61, 0x6d, 0x73, 0x53, 0x74, 0x61, 0x72, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, + 0x48, 0x00, 0x52, 0x10, 0x73, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x42, 0x65, 0x61, 0x6d, 0x73, 0x53, + 0x74, 0x61, 0x72, 0x74, 0x88, 0x01, 0x01, 0x12, 0x2b, 0x0a, 0x0e, 0x73, 0x74, 0x61, 0x62, 0x6c, + 0x65, 0x42, 0x65, 0x61, 0x6d, 0x73, 0x45, 0x6e, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x03, 0x48, + 0x01, 0x52, 0x0e, 0x73, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x42, 0x65, 0x61, 0x6d, 0x73, 0x45, 0x6e, + 0x64, 0x88, 0x01, 0x01, 0x12, 0x35, 0x0a, 0x13, 0x73, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x42, 0x65, + 0x61, 0x6d, 0x73, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, + 0x03, 0x48, 0x02, 0x52, 0x13, 0x73, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x42, 0x65, 0x61, 0x6d, 0x73, + 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x1a, 0x0a, 0x08, 0x62, + 0x65, 0x61, 0x6d, 0x54, 0x79, 0x70, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x62, + 0x65, 0x61, 0x6d, 0x54, 0x79, 0x70, 0x65, 0x12, 0x2c, 0x0a, 0x11, 0x66, 0x69, 0x6c, 0x6c, 0x69, + 0x6e, 0x67, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x11, 0x66, 0x69, 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x53, 0x63, 0x68, 0x65, 0x6d, + 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x42, 0x13, 0x0a, 0x11, 0x5f, 0x73, 0x74, 0x61, 0x62, 0x6c, 0x65, + 0x42, 0x65, 0x61, 0x6d, 0x73, 0x53, 0x74, 0x61, 0x72, 0x74, 0x42, 0x11, 0x0a, 0x0f, 0x5f, 0x73, + 0x74, 0x61, 0x62, 0x6c, 0x65, 0x42, 0x65, 0x61, 0x6d, 0x73, 0x45, 0x6e, 0x64, 0x42, 0x16, 0x0a, + 0x14, 0x5f, 0x73, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x42, 0x65, 0x61, 0x6d, 0x73, 0x44, 0x75, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x6a, 0x0a, 0x04, 0x55, 0x73, 0x65, 0x72, 0x12, 0x23, 0x0a, + 0x0a, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x05, 0x48, 0x00, 0x52, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x49, 0x64, 0x88, + 0x01, 0x01, 0x12, 0x13, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x48, 0x01, + 0x52, 0x02, 0x69, 0x64, 0x88, 0x01, 0x01, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, + 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x49, 0x64, 0x42, 0x05, 0x0a, 0x03, 0x5f, 0x69, + 0x64, 0x22, 0x90, 0x01, 0x0a, 0x09, 0x45, 0x6f, 0x72, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, + 0x13, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x48, 0x00, 0x52, 0x02, 0x69, + 0x64, 0x88, 0x01, 0x01, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, + 0x12, 0x14, 0x0a, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x12, 0x25, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x0b, 0x64, + 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x42, 0x05, 0x0a, + 0x03, 0x5f, 0x69, 0x64, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x07, 0x0a, 0x05, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x2a, 0xe7, 0x02, + 0x0a, 0x08, 0x44, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x11, 0x0a, 0x0d, 0x44, 0x45, + 0x54, 0x45, 0x43, 0x54, 0x4f, 0x52, 0x5f, 0x4e, 0x55, 0x4c, 0x4c, 0x10, 0x00, 0x12, 0x10, 0x0a, + 0x0c, 0x44, 0x45, 0x54, 0x45, 0x43, 0x54, 0x4f, 0x52, 0x5f, 0x54, 0x50, 0x43, 0x10, 0x03, 0x12, + 0x10, 0x0a, 0x0c, 0x44, 0x45, 0x54, 0x45, 0x43, 0x54, 0x4f, 0x52, 0x5f, 0x54, 0x52, 0x44, 0x10, + 0x04, 0x12, 0x10, 0x0a, 0x0c, 0x44, 0x45, 0x54, 0x45, 0x43, 0x54, 0x4f, 0x52, 0x5f, 0x54, 0x4f, + 0x46, 0x10, 0x05, 0x12, 0x10, 0x0a, 0x0c, 0x44, 0x45, 0x54, 0x45, 0x43, 0x54, 0x4f, 0x52, 0x5f, + 0x48, 0x4d, 0x50, 0x10, 0x06, 0x12, 0x10, 0x0a, 0x0c, 0x44, 0x45, 0x54, 0x45, 0x43, 0x54, 0x4f, + 0x52, 0x5f, 0x50, 0x48, 0x53, 0x10, 0x07, 0x12, 0x10, 0x0a, 0x0c, 0x44, 0x45, 0x54, 0x45, 0x43, + 0x54, 0x4f, 0x52, 0x5f, 0x43, 0x50, 0x56, 0x10, 0x08, 0x12, 0x10, 0x0a, 0x0c, 0x44, 0x45, 0x54, + 0x45, 0x43, 0x54, 0x4f, 0x52, 0x5f, 0x4d, 0x43, 0x48, 0x10, 0x0a, 0x12, 0x10, 0x0a, 0x0c, 0x44, + 0x45, 0x54, 0x45, 0x43, 0x54, 0x4f, 0x52, 0x5f, 0x5a, 0x44, 0x43, 0x10, 0x0f, 0x12, 0x10, 0x0a, + 0x0c, 0x44, 0x45, 0x54, 0x45, 0x43, 0x54, 0x4f, 0x52, 0x5f, 0x45, 0x4d, 0x43, 0x10, 0x12, 0x12, + 0x10, 0x0a, 0x0c, 0x44, 0x45, 0x54, 0x45, 0x43, 0x54, 0x4f, 0x52, 0x5f, 0x54, 0x53, 0x54, 0x10, + 0x13, 0x12, 0x10, 0x0a, 0x0c, 0x44, 0x45, 0x54, 0x45, 0x43, 0x54, 0x4f, 0x52, 0x5f, 0x49, 0x54, + 0x53, 0x10, 0x20, 0x12, 0x10, 0x0a, 0x0c, 0x44, 0x45, 0x54, 0x45, 0x43, 0x54, 0x4f, 0x52, 0x5f, + 0x46, 0x44, 0x44, 0x10, 0x21, 0x12, 0x10, 0x0a, 0x0c, 0x44, 0x45, 0x54, 0x45, 0x43, 0x54, 0x4f, + 0x52, 0x5f, 0x46, 0x54, 0x30, 0x10, 0x22, 0x12, 0x10, 0x0a, 0x0c, 0x44, 0x45, 0x54, 0x45, 0x43, + 0x54, 0x4f, 0x52, 0x5f, 0x46, 0x56, 0x30, 0x10, 0x23, 0x12, 0x10, 0x0a, 0x0c, 0x44, 0x45, 0x54, + 0x45, 0x43, 0x54, 0x4f, 0x52, 0x5f, 0x4d, 0x46, 0x54, 0x10, 0x24, 0x12, 0x10, 0x0a, 0x0c, 0x44, + 0x45, 0x54, 0x45, 0x43, 0x54, 0x4f, 0x52, 0x5f, 0x4d, 0x49, 0x44, 0x10, 0x25, 0x22, 0x04, 0x08, + 0x01, 0x10, 0x01, 0x22, 0x04, 0x08, 0x02, 0x10, 0x02, 0x22, 0x04, 0x08, 0x09, 0x10, 0x09, 0x22, + 0x04, 0x08, 0x0b, 0x10, 0x0e, 0x22, 0x04, 0x08, 0x10, 0x10, 0x10, 0x22, 0x04, 0x08, 0x11, 0x10, + 0x11, 0x22, 0x04, 0x08, 0x14, 0x10, 0x1f, 0x2a, 0x79, 0x0a, 0x0a, 0x52, 0x75, 0x6e, 0x51, 0x75, + 0x61, 0x6c, 0x69, 0x74, 0x79, 0x12, 0x14, 0x0a, 0x10, 0x52, 0x55, 0x4e, 0x5f, 0x51, 0x55, 0x41, + 0x4c, 0x49, 0x54, 0x59, 0x5f, 0x4e, 0x55, 0x4c, 0x4c, 0x10, 0x00, 0x12, 0x13, 0x0a, 0x0f, 0x52, + 0x55, 0x4e, 0x5f, 0x51, 0x55, 0x41, 0x4c, 0x49, 0x54, 0x59, 0x5f, 0x42, 0x41, 0x44, 0x10, 0x01, + 0x12, 0x14, 0x0a, 0x10, 0x52, 0x55, 0x4e, 0x5f, 0x51, 0x55, 0x41, 0x4c, 0x49, 0x54, 0x59, 0x5f, + 0x47, 0x4f, 0x4f, 0x44, 0x10, 0x02, 0x12, 0x14, 0x0a, 0x10, 0x52, 0x55, 0x4e, 0x5f, 0x51, 0x55, + 0x41, 0x4c, 0x49, 0x54, 0x59, 0x5f, 0x54, 0x45, 0x53, 0x54, 0x10, 0x03, 0x12, 0x14, 0x0a, 0x10, + 0x52, 0x55, 0x4e, 0x5f, 0x51, 0x55, 0x41, 0x4c, 0x49, 0x54, 0x59, 0x5f, 0x4e, 0x4f, 0x4e, 0x45, + 0x10, 0x04, 0x2a, 0xb0, 0x04, 0x0a, 0x07, 0x52, 0x75, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x11, + 0x0a, 0x0d, 0x52, 0x55, 0x4e, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4e, 0x55, 0x4c, 0x4c, 0x10, + 0x00, 0x12, 0x14, 0x0a, 0x10, 0x52, 0x55, 0x4e, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x50, 0x48, + 0x59, 0x53, 0x49, 0x43, 0x53, 0x10, 0x01, 0x12, 0x16, 0x0a, 0x12, 0x52, 0x55, 0x4e, 0x5f, 0x54, + 0x59, 0x50, 0x45, 0x5f, 0x54, 0x45, 0x43, 0x48, 0x4e, 0x49, 0x43, 0x41, 0x4c, 0x10, 0x02, 0x12, + 0x15, 0x0a, 0x11, 0x52, 0x55, 0x4e, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x50, 0x45, 0x44, 0x45, + 0x53, 0x54, 0x41, 0x4c, 0x10, 0x03, 0x12, 0x13, 0x0a, 0x0f, 0x52, 0x55, 0x4e, 0x5f, 0x54, 0x59, + 0x50, 0x45, 0x5f, 0x50, 0x55, 0x4c, 0x53, 0x45, 0x52, 0x10, 0x04, 0x12, 0x12, 0x0a, 0x0e, 0x52, + 0x55, 0x4e, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4c, 0x41, 0x53, 0x45, 0x52, 0x10, 0x05, 0x12, + 0x24, 0x0a, 0x20, 0x52, 0x55, 0x4e, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x43, 0x41, 0x4c, 0x49, + 0x42, 0x52, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x49, 0x54, 0x48, 0x52, 0x5f, 0x54, 0x55, 0x4e, + 0x49, 0x4e, 0x47, 0x10, 0x06, 0x12, 0x25, 0x0a, 0x21, 0x52, 0x55, 0x4e, 0x5f, 0x54, 0x59, 0x50, + 0x45, 0x5f, 0x43, 0x41, 0x4c, 0x49, 0x42, 0x52, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x56, 0x43, + 0x41, 0x53, 0x4e, 0x5f, 0x54, 0x55, 0x4e, 0x49, 0x4e, 0x47, 0x10, 0x07, 0x12, 0x21, 0x0a, 0x1d, + 0x52, 0x55, 0x4e, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x43, 0x41, 0x4c, 0x49, 0x42, 0x52, 0x41, + 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x54, 0x48, 0x52, 0x5f, 0x53, 0x43, 0x41, 0x4e, 0x10, 0x08, 0x12, + 0x25, 0x0a, 0x21, 0x52, 0x55, 0x4e, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x43, 0x41, 0x4c, 0x49, + 0x42, 0x52, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x44, 0x49, 0x47, 0x49, 0x54, 0x41, 0x4c, 0x5f, + 0x53, 0x43, 0x41, 0x4e, 0x10, 0x09, 0x12, 0x24, 0x0a, 0x20, 0x52, 0x55, 0x4e, 0x5f, 0x54, 0x59, + 0x50, 0x45, 0x5f, 0x43, 0x41, 0x4c, 0x49, 0x42, 0x52, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x41, + 0x4e, 0x41, 0x4c, 0x4f, 0x47, 0x5f, 0x53, 0x43, 0x41, 0x4e, 0x10, 0x0a, 0x12, 0x1c, 0x0a, 0x18, + 0x52, 0x55, 0x4e, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x43, 0x41, 0x4c, 0x49, 0x42, 0x52, 0x41, + 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x46, 0x48, 0x52, 0x10, 0x0b, 0x12, 0x24, 0x0a, 0x20, 0x52, 0x55, + 0x4e, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x43, 0x41, 0x4c, 0x49, 0x42, 0x52, 0x41, 0x54, 0x49, + 0x4f, 0x4e, 0x5f, 0x41, 0x4c, 0x50, 0x49, 0x44, 0x45, 0x5f, 0x53, 0x43, 0x41, 0x4e, 0x10, 0x0c, + 0x12, 0x18, 0x0a, 0x14, 0x52, 0x55, 0x4e, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x43, 0x41, 0x4c, + 0x49, 0x42, 0x52, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x0d, 0x12, 0x14, 0x0a, 0x10, 0x52, 0x55, + 0x4e, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x43, 0x4f, 0x53, 0x4d, 0x49, 0x43, 0x53, 0x10, 0x0e, + 0x12, 0x16, 0x0a, 0x12, 0x52, 0x55, 0x4e, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x59, 0x4e, + 0x54, 0x48, 0x45, 0x54, 0x49, 0x43, 0x10, 0x0f, 0x12, 0x12, 0x0a, 0x0e, 0x52, 0x55, 0x4e, 0x5f, + 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4e, 0x4f, 0x49, 0x53, 0x45, 0x10, 0x10, 0x12, 0x25, 0x0a, 0x21, + 0x52, 0x55, 0x4e, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x43, 0x41, 0x4c, 0x49, 0x42, 0x52, 0x41, + 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x50, 0x55, 0x4c, 0x53, 0x45, 0x5f, 0x4c, 0x45, 0x4e, 0x47, 0x54, + 0x48, 0x10, 0x11, 0x12, 0x20, 0x0a, 0x1c, 0x52, 0x55, 0x4e, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, + 0x43, 0x41, 0x4c, 0x49, 0x42, 0x52, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x56, 0x52, 0x45, 0x53, + 0x45, 0x54, 0x44, 0x10, 0x12, 0x42, 0x4a, 0x5a, 0x48, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x41, 0x6c, 0x69, 0x63, 0x65, 0x4f, 0x32, 0x47, 0x72, 0x6f, 0x75, 0x70, + 0x2f, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2f, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x69, 0x6e, + 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x62, 0x6f, 0x6f, 0x6b, 0x6b, 0x65, + 0x65, 0x70, 0x69, 0x6e, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x3b, 0x62, 0x6b, 0x70, + 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_protos_bkcommon_proto_rawDescOnce sync.Once + file_protos_bkcommon_proto_rawDescData = file_protos_bkcommon_proto_rawDesc +) + +func file_protos_bkcommon_proto_rawDescGZIP() []byte { + file_protos_bkcommon_proto_rawDescOnce.Do(func() { + file_protos_bkcommon_proto_rawDescData = protoimpl.X.CompressGZIP(file_protos_bkcommon_proto_rawDescData) + }) + return file_protos_bkcommon_proto_rawDescData +} + +var file_protos_bkcommon_proto_enumTypes = make([]protoimpl.EnumInfo, 3) +var file_protos_bkcommon_proto_msgTypes = make([]protoimpl.MessageInfo, 5) +var file_protos_bkcommon_proto_goTypes = []interface{}{ + (Detector)(0), // 0: o2.bookkeeping.Detector + (RunQuality)(0), // 1: o2.bookkeeping.RunQuality + (RunType)(0), // 2: o2.bookkeeping.RunType + (*Run)(nil), // 3: o2.bookkeeping.Run + (*LHCFill)(nil), // 4: o2.bookkeeping.LHCFill + (*User)(nil), // 5: o2.bookkeeping.User + (*EorReason)(nil), // 6: o2.bookkeeping.EorReason + (*Empty)(nil), // 7: o2.bookkeeping.Empty +} +var file_protos_bkcommon_proto_depIdxs = []int32{ + 1, // 0: o2.bookkeeping.Run.runQuality:type_name -> o2.bookkeeping.RunQuality + 2, // 1: o2.bookkeeping.Run.runType:type_name -> o2.bookkeeping.RunType + 0, // 2: o2.bookkeeping.Run.detectors:type_name -> o2.bookkeeping.Detector + 3, // [3:3] is the sub-list for method output_type + 3, // [3:3] is the sub-list for method input_type + 3, // [3:3] is the sub-list for extension type_name + 3, // [3:3] is the sub-list for extension extendee + 0, // [0:3] is the sub-list for field type_name +} + +func init() { file_protos_bkcommon_proto_init() } +func file_protos_bkcommon_proto_init() { + if File_protos_bkcommon_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_protos_bkcommon_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Run); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_bkcommon_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*LHCFill); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_bkcommon_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*User); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_bkcommon_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*EorReason); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_bkcommon_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Empty); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + file_protos_bkcommon_proto_msgTypes[0].OneofWrappers = []interface{}{} + file_protos_bkcommon_proto_msgTypes[1].OneofWrappers = []interface{}{} + file_protos_bkcommon_proto_msgTypes[2].OneofWrappers = []interface{}{} + file_protos_bkcommon_proto_msgTypes[3].OneofWrappers = []interface{}{} + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_protos_bkcommon_proto_rawDesc, + NumEnums: 3, + NumMessages: 5, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_protos_bkcommon_proto_goTypes, + DependencyIndexes: file_protos_bkcommon_proto_depIdxs, + EnumInfos: file_protos_bkcommon_proto_enumTypes, + MessageInfos: file_protos_bkcommon_proto_msgTypes, + }.Build() + File_protos_bkcommon_proto = out.File + file_protos_bkcommon_proto_rawDesc = nil + file_protos_bkcommon_proto_goTypes = nil + file_protos_bkcommon_proto_depIdxs = nil +} diff --git a/core/integration/bookkeeping/protos/bkcommon.proto b/core/integration/bookkeeping/protos/bkcommon.proto new file mode 100644 index 000000000..e23775248 --- /dev/null +++ b/core/integration/bookkeeping/protos/bkcommon.proto @@ -0,0 +1,144 @@ +syntax = 'proto3'; + +package o2.bookkeeping; +option go_package = "github.com/AliceO2Group/Control/core/integration/bookkeeping/protos;bkpb"; + +enum Detector { + DETECTOR_NULL = 0; + + reserved 1, 2; + DETECTOR_TPC = 3; + DETECTOR_TRD = 4; + DETECTOR_TOF = 5; + DETECTOR_HMP = 6; + DETECTOR_PHS = 7; + DETECTOR_CPV = 8; + reserved 9; + + DETECTOR_MCH = 10; + reserved 11 to 14; + + DETECTOR_ZDC = 15; + reserved 16, 17; + + DETECTOR_EMC = 18; + DETECTOR_TST = 19; + reserved 20 to 31; + + DETECTOR_ITS = 32; + DETECTOR_FDD = 33; + DETECTOR_FT0 = 34; + DETECTOR_FV0 = 35; + DETECTOR_MFT = 36; + DETECTOR_MID = 37; +} + +enum RunQuality { + RUN_QUALITY_NULL = 0; + RUN_QUALITY_BAD = 1; + RUN_QUALITY_GOOD = 2; + RUN_QUALITY_TEST = 3; + RUN_QUALITY_NONE = 4; +} + +enum RunType { + RUN_TYPE_NULL = 0; + RUN_TYPE_PHYSICS = 1; + RUN_TYPE_TECHNICAL = 2; + RUN_TYPE_PEDESTAL = 3; + RUN_TYPE_PULSER = 4; + RUN_TYPE_LASER = 5; + RUN_TYPE_CALIBRATION_ITHR_TUNING = 6; + RUN_TYPE_CALIBRATION_VCASN_TUNING = 7; + RUN_TYPE_CALIBRATION_THR_SCAN = 8; + RUN_TYPE_CALIBRATION_DIGITAL_SCAN = 9; + RUN_TYPE_CALIBRATION_ANALOG_SCAN = 10; + RUN_TYPE_CALIBRATION_FHR = 11; + RUN_TYPE_CALIBRATION_ALPIDE_SCAN = 12; + RUN_TYPE_CALIBRATION = 13; + RUN_TYPE_COSMICS = 14; + RUN_TYPE_SYNTHETIC = 15; + RUN_TYPE_NOISE = 16; + RUN_TYPE_CALIBRATION_PULSE_LENGTH = 17; + RUN_TYPE_CALIBRATION_VRESETD = 18; +} + +message Run { + int32 runNumber = 1; + optional string environmentId = 2; + optional int32 bytesReadOut = 3; + // Unix timestamp when this entity was created + int64 createdAt = 4; + int32 Id = 5; + optional int32 nDetectors = 6; + optional int32 nEpns = 7; + optional int32 nFlps = 8; + optional int32 nSubtimeframes = 9; + // Repository hash + optional string pdpConfigOption = 10; + // Library file location of the pdp topology description + optional string pdpTopologyDescriptionLibraryFile = 11; + // Parameters given to the pdp workflow + optional string pdpWorkflowParameters = 12; + // Beam type of the pdp + optional string pdpBeamType = 13; + // Config uri of readout. + optional string readoutCfgUri = 14; + RunQuality runQuality = 15; + optional RunType runType = 16; + optional string tfbDdMode = 17; + optional int64 timeO2End = 18; + optional int64 timeO2Start = 19; + optional int64 timeTrgEnd = 20; + optional int64 timeTrgStart = 21; + // Trigger value + optional string triggerValue = 22; + optional string rawCtpTriggerConfiguration = 32; + // The full name or file location of the odcTopology + optional string odcTopologyFullName = 23; + optional bool ddFlp = 24; + optional bool dcs = 25; + optional bool epn = 26; + optional string epnTopology = 27; + repeated Detector detectors = 28; + // Unix timestamp when this entity was last updated + int64 updatedAt = 30; + // A string that marks the period of the lhc + optional string lhcPeriod = 31; +} + +message LHCFill { + int32 fillNumber = 1; + // Timestamp of the start of the stable beam (if it applies) + optional int64 stableBeamsStart = 7; + // Timestamp of the end of the stable beam (if it applies) + optional int64 stableBeamsEnd = 8; + // Duration of the stable beam (if it applies) + optional int64 stableBeamsDuration = 9; + string beamType = 5; + string fillingSchemeName = 6; +} + +message User { + // The unique CERN identifier of this user. + optional int32 externalId = 1; + // The unique identifier of this entity. + optional int32 id = 2; + // Name of the user. + string name = 3; +} + +// End of run reason +message EorReason { + // The unique identifier + optional int32 id = 1; + // Category of the eor (as described in reason_types table) + string category = 2; + // Title of the eor (as described in reason_types table) + string title = 3; + optional string description = 4; +} + +// Generic empty message when services return no data +message Empty { +} diff --git a/core/integration/bookkeeping/protos/environment.pb.go b/core/integration/bookkeeping/protos/environment.pb.go new file mode 100644 index 000000000..5f7a6ae12 --- /dev/null +++ b/core/integration/bookkeeping/protos/environment.pb.go @@ -0,0 +1,405 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.34.1 +// protoc v5.29.3 +// source: protos/environment.proto + +package bkpb + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type EnvironmentCreationRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Status *string `protobuf:"bytes,3,opt,name=status,proto3,oneof" json:"status,omitempty"` + StatusMessage *string `protobuf:"bytes,4,opt,name=statusMessage,proto3,oneof" json:"statusMessage,omitempty"` + RawConfiguration string `protobuf:"bytes,5,opt,name=rawConfiguration,proto3" json:"rawConfiguration,omitempty"` +} + +func (x *EnvironmentCreationRequest) Reset() { + *x = EnvironmentCreationRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_environment_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *EnvironmentCreationRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*EnvironmentCreationRequest) ProtoMessage() {} + +func (x *EnvironmentCreationRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_environment_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use EnvironmentCreationRequest.ProtoReflect.Descriptor instead. +func (*EnvironmentCreationRequest) Descriptor() ([]byte, []int) { + return file_protos_environment_proto_rawDescGZIP(), []int{0} +} + +func (x *EnvironmentCreationRequest) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *EnvironmentCreationRequest) GetStatus() string { + if x != nil && x.Status != nil { + return *x.Status + } + return "" +} + +func (x *EnvironmentCreationRequest) GetStatusMessage() string { + if x != nil && x.StatusMessage != nil { + return *x.StatusMessage + } + return "" +} + +func (x *EnvironmentCreationRequest) GetRawConfiguration() string { + if x != nil { + return x.RawConfiguration + } + return "" +} + +type EnvironmentUpdateRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Status *string `protobuf:"bytes,3,opt,name=status,proto3,oneof" json:"status,omitempty"` + StatusMessage *string `protobuf:"bytes,4,opt,name=statusMessage,proto3,oneof" json:"statusMessage,omitempty"` +} + +func (x *EnvironmentUpdateRequest) Reset() { + *x = EnvironmentUpdateRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_environment_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *EnvironmentUpdateRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*EnvironmentUpdateRequest) ProtoMessage() {} + +func (x *EnvironmentUpdateRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_environment_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use EnvironmentUpdateRequest.ProtoReflect.Descriptor instead. +func (*EnvironmentUpdateRequest) Descriptor() ([]byte, []int) { + return file_protos_environment_proto_rawDescGZIP(), []int{1} +} + +func (x *EnvironmentUpdateRequest) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *EnvironmentUpdateRequest) GetStatus() string { + if x != nil && x.Status != nil { + return *x.Status + } + return "" +} + +func (x *EnvironmentUpdateRequest) GetStatusMessage() string { + if x != nil && x.StatusMessage != nil { + return *x.StatusMessage + } + return "" +} + +type Environment struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + // Unix timestamp when this entity was created. + CreatedAt int64 `protobuf:"varint,2,opt,name=createdAt,proto3" json:"createdAt,omitempty"` + // Unix timestamp when this entity was last updated. + UpdatedAt int64 `protobuf:"varint,3,opt,name=updatedAt,proto3" json:"updatedAt,omitempty"` + // The status of the environment. + Status string `protobuf:"bytes,5,opt,name=status,proto3" json:"status,omitempty"` + // A message explaining the status or the current state of the environment. + StatusMessage string `protobuf:"bytes,6,opt,name=statusMessage,proto3" json:"statusMessage,omitempty"` + // Array of minified Run objects. + Runs []*Run `protobuf:"bytes,7,rep,name=runs,proto3" json:"runs,omitempty"` +} + +func (x *Environment) Reset() { + *x = Environment{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_environment_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Environment) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Environment) ProtoMessage() {} + +func (x *Environment) ProtoReflect() protoreflect.Message { + mi := &file_protos_environment_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Environment.ProtoReflect.Descriptor instead. +func (*Environment) Descriptor() ([]byte, []int) { + return file_protos_environment_proto_rawDescGZIP(), []int{2} +} + +func (x *Environment) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *Environment) GetCreatedAt() int64 { + if x != nil { + return x.CreatedAt + } + return 0 +} + +func (x *Environment) GetUpdatedAt() int64 { + if x != nil { + return x.UpdatedAt + } + return 0 +} + +func (x *Environment) GetStatus() string { + if x != nil { + return x.Status + } + return "" +} + +func (x *Environment) GetStatusMessage() string { + if x != nil { + return x.StatusMessage + } + return "" +} + +func (x *Environment) GetRuns() []*Run { + if x != nil { + return x.Runs + } + return nil +} + +var File_protos_environment_proto protoreflect.FileDescriptor + +var file_protos_environment_proto_rawDesc = []byte{ + 0x0a, 0x18, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x65, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, + 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0e, 0x6f, 0x32, 0x2e, 0x62, + 0x6f, 0x6f, 0x6b, 0x6b, 0x65, 0x65, 0x70, 0x69, 0x6e, 0x67, 0x1a, 0x15, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x73, 0x2f, 0x62, 0x6b, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x22, 0xbd, 0x01, 0x0a, 0x1a, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, + 0x74, 0x43, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, + 0x12, 0x1b, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x48, 0x00, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x88, 0x01, 0x01, 0x12, 0x29, 0x0a, + 0x0d, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x0d, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x4d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x88, 0x01, 0x01, 0x12, 0x2a, 0x0a, 0x10, 0x72, 0x61, 0x77, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x10, 0x72, 0x61, 0x77, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x42, + 0x10, 0x0a, 0x0e, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x22, 0x8f, 0x01, 0x0a, 0x18, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, + 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, + 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1b, + 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, + 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x88, 0x01, 0x01, 0x12, 0x29, 0x0a, 0x0d, 0x73, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x09, 0x48, 0x01, 0x52, 0x0d, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x4d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x88, 0x01, 0x01, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x4d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x22, 0xc0, 0x01, 0x0a, 0x0b, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, + 0x65, 0x6e, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x02, 0x69, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, + 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, + 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x24, 0x0a, 0x0d, 0x73, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, + 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x27, 0x0a, + 0x04, 0x72, 0x75, 0x6e, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6f, 0x32, + 0x2e, 0x62, 0x6f, 0x6f, 0x6b, 0x6b, 0x65, 0x65, 0x70, 0x69, 0x6e, 0x67, 0x2e, 0x52, 0x75, 0x6e, + 0x52, 0x04, 0x72, 0x75, 0x6e, 0x73, 0x32, 0xb8, 0x01, 0x0a, 0x12, 0x45, 0x6e, 0x76, 0x69, 0x72, + 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x51, 0x0a, + 0x06, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x12, 0x2a, 0x2e, 0x6f, 0x32, 0x2e, 0x62, 0x6f, 0x6f, + 0x6b, 0x6b, 0x65, 0x65, 0x70, 0x69, 0x6e, 0x67, 0x2e, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, + 0x6d, 0x65, 0x6e, 0x74, 0x43, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x6f, 0x32, 0x2e, 0x62, 0x6f, 0x6f, 0x6b, 0x6b, 0x65, 0x65, + 0x70, 0x69, 0x6e, 0x67, 0x2e, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, + 0x12, 0x4f, 0x0a, 0x06, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x28, 0x2e, 0x6f, 0x32, 0x2e, + 0x62, 0x6f, 0x6f, 0x6b, 0x6b, 0x65, 0x65, 0x70, 0x69, 0x6e, 0x67, 0x2e, 0x45, 0x6e, 0x76, 0x69, + 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x6f, 0x32, 0x2e, 0x62, 0x6f, 0x6f, 0x6b, 0x6b, 0x65, + 0x65, 0x70, 0x69, 0x6e, 0x67, 0x2e, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, + 0x74, 0x42, 0x4a, 0x5a, 0x48, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x41, 0x6c, 0x69, 0x63, 0x65, 0x4f, 0x32, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x2f, 0x43, 0x6f, 0x6e, + 0x74, 0x72, 0x6f, 0x6c, 0x2f, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x62, 0x6f, 0x6f, 0x6b, 0x6b, 0x65, 0x65, 0x70, 0x69, 0x6e, + 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x3b, 0x62, 0x6b, 0x70, 0x62, 0x62, 0x06, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_protos_environment_proto_rawDescOnce sync.Once + file_protos_environment_proto_rawDescData = file_protos_environment_proto_rawDesc +) + +func file_protos_environment_proto_rawDescGZIP() []byte { + file_protos_environment_proto_rawDescOnce.Do(func() { + file_protos_environment_proto_rawDescData = protoimpl.X.CompressGZIP(file_protos_environment_proto_rawDescData) + }) + return file_protos_environment_proto_rawDescData +} + +var file_protos_environment_proto_msgTypes = make([]protoimpl.MessageInfo, 3) +var file_protos_environment_proto_goTypes = []interface{}{ + (*EnvironmentCreationRequest)(nil), // 0: o2.bookkeeping.EnvironmentCreationRequest + (*EnvironmentUpdateRequest)(nil), // 1: o2.bookkeeping.EnvironmentUpdateRequest + (*Environment)(nil), // 2: o2.bookkeeping.Environment + (*Run)(nil), // 3: o2.bookkeeping.Run +} +var file_protos_environment_proto_depIdxs = []int32{ + 3, // 0: o2.bookkeeping.Environment.runs:type_name -> o2.bookkeeping.Run + 0, // 1: o2.bookkeeping.EnvironmentService.Create:input_type -> o2.bookkeeping.EnvironmentCreationRequest + 1, // 2: o2.bookkeeping.EnvironmentService.Update:input_type -> o2.bookkeeping.EnvironmentUpdateRequest + 2, // 3: o2.bookkeeping.EnvironmentService.Create:output_type -> o2.bookkeeping.Environment + 2, // 4: o2.bookkeeping.EnvironmentService.Update:output_type -> o2.bookkeeping.Environment + 3, // [3:5] is the sub-list for method output_type + 1, // [1:3] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name +} + +func init() { file_protos_environment_proto_init() } +func file_protos_environment_proto_init() { + if File_protos_environment_proto != nil { + return + } + file_protos_bkcommon_proto_init() + if !protoimpl.UnsafeEnabled { + file_protos_environment_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*EnvironmentCreationRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_environment_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*EnvironmentUpdateRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_environment_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Environment); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + file_protos_environment_proto_msgTypes[0].OneofWrappers = []interface{}{} + file_protos_environment_proto_msgTypes[1].OneofWrappers = []interface{}{} + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_protos_environment_proto_rawDesc, + NumEnums: 0, + NumMessages: 3, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_protos_environment_proto_goTypes, + DependencyIndexes: file_protos_environment_proto_depIdxs, + MessageInfos: file_protos_environment_proto_msgTypes, + }.Build() + File_protos_environment_proto = out.File + file_protos_environment_proto_rawDesc = nil + file_protos_environment_proto_goTypes = nil + file_protos_environment_proto_depIdxs = nil +} diff --git a/core/integration/bookkeeping/protos/environment.proto b/core/integration/bookkeeping/protos/environment.proto new file mode 100644 index 000000000..137850931 --- /dev/null +++ b/core/integration/bookkeeping/protos/environment.proto @@ -0,0 +1,44 @@ +syntax = 'proto3'; + +import "protos/bkcommon.proto"; + +package o2.bookkeeping; +option go_package = "github.com/AliceO2Group/Control/core/integration/bookkeeping/protos;bkpb"; + +// Services + +service EnvironmentService { + rpc Create(EnvironmentCreationRequest) returns (Environment); + rpc Update(EnvironmentUpdateRequest) returns (Environment); +} + +// High level messages + +message EnvironmentCreationRequest { + string id = 1; + optional string status = 3; + optional string statusMessage = 4; + string rawConfiguration = 5; +} + +message EnvironmentUpdateRequest { + string id = 1; + optional string status = 3; + optional string statusMessage = 4; +} + +// Low-level messages and enums + +message Environment { + string id = 1; + // Unix timestamp when this entity was created. + int64 createdAt = 2; + // Unix timestamp when this entity was last updated. + int64 updatedAt = 3; + // The status of the environment. + string status = 5; + // A message explaining the status or the current state of the environment. + string statusMessage = 6; + // Array of minified Run objects. + repeated Run runs = 7; +} diff --git a/core/integration/bookkeeping/protos/environment_grpc.pb.go b/core/integration/bookkeeping/protos/environment_grpc.pb.go new file mode 100644 index 000000000..4d3343267 --- /dev/null +++ b/core/integration/bookkeeping/protos/environment_grpc.pb.go @@ -0,0 +1,144 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.3.0 +// - protoc v5.29.3 +// source: protos/environment.proto + +package bkpb + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +const ( + EnvironmentService_Create_FullMethodName = "/o2.bookkeeping.EnvironmentService/Create" + EnvironmentService_Update_FullMethodName = "/o2.bookkeeping.EnvironmentService/Update" +) + +// EnvironmentServiceClient is the client API for EnvironmentService service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type EnvironmentServiceClient interface { + Create(ctx context.Context, in *EnvironmentCreationRequest, opts ...grpc.CallOption) (*Environment, error) + Update(ctx context.Context, in *EnvironmentUpdateRequest, opts ...grpc.CallOption) (*Environment, error) +} + +type environmentServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewEnvironmentServiceClient(cc grpc.ClientConnInterface) EnvironmentServiceClient { + return &environmentServiceClient{cc} +} + +func (c *environmentServiceClient) Create(ctx context.Context, in *EnvironmentCreationRequest, opts ...grpc.CallOption) (*Environment, error) { + out := new(Environment) + err := c.cc.Invoke(ctx, EnvironmentService_Create_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *environmentServiceClient) Update(ctx context.Context, in *EnvironmentUpdateRequest, opts ...grpc.CallOption) (*Environment, error) { + out := new(Environment) + err := c.cc.Invoke(ctx, EnvironmentService_Update_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// EnvironmentServiceServer is the server API for EnvironmentService service. +// All implementations should embed UnimplementedEnvironmentServiceServer +// for forward compatibility +type EnvironmentServiceServer interface { + Create(context.Context, *EnvironmentCreationRequest) (*Environment, error) + Update(context.Context, *EnvironmentUpdateRequest) (*Environment, error) +} + +// UnimplementedEnvironmentServiceServer should be embedded to have forward compatible implementations. +type UnimplementedEnvironmentServiceServer struct { +} + +func (UnimplementedEnvironmentServiceServer) Create(context.Context, *EnvironmentCreationRequest) (*Environment, error) { + return nil, status.Errorf(codes.Unimplemented, "method Create not implemented") +} +func (UnimplementedEnvironmentServiceServer) Update(context.Context, *EnvironmentUpdateRequest) (*Environment, error) { + return nil, status.Errorf(codes.Unimplemented, "method Update not implemented") +} + +// UnsafeEnvironmentServiceServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to EnvironmentServiceServer will +// result in compilation errors. +type UnsafeEnvironmentServiceServer interface { + mustEmbedUnimplementedEnvironmentServiceServer() +} + +func RegisterEnvironmentServiceServer(s grpc.ServiceRegistrar, srv EnvironmentServiceServer) { + s.RegisterService(&EnvironmentService_ServiceDesc, srv) +} + +func _EnvironmentService_Create_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(EnvironmentCreationRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(EnvironmentServiceServer).Create(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: EnvironmentService_Create_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(EnvironmentServiceServer).Create(ctx, req.(*EnvironmentCreationRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _EnvironmentService_Update_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(EnvironmentUpdateRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(EnvironmentServiceServer).Update(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: EnvironmentService_Update_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(EnvironmentServiceServer).Update(ctx, req.(*EnvironmentUpdateRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// EnvironmentService_ServiceDesc is the grpc.ServiceDesc for EnvironmentService service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var EnvironmentService_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "o2.bookkeeping.EnvironmentService", + HandlerType: (*EnvironmentServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Create", + Handler: _EnvironmentService_Create_Handler, + }, + { + MethodName: "Update", + Handler: _EnvironmentService_Update_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "protos/environment.proto", +} diff --git a/core/integration/bookkeeping/protos/flp.pb.go b/core/integration/bookkeeping/protos/flp.pb.go new file mode 100644 index 000000000..c1a14b2d5 --- /dev/null +++ b/core/integration/bookkeeping/protos/flp.pb.go @@ -0,0 +1,585 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.34.1 +// protoc v5.29.3 +// source: protos/flp.proto + +package bkpb + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type FlpCreationRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Hostname string `protobuf:"bytes,2,opt,name=hostname,proto3" json:"hostname,omitempty"` + RunNumber *int32 `protobuf:"varint,3,opt,name=runNumber,proto3,oneof" json:"runNumber,omitempty"` +} + +func (x *FlpCreationRequest) Reset() { + *x = FlpCreationRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_flp_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FlpCreationRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FlpCreationRequest) ProtoMessage() {} + +func (x *FlpCreationRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_flp_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FlpCreationRequest.ProtoReflect.Descriptor instead. +func (*FlpCreationRequest) Descriptor() ([]byte, []int) { + return file_protos_flp_proto_rawDescGZIP(), []int{0} +} + +func (x *FlpCreationRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *FlpCreationRequest) GetHostname() string { + if x != nil { + return x.Hostname + } + return "" +} + +func (x *FlpCreationRequest) GetRunNumber() int32 { + if x != nil && x.RunNumber != nil { + return *x.RunNumber + } + return 0 +} + +type ManyFlpsCreationRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Flps []*FlpCreationRequest `protobuf:"bytes,1,rep,name=flps,proto3" json:"flps,omitempty"` +} + +func (x *ManyFlpsCreationRequest) Reset() { + *x = ManyFlpsCreationRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_flp_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ManyFlpsCreationRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ManyFlpsCreationRequest) ProtoMessage() {} + +func (x *ManyFlpsCreationRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_flp_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ManyFlpsCreationRequest.ProtoReflect.Descriptor instead. +func (*ManyFlpsCreationRequest) Descriptor() ([]byte, []int) { + return file_protos_flp_proto_rawDescGZIP(), []int{1} +} + +func (x *ManyFlpsCreationRequest) GetFlps() []*FlpCreationRequest { + if x != nil { + return x.Flps + } + return nil +} + +type UpdateCountersRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + FlpName string `protobuf:"bytes,1,opt,name=flpName,proto3" json:"flpName,omitempty"` + RunNumber int32 `protobuf:"varint,2,opt,name=runNumber,proto3" json:"runNumber,omitempty"` + NSubTimeframes uint64 `protobuf:"varint,3,opt,name=nSubTimeframes,proto3" json:"nSubTimeframes,omitempty"` + NEquipmentBytes uint64 `protobuf:"varint,4,opt,name=nEquipmentBytes,proto3" json:"nEquipmentBytes,omitempty"` + NRecordingBytes uint64 `protobuf:"varint,5,opt,name=nRecordingBytes,proto3" json:"nRecordingBytes,omitempty"` + NFairMQBytes uint64 `protobuf:"varint,6,opt,name=nFairMQBytes,proto3" json:"nFairMQBytes,omitempty"` +} + +func (x *UpdateCountersRequest) Reset() { + *x = UpdateCountersRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_flp_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UpdateCountersRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdateCountersRequest) ProtoMessage() {} + +func (x *UpdateCountersRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_flp_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UpdateCountersRequest.ProtoReflect.Descriptor instead. +func (*UpdateCountersRequest) Descriptor() ([]byte, []int) { + return file_protos_flp_proto_rawDescGZIP(), []int{2} +} + +func (x *UpdateCountersRequest) GetFlpName() string { + if x != nil { + return x.FlpName + } + return "" +} + +func (x *UpdateCountersRequest) GetRunNumber() int32 { + if x != nil { + return x.RunNumber + } + return 0 +} + +func (x *UpdateCountersRequest) GetNSubTimeframes() uint64 { + if x != nil { + return x.NSubTimeframes + } + return 0 +} + +func (x *UpdateCountersRequest) GetNEquipmentBytes() uint64 { + if x != nil { + return x.NEquipmentBytes + } + return 0 +} + +func (x *UpdateCountersRequest) GetNRecordingBytes() uint64 { + if x != nil { + return x.NRecordingBytes + } + return 0 +} + +func (x *UpdateCountersRequest) GetNFairMQBytes() uint64 { + if x != nil { + return x.NFairMQBytes + } + return 0 +} + +type FlpList struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Flps []*Flp `protobuf:"bytes,1,rep,name=flps,proto3" json:"flps,omitempty"` +} + +func (x *FlpList) Reset() { + *x = FlpList{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_flp_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FlpList) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FlpList) ProtoMessage() {} + +func (x *FlpList) ProtoReflect() protoreflect.Message { + mi := &file_protos_flp_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FlpList.ProtoReflect.Descriptor instead. +func (*FlpList) Descriptor() ([]byte, []int) { + return file_protos_flp_proto_rawDescGZIP(), []int{3} +} + +func (x *FlpList) GetFlps() []*Flp { + if x != nil { + return x.Flps + } + return nil +} + +type Flp struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id int32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + Hostname string `protobuf:"bytes,3,opt,name=hostname,proto3" json:"hostname,omitempty"` + // Unix timestamp when this entity was created. + CreatedAt int64 `protobuf:"varint,4,opt,name=createdAt,proto3" json:"createdAt,omitempty"` + // Unix timestamp when this entity was last updated. + UpdatedAt int64 `protobuf:"varint,5,opt,name=updatedAt,proto3" json:"updatedAt,omitempty"` + BytesEquipmentReadOut uint64 `protobuf:"varint,6,opt,name=bytesEquipmentReadOut,proto3" json:"bytesEquipmentReadOut,omitempty"` + BytesFairMQReadOut uint64 `protobuf:"varint,7,opt,name=bytesFairMQReadOut,proto3" json:"bytesFairMQReadOut,omitempty"` + BytesProcessed uint64 `protobuf:"varint,8,opt,name=bytesProcessed,proto3" json:"bytesProcessed,omitempty"` + BytesRecordingReadOut uint64 `protobuf:"varint,9,opt,name=bytesRecordingReadOut,proto3" json:"bytesRecordingReadOut,omitempty"` + NTimeframes uint64 `protobuf:"varint,10,opt,name=nTimeframes,proto3" json:"nTimeframes,omitempty"` +} + +func (x *Flp) Reset() { + *x = Flp{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_flp_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Flp) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Flp) ProtoMessage() {} + +func (x *Flp) ProtoReflect() protoreflect.Message { + mi := &file_protos_flp_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Flp.ProtoReflect.Descriptor instead. +func (*Flp) Descriptor() ([]byte, []int) { + return file_protos_flp_proto_rawDescGZIP(), []int{4} +} + +func (x *Flp) GetId() int32 { + if x != nil { + return x.Id + } + return 0 +} + +func (x *Flp) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Flp) GetHostname() string { + if x != nil { + return x.Hostname + } + return "" +} + +func (x *Flp) GetCreatedAt() int64 { + if x != nil { + return x.CreatedAt + } + return 0 +} + +func (x *Flp) GetUpdatedAt() int64 { + if x != nil { + return x.UpdatedAt + } + return 0 +} + +func (x *Flp) GetBytesEquipmentReadOut() uint64 { + if x != nil { + return x.BytesEquipmentReadOut + } + return 0 +} + +func (x *Flp) GetBytesFairMQReadOut() uint64 { + if x != nil { + return x.BytesFairMQReadOut + } + return 0 +} + +func (x *Flp) GetBytesProcessed() uint64 { + if x != nil { + return x.BytesProcessed + } + return 0 +} + +func (x *Flp) GetBytesRecordingReadOut() uint64 { + if x != nil { + return x.BytesRecordingReadOut + } + return 0 +} + +func (x *Flp) GetNTimeframes() uint64 { + if x != nil { + return x.NTimeframes + } + return 0 +} + +var File_protos_flp_proto protoreflect.FileDescriptor + +var file_protos_flp_proto_rawDesc = []byte{ + 0x0a, 0x10, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x66, 0x6c, 0x70, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x12, 0x0e, 0x6f, 0x32, 0x2e, 0x62, 0x6f, 0x6f, 0x6b, 0x6b, 0x65, 0x65, 0x70, 0x69, + 0x6e, 0x67, 0x22, 0x75, 0x0a, 0x12, 0x46, 0x6c, 0x70, 0x43, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, + 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, + 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x09, 0x72, 0x75, 0x6e, 0x4e, + 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x48, 0x00, 0x52, 0x09, 0x72, + 0x75, 0x6e, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x88, 0x01, 0x01, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, + 0x72, 0x75, 0x6e, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x22, 0x51, 0x0a, 0x17, 0x4d, 0x61, 0x6e, + 0x79, 0x46, 0x6c, 0x70, 0x73, 0x43, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x36, 0x0a, 0x04, 0x66, 0x6c, 0x70, 0x73, 0x18, 0x01, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x6f, 0x32, 0x2e, 0x62, 0x6f, 0x6f, 0x6b, 0x6b, 0x65, 0x65, 0x70, + 0x69, 0x6e, 0x67, 0x2e, 0x46, 0x6c, 0x70, 0x43, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x04, 0x66, 0x6c, 0x70, 0x73, 0x22, 0xef, 0x01, 0x0a, + 0x15, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x66, 0x6c, 0x70, 0x4e, 0x61, 0x6d, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x66, 0x6c, 0x70, 0x4e, 0x61, 0x6d, 0x65, + 0x12, 0x1c, 0x0a, 0x09, 0x72, 0x75, 0x6e, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x05, 0x52, 0x09, 0x72, 0x75, 0x6e, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x26, + 0x0a, 0x0e, 0x6e, 0x53, 0x75, 0x62, 0x54, 0x69, 0x6d, 0x65, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x73, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0e, 0x6e, 0x53, 0x75, 0x62, 0x54, 0x69, 0x6d, 0x65, + 0x66, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x28, 0x0a, 0x0f, 0x6e, 0x45, 0x71, 0x75, 0x69, 0x70, + 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x74, 0x65, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, + 0x0f, 0x6e, 0x45, 0x71, 0x75, 0x69, 0x70, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x74, 0x65, 0x73, + 0x12, 0x28, 0x0a, 0x0f, 0x6e, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x42, 0x79, + 0x74, 0x65, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x6e, 0x52, 0x65, 0x63, 0x6f, + 0x72, 0x64, 0x69, 0x6e, 0x67, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x22, 0x0a, 0x0c, 0x6e, 0x46, + 0x61, 0x69, 0x72, 0x4d, 0x51, 0x42, 0x79, 0x74, 0x65, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x0c, 0x6e, 0x46, 0x61, 0x69, 0x72, 0x4d, 0x51, 0x42, 0x79, 0x74, 0x65, 0x73, 0x22, 0x32, + 0x0a, 0x07, 0x46, 0x6c, 0x70, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x27, 0x0a, 0x04, 0x66, 0x6c, 0x70, + 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6f, 0x32, 0x2e, 0x62, 0x6f, 0x6f, + 0x6b, 0x6b, 0x65, 0x65, 0x70, 0x69, 0x6e, 0x67, 0x2e, 0x46, 0x6c, 0x70, 0x52, 0x04, 0x66, 0x6c, + 0x70, 0x73, 0x22, 0xe7, 0x02, 0x0a, 0x03, 0x46, 0x6c, 0x70, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, + 0x0a, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x72, + 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x63, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x75, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x64, 0x41, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x75, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x34, 0x0a, 0x15, 0x62, 0x79, 0x74, 0x65, 0x73, 0x45, + 0x71, 0x75, 0x69, 0x70, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x61, 0x64, 0x4f, 0x75, 0x74, 0x18, + 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x15, 0x62, 0x79, 0x74, 0x65, 0x73, 0x45, 0x71, 0x75, 0x69, + 0x70, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x61, 0x64, 0x4f, 0x75, 0x74, 0x12, 0x2e, 0x0a, 0x12, + 0x62, 0x79, 0x74, 0x65, 0x73, 0x46, 0x61, 0x69, 0x72, 0x4d, 0x51, 0x52, 0x65, 0x61, 0x64, 0x4f, + 0x75, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x12, 0x62, 0x79, 0x74, 0x65, 0x73, 0x46, + 0x61, 0x69, 0x72, 0x4d, 0x51, 0x52, 0x65, 0x61, 0x64, 0x4f, 0x75, 0x74, 0x12, 0x26, 0x0a, 0x0e, + 0x62, 0x79, 0x74, 0x65, 0x73, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x64, 0x18, 0x08, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x0e, 0x62, 0x79, 0x74, 0x65, 0x73, 0x50, 0x72, 0x6f, 0x63, 0x65, + 0x73, 0x73, 0x65, 0x64, 0x12, 0x34, 0x0a, 0x15, 0x62, 0x79, 0x74, 0x65, 0x73, 0x52, 0x65, 0x63, + 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x61, 0x64, 0x4f, 0x75, 0x74, 0x18, 0x09, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x15, 0x62, 0x79, 0x74, 0x65, 0x73, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, + 0x69, 0x6e, 0x67, 0x52, 0x65, 0x61, 0x64, 0x4f, 0x75, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x6e, 0x54, + 0x69, 0x6d, 0x65, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x04, 0x52, + 0x0b, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x32, 0xaa, 0x01, 0x0a, + 0x0a, 0x46, 0x6c, 0x70, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x4e, 0x0a, 0x0a, 0x43, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x4d, 0x61, 0x6e, 0x79, 0x12, 0x27, 0x2e, 0x6f, 0x32, 0x2e, 0x62, + 0x6f, 0x6f, 0x6b, 0x6b, 0x65, 0x65, 0x70, 0x69, 0x6e, 0x67, 0x2e, 0x4d, 0x61, 0x6e, 0x79, 0x46, + 0x6c, 0x70, 0x73, 0x43, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x6f, 0x32, 0x2e, 0x62, 0x6f, 0x6f, 0x6b, 0x6b, 0x65, 0x65, 0x70, + 0x69, 0x6e, 0x67, 0x2e, 0x46, 0x6c, 0x70, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x4c, 0x0a, 0x0e, 0x55, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x12, 0x25, 0x2e, + 0x6f, 0x32, 0x2e, 0x62, 0x6f, 0x6f, 0x6b, 0x6b, 0x65, 0x65, 0x70, 0x69, 0x6e, 0x67, 0x2e, 0x55, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x6f, 0x32, 0x2e, 0x62, 0x6f, 0x6f, 0x6b, 0x6b, 0x65, + 0x65, 0x70, 0x69, 0x6e, 0x67, 0x2e, 0x46, 0x6c, 0x70, 0x42, 0x4a, 0x5a, 0x48, 0x67, 0x69, 0x74, + 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x41, 0x6c, 0x69, 0x63, 0x65, 0x4f, 0x32, 0x47, + 0x72, 0x6f, 0x75, 0x70, 0x2f, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2f, 0x63, 0x6f, 0x72, + 0x65, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x62, 0x6f, + 0x6f, 0x6b, 0x6b, 0x65, 0x65, 0x70, 0x69, 0x6e, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, + 0x3b, 0x62, 0x6b, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_protos_flp_proto_rawDescOnce sync.Once + file_protos_flp_proto_rawDescData = file_protos_flp_proto_rawDesc +) + +func file_protos_flp_proto_rawDescGZIP() []byte { + file_protos_flp_proto_rawDescOnce.Do(func() { + file_protos_flp_proto_rawDescData = protoimpl.X.CompressGZIP(file_protos_flp_proto_rawDescData) + }) + return file_protos_flp_proto_rawDescData +} + +var file_protos_flp_proto_msgTypes = make([]protoimpl.MessageInfo, 5) +var file_protos_flp_proto_goTypes = []interface{}{ + (*FlpCreationRequest)(nil), // 0: o2.bookkeeping.FlpCreationRequest + (*ManyFlpsCreationRequest)(nil), // 1: o2.bookkeeping.ManyFlpsCreationRequest + (*UpdateCountersRequest)(nil), // 2: o2.bookkeeping.UpdateCountersRequest + (*FlpList)(nil), // 3: o2.bookkeeping.FlpList + (*Flp)(nil), // 4: o2.bookkeeping.Flp +} +var file_protos_flp_proto_depIdxs = []int32{ + 0, // 0: o2.bookkeeping.ManyFlpsCreationRequest.flps:type_name -> o2.bookkeeping.FlpCreationRequest + 4, // 1: o2.bookkeeping.FlpList.flps:type_name -> o2.bookkeeping.Flp + 1, // 2: o2.bookkeeping.FlpService.CreateMany:input_type -> o2.bookkeeping.ManyFlpsCreationRequest + 2, // 3: o2.bookkeeping.FlpService.UpdateCounters:input_type -> o2.bookkeeping.UpdateCountersRequest + 3, // 4: o2.bookkeeping.FlpService.CreateMany:output_type -> o2.bookkeeping.FlpList + 4, // 5: o2.bookkeeping.FlpService.UpdateCounters:output_type -> o2.bookkeeping.Flp + 4, // [4:6] is the sub-list for method output_type + 2, // [2:4] is the sub-list for method input_type + 2, // [2:2] is the sub-list for extension type_name + 2, // [2:2] is the sub-list for extension extendee + 0, // [0:2] is the sub-list for field type_name +} + +func init() { file_protos_flp_proto_init() } +func file_protos_flp_proto_init() { + if File_protos_flp_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_protos_flp_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*FlpCreationRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_flp_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ManyFlpsCreationRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_flp_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UpdateCountersRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_flp_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*FlpList); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_flp_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Flp); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + file_protos_flp_proto_msgTypes[0].OneofWrappers = []interface{}{} + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_protos_flp_proto_rawDesc, + NumEnums: 0, + NumMessages: 5, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_protos_flp_proto_goTypes, + DependencyIndexes: file_protos_flp_proto_depIdxs, + MessageInfos: file_protos_flp_proto_msgTypes, + }.Build() + File_protos_flp_proto = out.File + file_protos_flp_proto_rawDesc = nil + file_protos_flp_proto_goTypes = nil + file_protos_flp_proto_depIdxs = nil +} diff --git a/core/integration/bookkeeping/protos/flp.proto b/core/integration/bookkeeping/protos/flp.proto new file mode 100644 index 000000000..8a7309a27 --- /dev/null +++ b/core/integration/bookkeeping/protos/flp.proto @@ -0,0 +1,53 @@ +syntax = 'proto3'; + +package o2.bookkeeping; +option go_package = "github.com/AliceO2Group/Control/core/integration/bookkeeping/protos;bkpb"; + +// Services + +service FlpService { + rpc CreateMany(ManyFlpsCreationRequest) returns (FlpList); + rpc UpdateCounters(UpdateCountersRequest) returns (Flp); +} + +// High level messages + +message FlpCreationRequest { + string name = 1; + string hostname = 2; + optional int32 runNumber = 3; +} + +message ManyFlpsCreationRequest { + repeated FlpCreationRequest flps = 1; +} + +message UpdateCountersRequest { + string flpName = 1; + int32 runNumber = 2; + uint64 nSubTimeframes = 3; + uint64 nEquipmentBytes = 4; + uint64 nRecordingBytes = 5; + uint64 nFairMQBytes = 6; +} + +message FlpList { + repeated Flp flps = 1; +} + +// Low-level messages and enums + +message Flp { + int32 id = 1; + string name = 2; + string hostname = 3; + // Unix timestamp when this entity was created. + int64 createdAt = 4; + // Unix timestamp when this entity was last updated. + int64 updatedAt = 5; + uint64 bytesEquipmentReadOut = 6; + uint64 bytesFairMQReadOut = 7; + uint64 bytesProcessed = 8; + uint64 bytesRecordingReadOut = 9; + uint64 nTimeframes = 10; +} diff --git a/core/integration/bookkeeping/protos/flp_grpc.pb.go b/core/integration/bookkeeping/protos/flp_grpc.pb.go new file mode 100644 index 000000000..0a6baa76d --- /dev/null +++ b/core/integration/bookkeeping/protos/flp_grpc.pb.go @@ -0,0 +1,144 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.3.0 +// - protoc v5.29.3 +// source: protos/flp.proto + +package bkpb + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +const ( + FlpService_CreateMany_FullMethodName = "/o2.bookkeeping.FlpService/CreateMany" + FlpService_UpdateCounters_FullMethodName = "/o2.bookkeeping.FlpService/UpdateCounters" +) + +// FlpServiceClient is the client API for FlpService service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type FlpServiceClient interface { + CreateMany(ctx context.Context, in *ManyFlpsCreationRequest, opts ...grpc.CallOption) (*FlpList, error) + UpdateCounters(ctx context.Context, in *UpdateCountersRequest, opts ...grpc.CallOption) (*Flp, error) +} + +type flpServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewFlpServiceClient(cc grpc.ClientConnInterface) FlpServiceClient { + return &flpServiceClient{cc} +} + +func (c *flpServiceClient) CreateMany(ctx context.Context, in *ManyFlpsCreationRequest, opts ...grpc.CallOption) (*FlpList, error) { + out := new(FlpList) + err := c.cc.Invoke(ctx, FlpService_CreateMany_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *flpServiceClient) UpdateCounters(ctx context.Context, in *UpdateCountersRequest, opts ...grpc.CallOption) (*Flp, error) { + out := new(Flp) + err := c.cc.Invoke(ctx, FlpService_UpdateCounters_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// FlpServiceServer is the server API for FlpService service. +// All implementations should embed UnimplementedFlpServiceServer +// for forward compatibility +type FlpServiceServer interface { + CreateMany(context.Context, *ManyFlpsCreationRequest) (*FlpList, error) + UpdateCounters(context.Context, *UpdateCountersRequest) (*Flp, error) +} + +// UnimplementedFlpServiceServer should be embedded to have forward compatible implementations. +type UnimplementedFlpServiceServer struct { +} + +func (UnimplementedFlpServiceServer) CreateMany(context.Context, *ManyFlpsCreationRequest) (*FlpList, error) { + return nil, status.Errorf(codes.Unimplemented, "method CreateMany not implemented") +} +func (UnimplementedFlpServiceServer) UpdateCounters(context.Context, *UpdateCountersRequest) (*Flp, error) { + return nil, status.Errorf(codes.Unimplemented, "method UpdateCounters not implemented") +} + +// UnsafeFlpServiceServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to FlpServiceServer will +// result in compilation errors. +type UnsafeFlpServiceServer interface { + mustEmbedUnimplementedFlpServiceServer() +} + +func RegisterFlpServiceServer(s grpc.ServiceRegistrar, srv FlpServiceServer) { + s.RegisterService(&FlpService_ServiceDesc, srv) +} + +func _FlpService_CreateMany_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ManyFlpsCreationRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(FlpServiceServer).CreateMany(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: FlpService_CreateMany_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(FlpServiceServer).CreateMany(ctx, req.(*ManyFlpsCreationRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _FlpService_UpdateCounters_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UpdateCountersRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(FlpServiceServer).UpdateCounters(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: FlpService_UpdateCounters_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(FlpServiceServer).UpdateCounters(ctx, req.(*UpdateCountersRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// FlpService_ServiceDesc is the grpc.ServiceDesc for FlpService service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var FlpService_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "o2.bookkeeping.FlpService", + HandlerType: (*FlpServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "CreateMany", + Handler: _FlpService_CreateMany_Handler, + }, + { + MethodName: "UpdateCounters", + Handler: _FlpService_UpdateCounters_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "protos/flp.proto", +} diff --git a/core/integration/bookkeeping/protos/lhcFill.pb.go b/core/integration/bookkeeping/protos/lhcFill.pb.go new file mode 100644 index 000000000..e615a4139 --- /dev/null +++ b/core/integration/bookkeeping/protos/lhcFill.pb.go @@ -0,0 +1,215 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.34.1 +// protoc v5.29.3 +// source: protos/lhcFill.proto + +package bkpb + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type LastLhcFillFetchRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *LastLhcFillFetchRequest) Reset() { + *x = LastLhcFillFetchRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_lhcFill_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *LastLhcFillFetchRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*LastLhcFillFetchRequest) ProtoMessage() {} + +func (x *LastLhcFillFetchRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_lhcFill_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use LastLhcFillFetchRequest.ProtoReflect.Descriptor instead. +func (*LastLhcFillFetchRequest) Descriptor() ([]byte, []int) { + return file_protos_lhcFill_proto_rawDescGZIP(), []int{0} +} + +type LhcFillWithRelations struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + LhcFill *LHCFill `protobuf:"bytes,1,opt,name=lhcFill,proto3" json:"lhcFill,omitempty"` // For now we do not include relations to LHC fill +} + +func (x *LhcFillWithRelations) Reset() { + *x = LhcFillWithRelations{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_lhcFill_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *LhcFillWithRelations) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*LhcFillWithRelations) ProtoMessage() {} + +func (x *LhcFillWithRelations) ProtoReflect() protoreflect.Message { + mi := &file_protos_lhcFill_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use LhcFillWithRelations.ProtoReflect.Descriptor instead. +func (*LhcFillWithRelations) Descriptor() ([]byte, []int) { + return file_protos_lhcFill_proto_rawDescGZIP(), []int{1} +} + +func (x *LhcFillWithRelations) GetLhcFill() *LHCFill { + if x != nil { + return x.LhcFill + } + return nil +} + +var File_protos_lhcFill_proto protoreflect.FileDescriptor + +var file_protos_lhcFill_proto_rawDesc = []byte{ + 0x0a, 0x14, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x6c, 0x68, 0x63, 0x46, 0x69, 0x6c, 0x6c, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0e, 0x6f, 0x32, 0x2e, 0x62, 0x6f, 0x6f, 0x6b, 0x6b, + 0x65, 0x65, 0x70, 0x69, 0x6e, 0x67, 0x1a, 0x15, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x62, + 0x6b, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x19, 0x0a, + 0x17, 0x4c, 0x61, 0x73, 0x74, 0x4c, 0x68, 0x63, 0x46, 0x69, 0x6c, 0x6c, 0x46, 0x65, 0x74, 0x63, + 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x49, 0x0a, 0x14, 0x4c, 0x68, 0x63, 0x46, + 0x69, 0x6c, 0x6c, 0x57, 0x69, 0x74, 0x68, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x12, 0x31, 0x0a, 0x07, 0x6c, 0x68, 0x63, 0x46, 0x69, 0x6c, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x17, 0x2e, 0x6f, 0x32, 0x2e, 0x62, 0x6f, 0x6f, 0x6b, 0x6b, 0x65, 0x65, 0x70, 0x69, + 0x6e, 0x67, 0x2e, 0x4c, 0x48, 0x43, 0x46, 0x69, 0x6c, 0x6c, 0x52, 0x07, 0x6c, 0x68, 0x63, 0x46, + 0x69, 0x6c, 0x6c, 0x32, 0x6a, 0x0a, 0x0e, 0x4c, 0x68, 0x63, 0x46, 0x69, 0x6c, 0x6c, 0x53, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x58, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x4c, 0x61, 0x73, 0x74, + 0x12, 0x27, 0x2e, 0x6f, 0x32, 0x2e, 0x62, 0x6f, 0x6f, 0x6b, 0x6b, 0x65, 0x65, 0x70, 0x69, 0x6e, + 0x67, 0x2e, 0x4c, 0x61, 0x73, 0x74, 0x4c, 0x68, 0x63, 0x46, 0x69, 0x6c, 0x6c, 0x46, 0x65, 0x74, + 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x6f, 0x32, 0x2e, 0x62, + 0x6f, 0x6f, 0x6b, 0x6b, 0x65, 0x65, 0x70, 0x69, 0x6e, 0x67, 0x2e, 0x4c, 0x68, 0x63, 0x46, 0x69, + 0x6c, 0x6c, 0x57, 0x69, 0x74, 0x68, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x42, + 0x4a, 0x5a, 0x48, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x41, 0x6c, + 0x69, 0x63, 0x65, 0x4f, 0x32, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x2f, 0x43, 0x6f, 0x6e, 0x74, 0x72, + 0x6f, 0x6c, 0x2f, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x2f, 0x62, 0x6f, 0x6f, 0x6b, 0x6b, 0x65, 0x65, 0x70, 0x69, 0x6e, 0x67, 0x2f, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x3b, 0x62, 0x6b, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x33, +} + +var ( + file_protos_lhcFill_proto_rawDescOnce sync.Once + file_protos_lhcFill_proto_rawDescData = file_protos_lhcFill_proto_rawDesc +) + +func file_protos_lhcFill_proto_rawDescGZIP() []byte { + file_protos_lhcFill_proto_rawDescOnce.Do(func() { + file_protos_lhcFill_proto_rawDescData = protoimpl.X.CompressGZIP(file_protos_lhcFill_proto_rawDescData) + }) + return file_protos_lhcFill_proto_rawDescData +} + +var file_protos_lhcFill_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_protos_lhcFill_proto_goTypes = []interface{}{ + (*LastLhcFillFetchRequest)(nil), // 0: o2.bookkeeping.LastLhcFillFetchRequest + (*LhcFillWithRelations)(nil), // 1: o2.bookkeeping.LhcFillWithRelations + (*LHCFill)(nil), // 2: o2.bookkeeping.LHCFill +} +var file_protos_lhcFill_proto_depIdxs = []int32{ + 2, // 0: o2.bookkeeping.LhcFillWithRelations.lhcFill:type_name -> o2.bookkeeping.LHCFill + 0, // 1: o2.bookkeeping.LhcFillService.GetLast:input_type -> o2.bookkeeping.LastLhcFillFetchRequest + 1, // 2: o2.bookkeeping.LhcFillService.GetLast:output_type -> o2.bookkeeping.LhcFillWithRelations + 2, // [2:3] is the sub-list for method output_type + 1, // [1:2] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name +} + +func init() { file_protos_lhcFill_proto_init() } +func file_protos_lhcFill_proto_init() { + if File_protos_lhcFill_proto != nil { + return + } + file_protos_bkcommon_proto_init() + if !protoimpl.UnsafeEnabled { + file_protos_lhcFill_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*LastLhcFillFetchRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_lhcFill_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*LhcFillWithRelations); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_protos_lhcFill_proto_rawDesc, + NumEnums: 0, + NumMessages: 2, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_protos_lhcFill_proto_goTypes, + DependencyIndexes: file_protos_lhcFill_proto_depIdxs, + MessageInfos: file_protos_lhcFill_proto_msgTypes, + }.Build() + File_protos_lhcFill_proto = out.File + file_protos_lhcFill_proto_rawDesc = nil + file_protos_lhcFill_proto_goTypes = nil + file_protos_lhcFill_proto_depIdxs = nil +} diff --git a/core/integration/bookkeeping/protos/lhcFill.proto b/core/integration/bookkeeping/protos/lhcFill.proto new file mode 100644 index 000000000..b64435f4b --- /dev/null +++ b/core/integration/bookkeeping/protos/lhcFill.proto @@ -0,0 +1,23 @@ +syntax = 'proto3'; + +package o2.bookkeeping; +option go_package = "github.com/AliceO2Group/Control/core/integration/bookkeeping/protos;bkpb"; + +import "protos/bkcommon.proto"; + +// Services + +service LhcFillService { + rpc GetLast(LastLhcFillFetchRequest) returns (LhcFillWithRelations); +} + +// High level messages + +message LastLhcFillFetchRequest { + // For now, request is empty +} + +message LhcFillWithRelations { + LHCFill lhcFill = 1; + // For now we do not include relations to LHC fill +} diff --git a/core/integration/bookkeeping/protos/lhcFill_grpc.pb.go b/core/integration/bookkeeping/protos/lhcFill_grpc.pb.go new file mode 100644 index 000000000..17eca77f7 --- /dev/null +++ b/core/integration/bookkeeping/protos/lhcFill_grpc.pb.go @@ -0,0 +1,107 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.3.0 +// - protoc v5.29.3 +// source: protos/lhcFill.proto + +package bkpb + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +const ( + LhcFillService_GetLast_FullMethodName = "/o2.bookkeeping.LhcFillService/GetLast" +) + +// LhcFillServiceClient is the client API for LhcFillService service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type LhcFillServiceClient interface { + GetLast(ctx context.Context, in *LastLhcFillFetchRequest, opts ...grpc.CallOption) (*LhcFillWithRelations, error) +} + +type lhcFillServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewLhcFillServiceClient(cc grpc.ClientConnInterface) LhcFillServiceClient { + return &lhcFillServiceClient{cc} +} + +func (c *lhcFillServiceClient) GetLast(ctx context.Context, in *LastLhcFillFetchRequest, opts ...grpc.CallOption) (*LhcFillWithRelations, error) { + out := new(LhcFillWithRelations) + err := c.cc.Invoke(ctx, LhcFillService_GetLast_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// LhcFillServiceServer is the server API for LhcFillService service. +// All implementations should embed UnimplementedLhcFillServiceServer +// for forward compatibility +type LhcFillServiceServer interface { + GetLast(context.Context, *LastLhcFillFetchRequest) (*LhcFillWithRelations, error) +} + +// UnimplementedLhcFillServiceServer should be embedded to have forward compatible implementations. +type UnimplementedLhcFillServiceServer struct { +} + +func (UnimplementedLhcFillServiceServer) GetLast(context.Context, *LastLhcFillFetchRequest) (*LhcFillWithRelations, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetLast not implemented") +} + +// UnsafeLhcFillServiceServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to LhcFillServiceServer will +// result in compilation errors. +type UnsafeLhcFillServiceServer interface { + mustEmbedUnimplementedLhcFillServiceServer() +} + +func RegisterLhcFillServiceServer(s grpc.ServiceRegistrar, srv LhcFillServiceServer) { + s.RegisterService(&LhcFillService_ServiceDesc, srv) +} + +func _LhcFillService_GetLast_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(LastLhcFillFetchRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(LhcFillServiceServer).GetLast(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: LhcFillService_GetLast_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(LhcFillServiceServer).GetLast(ctx, req.(*LastLhcFillFetchRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// LhcFillService_ServiceDesc is the grpc.ServiceDesc for LhcFillService service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var LhcFillService_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "o2.bookkeeping.LhcFillService", + HandlerType: (*LhcFillServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "GetLast", + Handler: _LhcFillService_GetLast_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "protos/lhcFill.proto", +} diff --git a/core/integration/bookkeeping/protos/log.pb.go b/core/integration/bookkeeping/protos/log.pb.go new file mode 100644 index 000000000..5b97693a4 --- /dev/null +++ b/core/integration/bookkeeping/protos/log.pb.go @@ -0,0 +1,881 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.34.1 +// protoc v5.29.3 +// source: protos/log.proto + +package bkpb + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type LogOrigin int32 + +const ( + LogOrigin_LOG_ORIGIN_NULL LogOrigin = 0 + LogOrigin_LOG_ORIGIN_HUMAN LogOrigin = 1 + LogOrigin_LOG_ORIGIN_PROCESS LogOrigin = 2 +) + +// Enum value maps for LogOrigin. +var ( + LogOrigin_name = map[int32]string{ + 0: "LOG_ORIGIN_NULL", + 1: "LOG_ORIGIN_HUMAN", + 2: "LOG_ORIGIN_PROCESS", + } + LogOrigin_value = map[string]int32{ + "LOG_ORIGIN_NULL": 0, + "LOG_ORIGIN_HUMAN": 1, + "LOG_ORIGIN_PROCESS": 2, + } +) + +func (x LogOrigin) Enum() *LogOrigin { + p := new(LogOrigin) + *p = x + return p +} + +func (x LogOrigin) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (LogOrigin) Descriptor() protoreflect.EnumDescriptor { + return file_protos_log_proto_enumTypes[0].Descriptor() +} + +func (LogOrigin) Type() protoreflect.EnumType { + return &file_protos_log_proto_enumTypes[0] +} + +func (x LogOrigin) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use LogOrigin.Descriptor instead. +func (LogOrigin) EnumDescriptor() ([]byte, []int) { + return file_protos_log_proto_rawDescGZIP(), []int{0} +} + +type LogSubtype int32 + +const ( + LogSubtype_LOG_SUBTYPE_NULL LogSubtype = 0 + LogSubtype_LOG_SUBTYPE_RUN LogSubtype = 1 + LogSubtype_LOG_SUBTYPE_ANNOUNCEMENT LogSubtype = 3 + LogSubtype_LOG_SUBTYPE_INTERVENTION LogSubtype = 4 + LogSubtype_LOG_SUBTYPE_COMMENT LogSubtype = 5 +) + +// Enum value maps for LogSubtype. +var ( + LogSubtype_name = map[int32]string{ + 0: "LOG_SUBTYPE_NULL", + 1: "LOG_SUBTYPE_RUN", + 3: "LOG_SUBTYPE_ANNOUNCEMENT", + 4: "LOG_SUBTYPE_INTERVENTION", + 5: "LOG_SUBTYPE_COMMENT", + } + LogSubtype_value = map[string]int32{ + "LOG_SUBTYPE_NULL": 0, + "LOG_SUBTYPE_RUN": 1, + "LOG_SUBTYPE_ANNOUNCEMENT": 3, + "LOG_SUBTYPE_INTERVENTION": 4, + "LOG_SUBTYPE_COMMENT": 5, + } +) + +func (x LogSubtype) Enum() *LogSubtype { + p := new(LogSubtype) + *p = x + return p +} + +func (x LogSubtype) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (LogSubtype) Descriptor() protoreflect.EnumDescriptor { + return file_protos_log_proto_enumTypes[1].Descriptor() +} + +func (LogSubtype) Type() protoreflect.EnumType { + return &file_protos_log_proto_enumTypes[1] +} + +func (x LogSubtype) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use LogSubtype.Descriptor instead. +func (LogSubtype) EnumDescriptor() ([]byte, []int) { + return file_protos_log_proto_rawDescGZIP(), []int{1} +} + +type LogCreationRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Title string `protobuf:"bytes,1,opt,name=title,proto3" json:"title,omitempty"` + Text string `protobuf:"bytes,2,opt,name=text,proto3" json:"text,omitempty"` + RunNumbers []int32 `protobuf:"varint,3,rep,packed,name=runNumbers,proto3" json:"runNumbers,omitempty"` + ParentLogId *int32 `protobuf:"varint,4,opt,name=parentLogId,proto3,oneof" json:"parentLogId,omitempty"` +} + +func (x *LogCreationRequest) Reset() { + *x = LogCreationRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_log_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *LogCreationRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*LogCreationRequest) ProtoMessage() {} + +func (x *LogCreationRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_log_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use LogCreationRequest.ProtoReflect.Descriptor instead. +func (*LogCreationRequest) Descriptor() ([]byte, []int) { + return file_protos_log_proto_rawDescGZIP(), []int{0} +} + +func (x *LogCreationRequest) GetTitle() string { + if x != nil { + return x.Title + } + return "" +} + +func (x *LogCreationRequest) GetText() string { + if x != nil { + return x.Text + } + return "" +} + +func (x *LogCreationRequest) GetRunNumbers() []int32 { + if x != nil { + return x.RunNumbers + } + return nil +} + +func (x *LogCreationRequest) GetParentLogId() int32 { + if x != nil && x.ParentLogId != nil { + return *x.ParentLogId + } + return 0 +} + +type Attachment struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Unix timestamp when this entity was created. + CreatedAt int64 `protobuf:"varint,1,opt,name=createdAt,proto3" json:"createdAt,omitempty"` + Encoding string `protobuf:"bytes,2,opt,name=encoding,proto3" json:"encoding,omitempty"` + FileName string `protobuf:"bytes,3,opt,name=fileName,proto3" json:"fileName,omitempty"` + Id int32 `protobuf:"varint,4,opt,name=id,proto3" json:"id,omitempty"` + LogId int32 `protobuf:"varint,5,opt,name=logId,proto3" json:"logId,omitempty"` + MimeType string `protobuf:"bytes,6,opt,name=mimeType,proto3" json:"mimeType,omitempty"` + OriginalName string `protobuf:"bytes,7,opt,name=originalName,proto3" json:"originalName,omitempty"` + Path string `protobuf:"bytes,8,opt,name=path,proto3" json:"path,omitempty"` + Size int32 `protobuf:"varint,9,opt,name=size,proto3" json:"size,omitempty"` + // Unix timestamp when this entity was created. + UpdatedAt int64 `protobuf:"varint,10,opt,name=updatedAt,proto3" json:"updatedAt,omitempty"` +} + +func (x *Attachment) Reset() { + *x = Attachment{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_log_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Attachment) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Attachment) ProtoMessage() {} + +func (x *Attachment) ProtoReflect() protoreflect.Message { + mi := &file_protos_log_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Attachment.ProtoReflect.Descriptor instead. +func (*Attachment) Descriptor() ([]byte, []int) { + return file_protos_log_proto_rawDescGZIP(), []int{1} +} + +func (x *Attachment) GetCreatedAt() int64 { + if x != nil { + return x.CreatedAt + } + return 0 +} + +func (x *Attachment) GetEncoding() string { + if x != nil { + return x.Encoding + } + return "" +} + +func (x *Attachment) GetFileName() string { + if x != nil { + return x.FileName + } + return "" +} + +func (x *Attachment) GetId() int32 { + if x != nil { + return x.Id + } + return 0 +} + +func (x *Attachment) GetLogId() int32 { + if x != nil { + return x.LogId + } + return 0 +} + +func (x *Attachment) GetMimeType() string { + if x != nil { + return x.MimeType + } + return "" +} + +func (x *Attachment) GetOriginalName() string { + if x != nil { + return x.OriginalName + } + return "" +} + +func (x *Attachment) GetPath() string { + if x != nil { + return x.Path + } + return "" +} + +func (x *Attachment) GetSize() int32 { + if x != nil { + return x.Size + } + return 0 +} + +func (x *Attachment) GetUpdatedAt() int64 { + if x != nil { + return x.UpdatedAt + } + return 0 +} + +type Log struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Attachments []*Attachment `protobuf:"bytes,1,rep,name=attachments,proto3" json:"attachments,omitempty"` + Author *User `protobuf:"bytes,2,opt,name=author,proto3" json:"author,omitempty"` + // Unix timestamp when this entity was created. + CreatedAt int64 `protobuf:"varint,3,opt,name=CreatedAt,proto3" json:"CreatedAt,omitempty"` + Id int32 `protobuf:"varint,4,opt,name=id,proto3" json:"id,omitempty"` + Origin *LogOrigin `protobuf:"varint,5,opt,name=origin,proto3,enum=o2.bookkeeping.LogOrigin,oneof" json:"origin,omitempty"` + ParentLogId int32 `protobuf:"varint,6,opt,name=parentLogId,proto3" json:"parentLogId,omitempty"` + Replies int32 `protobuf:"varint,7,opt,name=Replies,proto3" json:"Replies,omitempty"` + RootLogId int32 `protobuf:"varint,8,opt,name=rootLogId,proto3" json:"rootLogId,omitempty"` + // Array of minified Run objects. + Runs []*MinimalRun `protobuf:"bytes,9,rep,name=runs,proto3" json:"runs,omitempty"` + Subtype LogSubtype `protobuf:"varint,11,opt,name=subtype,proto3,enum=o2.bookkeeping.LogSubtype" json:"subtype,omitempty"` + Tags []*Tag `protobuf:"bytes,12,rep,name=tags,proto3" json:"tags,omitempty"` + Text string `protobuf:"bytes,14,opt,name=text,proto3" json:"text,omitempty"` + Title string `protobuf:"bytes,15,opt,name=title,proto3" json:"title,omitempty"` + // Unix timestamp when this entity was last updated. + UpdatedAt int64 `protobuf:"varint,16,opt,name=updatedAt,proto3" json:"updatedAt,omitempty"` +} + +func (x *Log) Reset() { + *x = Log{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_log_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Log) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Log) ProtoMessage() {} + +func (x *Log) ProtoReflect() protoreflect.Message { + mi := &file_protos_log_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Log.ProtoReflect.Descriptor instead. +func (*Log) Descriptor() ([]byte, []int) { + return file_protos_log_proto_rawDescGZIP(), []int{2} +} + +func (x *Log) GetAttachments() []*Attachment { + if x != nil { + return x.Attachments + } + return nil +} + +func (x *Log) GetAuthor() *User { + if x != nil { + return x.Author + } + return nil +} + +func (x *Log) GetCreatedAt() int64 { + if x != nil { + return x.CreatedAt + } + return 0 +} + +func (x *Log) GetId() int32 { + if x != nil { + return x.Id + } + return 0 +} + +func (x *Log) GetOrigin() LogOrigin { + if x != nil && x.Origin != nil { + return *x.Origin + } + return LogOrigin_LOG_ORIGIN_NULL +} + +func (x *Log) GetParentLogId() int32 { + if x != nil { + return x.ParentLogId + } + return 0 +} + +func (x *Log) GetReplies() int32 { + if x != nil { + return x.Replies + } + return 0 +} + +func (x *Log) GetRootLogId() int32 { + if x != nil { + return x.RootLogId + } + return 0 +} + +func (x *Log) GetRuns() []*MinimalRun { + if x != nil { + return x.Runs + } + return nil +} + +func (x *Log) GetSubtype() LogSubtype { + if x != nil { + return x.Subtype + } + return LogSubtype_LOG_SUBTYPE_NULL +} + +func (x *Log) GetTags() []*Tag { + if x != nil { + return x.Tags + } + return nil +} + +func (x *Log) GetText() string { + if x != nil { + return x.Text + } + return "" +} + +func (x *Log) GetTitle() string { + if x != nil { + return x.Title + } + return "" +} + +func (x *Log) GetUpdatedAt() int64 { + if x != nil { + return x.UpdatedAt + } + return 0 +} + +type MinimalRun struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + RunNumber int32 `protobuf:"varint,1,opt,name=runNumber,proto3" json:"runNumber,omitempty"` + Id int32 `protobuf:"varint,2,opt,name=id,proto3" json:"id,omitempty"` +} + +func (x *MinimalRun) Reset() { + *x = MinimalRun{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_log_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *MinimalRun) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*MinimalRun) ProtoMessage() {} + +func (x *MinimalRun) ProtoReflect() protoreflect.Message { + mi := &file_protos_log_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use MinimalRun.ProtoReflect.Descriptor instead. +func (*MinimalRun) Descriptor() ([]byte, []int) { + return file_protos_log_proto_rawDescGZIP(), []int{3} +} + +func (x *MinimalRun) GetRunNumber() int32 { + if x != nil { + return x.RunNumber + } + return 0 +} + +func (x *MinimalRun) GetId() int32 { + if x != nil { + return x.Id + } + return 0 +} + +type Tag struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Unix timestamp when this entity was created. + CreatedAt int64 `protobuf:"varint,1,opt,name=createdAt,proto3" json:"createdAt,omitempty"` + Id int32 `protobuf:"varint,2,opt,name=id,proto3" json:"id,omitempty"` + // The label value of the tag. + Text string `protobuf:"bytes,3,opt,name=text,proto3" json:"text,omitempty"` + // The email for the tag. + Email string `protobuf:"bytes,4,opt,name=email,proto3" json:"email,omitempty"` + // All the mattermost tags for updates + Mattermost string `protobuf:"bytes,5,opt,name=mattermost,proto3" json:"mattermost,omitempty"` + // Unix timestamp when this entity was last updated. + UpdatedAt int64 `protobuf:"varint,6,opt,name=updatedAt,proto3" json:"updatedAt,omitempty"` + // The last person that edited the email/mattermost fields + LastEditedName string `protobuf:"bytes,7,opt,name=lastEditedName,proto3" json:"lastEditedName,omitempty"` + // The description of the tag + Description *string `protobuf:"bytes,8,opt,name=description,proto3,oneof" json:"description,omitempty"` +} + +func (x *Tag) Reset() { + *x = Tag{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_log_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Tag) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Tag) ProtoMessage() {} + +func (x *Tag) ProtoReflect() protoreflect.Message { + mi := &file_protos_log_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Tag.ProtoReflect.Descriptor instead. +func (*Tag) Descriptor() ([]byte, []int) { + return file_protos_log_proto_rawDescGZIP(), []int{4} +} + +func (x *Tag) GetCreatedAt() int64 { + if x != nil { + return x.CreatedAt + } + return 0 +} + +func (x *Tag) GetId() int32 { + if x != nil { + return x.Id + } + return 0 +} + +func (x *Tag) GetText() string { + if x != nil { + return x.Text + } + return "" +} + +func (x *Tag) GetEmail() string { + if x != nil { + return x.Email + } + return "" +} + +func (x *Tag) GetMattermost() string { + if x != nil { + return x.Mattermost + } + return "" +} + +func (x *Tag) GetUpdatedAt() int64 { + if x != nil { + return x.UpdatedAt + } + return 0 +} + +func (x *Tag) GetLastEditedName() string { + if x != nil { + return x.LastEditedName + } + return "" +} + +func (x *Tag) GetDescription() string { + if x != nil && x.Description != nil { + return *x.Description + } + return "" +} + +var File_protos_log_proto protoreflect.FileDescriptor + +var file_protos_log_proto_rawDesc = []byte{ + 0x0a, 0x10, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x6c, 0x6f, 0x67, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x12, 0x0e, 0x6f, 0x32, 0x2e, 0x62, 0x6f, 0x6f, 0x6b, 0x6b, 0x65, 0x65, 0x70, 0x69, + 0x6e, 0x67, 0x1a, 0x15, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x62, 0x6b, 0x63, 0x6f, 0x6d, + 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x95, 0x01, 0x0a, 0x12, 0x4c, 0x6f, + 0x67, 0x43, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x14, 0x0a, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x65, 0x78, 0x74, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x65, 0x78, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x72, 0x75, + 0x6e, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x05, 0x52, 0x0a, + 0x72, 0x75, 0x6e, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x12, 0x25, 0x0a, 0x0b, 0x70, 0x61, + 0x72, 0x65, 0x6e, 0x74, 0x4c, 0x6f, 0x67, 0x49, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x48, + 0x00, 0x52, 0x0b, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x4c, 0x6f, 0x67, 0x49, 0x64, 0x88, 0x01, + 0x01, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x4c, 0x6f, 0x67, 0x49, + 0x64, 0x22, 0x8e, 0x02, 0x0a, 0x0a, 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, 0x6d, 0x65, 0x6e, 0x74, + 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x1a, + 0x0a, 0x08, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x08, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x12, 0x1a, 0x0a, 0x08, 0x66, 0x69, + 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, 0x69, + 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x05, 0x52, 0x02, 0x69, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x6f, 0x67, 0x49, 0x64, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x6c, 0x6f, 0x67, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, + 0x6d, 0x69, 0x6d, 0x65, 0x54, 0x79, 0x70, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, + 0x6d, 0x69, 0x6d, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x6f, 0x72, 0x69, 0x67, + 0x69, 0x6e, 0x61, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, + 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, + 0x70, 0x61, 0x74, 0x68, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, + 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, + 0x73, 0x69, 0x7a, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, + 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, + 0x41, 0x74, 0x22, 0x93, 0x04, 0x0a, 0x03, 0x4c, 0x6f, 0x67, 0x12, 0x3c, 0x0a, 0x0b, 0x61, 0x74, + 0x74, 0x61, 0x63, 0x68, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x1a, 0x2e, 0x6f, 0x32, 0x2e, 0x62, 0x6f, 0x6f, 0x6b, 0x6b, 0x65, 0x65, 0x70, 0x69, 0x6e, 0x67, + 0x2e, 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x0b, 0x61, 0x74, 0x74, + 0x61, 0x63, 0x68, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x2c, 0x0a, 0x06, 0x61, 0x75, 0x74, 0x68, + 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6f, 0x32, 0x2e, 0x62, 0x6f, + 0x6f, 0x6b, 0x6b, 0x65, 0x65, 0x70, 0x69, 0x6e, 0x67, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x06, + 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x12, 0x1c, 0x0a, 0x09, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, + 0x64, 0x41, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x43, 0x72, 0x65, 0x61, 0x74, + 0x65, 0x64, 0x41, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, + 0x52, 0x02, 0x69, 0x64, 0x12, 0x36, 0x0a, 0x06, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x0e, 0x32, 0x19, 0x2e, 0x6f, 0x32, 0x2e, 0x62, 0x6f, 0x6f, 0x6b, 0x6b, 0x65, + 0x65, 0x70, 0x69, 0x6e, 0x67, 0x2e, 0x4c, 0x6f, 0x67, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x48, + 0x00, 0x52, 0x06, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x20, 0x0a, 0x0b, + 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x4c, 0x6f, 0x67, 0x49, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x0b, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x4c, 0x6f, 0x67, 0x49, 0x64, 0x12, 0x18, + 0x0a, 0x07, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x65, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x05, 0x52, + 0x07, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x65, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x72, 0x6f, 0x6f, 0x74, + 0x4c, 0x6f, 0x67, 0x49, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x72, 0x6f, 0x6f, + 0x74, 0x4c, 0x6f, 0x67, 0x49, 0x64, 0x12, 0x2e, 0x0a, 0x04, 0x72, 0x75, 0x6e, 0x73, 0x18, 0x09, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6f, 0x32, 0x2e, 0x62, 0x6f, 0x6f, 0x6b, 0x6b, 0x65, + 0x65, 0x70, 0x69, 0x6e, 0x67, 0x2e, 0x4d, 0x69, 0x6e, 0x69, 0x6d, 0x61, 0x6c, 0x52, 0x75, 0x6e, + 0x52, 0x04, 0x72, 0x75, 0x6e, 0x73, 0x12, 0x34, 0x0a, 0x07, 0x73, 0x75, 0x62, 0x74, 0x79, 0x70, + 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x6f, 0x32, 0x2e, 0x62, 0x6f, 0x6f, + 0x6b, 0x6b, 0x65, 0x65, 0x70, 0x69, 0x6e, 0x67, 0x2e, 0x4c, 0x6f, 0x67, 0x53, 0x75, 0x62, 0x74, + 0x79, 0x70, 0x65, 0x52, 0x07, 0x73, 0x75, 0x62, 0x74, 0x79, 0x70, 0x65, 0x12, 0x27, 0x0a, 0x04, + 0x74, 0x61, 0x67, 0x73, 0x18, 0x0c, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6f, 0x32, 0x2e, + 0x62, 0x6f, 0x6f, 0x6b, 0x6b, 0x65, 0x65, 0x70, 0x69, 0x6e, 0x67, 0x2e, 0x54, 0x61, 0x67, 0x52, + 0x04, 0x74, 0x61, 0x67, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x65, 0x78, 0x74, 0x18, 0x0e, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x65, 0x78, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x69, 0x74, + 0x6c, 0x65, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x12, + 0x1c, 0x0a, 0x09, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x18, 0x10, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x09, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x42, 0x09, 0x0a, + 0x07, 0x5f, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x22, 0x3a, 0x0a, 0x0a, 0x4d, 0x69, 0x6e, 0x69, + 0x6d, 0x61, 0x6c, 0x52, 0x75, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x72, 0x75, 0x6e, 0x4e, 0x75, 0x6d, + 0x62, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x72, 0x75, 0x6e, 0x4e, 0x75, + 0x6d, 0x62, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, + 0x52, 0x02, 0x69, 0x64, 0x22, 0xfa, 0x01, 0x0a, 0x03, 0x54, 0x61, 0x67, 0x12, 0x1c, 0x0a, 0x09, + 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x65, + 0x78, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x65, 0x78, 0x74, 0x12, 0x14, + 0x0a, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, + 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x1e, 0x0a, 0x0a, 0x6d, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6d, 0x6f, + 0x73, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6d, 0x61, 0x74, 0x74, 0x65, 0x72, + 0x6d, 0x6f, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, + 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, + 0x41, 0x74, 0x12, 0x26, 0x0a, 0x0e, 0x6c, 0x61, 0x73, 0x74, 0x45, 0x64, 0x69, 0x74, 0x65, 0x64, + 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x6c, 0x61, 0x73, 0x74, + 0x45, 0x64, 0x69, 0x74, 0x65, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x0b, 0x64, 0x65, + 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x48, + 0x00, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x88, 0x01, + 0x01, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, + 0x6e, 0x2a, 0x4e, 0x0a, 0x09, 0x4c, 0x6f, 0x67, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x12, 0x13, + 0x0a, 0x0f, 0x4c, 0x4f, 0x47, 0x5f, 0x4f, 0x52, 0x49, 0x47, 0x49, 0x4e, 0x5f, 0x4e, 0x55, 0x4c, + 0x4c, 0x10, 0x00, 0x12, 0x14, 0x0a, 0x10, 0x4c, 0x4f, 0x47, 0x5f, 0x4f, 0x52, 0x49, 0x47, 0x49, + 0x4e, 0x5f, 0x48, 0x55, 0x4d, 0x41, 0x4e, 0x10, 0x01, 0x12, 0x16, 0x0a, 0x12, 0x4c, 0x4f, 0x47, + 0x5f, 0x4f, 0x52, 0x49, 0x47, 0x49, 0x4e, 0x5f, 0x50, 0x52, 0x4f, 0x43, 0x45, 0x53, 0x53, 0x10, + 0x02, 0x2a, 0x8c, 0x01, 0x0a, 0x0a, 0x4c, 0x6f, 0x67, 0x53, 0x75, 0x62, 0x74, 0x79, 0x70, 0x65, + 0x12, 0x14, 0x0a, 0x10, 0x4c, 0x4f, 0x47, 0x5f, 0x53, 0x55, 0x42, 0x54, 0x59, 0x50, 0x45, 0x5f, + 0x4e, 0x55, 0x4c, 0x4c, 0x10, 0x00, 0x12, 0x13, 0x0a, 0x0f, 0x4c, 0x4f, 0x47, 0x5f, 0x53, 0x55, + 0x42, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x52, 0x55, 0x4e, 0x10, 0x01, 0x12, 0x1c, 0x0a, 0x18, 0x4c, + 0x4f, 0x47, 0x5f, 0x53, 0x55, 0x42, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x41, 0x4e, 0x4e, 0x4f, 0x55, + 0x4e, 0x43, 0x45, 0x4d, 0x45, 0x4e, 0x54, 0x10, 0x03, 0x12, 0x1c, 0x0a, 0x18, 0x4c, 0x4f, 0x47, + 0x5f, 0x53, 0x55, 0x42, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x49, 0x4e, 0x54, 0x45, 0x52, 0x56, 0x45, + 0x4e, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x04, 0x12, 0x17, 0x0a, 0x13, 0x4c, 0x4f, 0x47, 0x5f, 0x53, + 0x55, 0x42, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x43, 0x4f, 0x4d, 0x4d, 0x45, 0x4e, 0x54, 0x10, 0x05, + 0x32, 0x4f, 0x0a, 0x0a, 0x4c, 0x6f, 0x67, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x41, + 0x0a, 0x06, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x12, 0x22, 0x2e, 0x6f, 0x32, 0x2e, 0x62, 0x6f, + 0x6f, 0x6b, 0x6b, 0x65, 0x65, 0x70, 0x69, 0x6e, 0x67, 0x2e, 0x4c, 0x6f, 0x67, 0x43, 0x72, 0x65, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x6f, + 0x32, 0x2e, 0x62, 0x6f, 0x6f, 0x6b, 0x6b, 0x65, 0x65, 0x70, 0x69, 0x6e, 0x67, 0x2e, 0x4c, 0x6f, + 0x67, 0x42, 0x4a, 0x5a, 0x48, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x41, 0x6c, 0x69, 0x63, 0x65, 0x4f, 0x32, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x2f, 0x43, 0x6f, 0x6e, + 0x74, 0x72, 0x6f, 0x6c, 0x2f, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x62, 0x6f, 0x6f, 0x6b, 0x6b, 0x65, 0x65, 0x70, 0x69, 0x6e, + 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x3b, 0x62, 0x6b, 0x70, 0x62, 0x62, 0x06, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_protos_log_proto_rawDescOnce sync.Once + file_protos_log_proto_rawDescData = file_protos_log_proto_rawDesc +) + +func file_protos_log_proto_rawDescGZIP() []byte { + file_protos_log_proto_rawDescOnce.Do(func() { + file_protos_log_proto_rawDescData = protoimpl.X.CompressGZIP(file_protos_log_proto_rawDescData) + }) + return file_protos_log_proto_rawDescData +} + +var file_protos_log_proto_enumTypes = make([]protoimpl.EnumInfo, 2) +var file_protos_log_proto_msgTypes = make([]protoimpl.MessageInfo, 5) +var file_protos_log_proto_goTypes = []interface{}{ + (LogOrigin)(0), // 0: o2.bookkeeping.LogOrigin + (LogSubtype)(0), // 1: o2.bookkeeping.LogSubtype + (*LogCreationRequest)(nil), // 2: o2.bookkeeping.LogCreationRequest + (*Attachment)(nil), // 3: o2.bookkeeping.Attachment + (*Log)(nil), // 4: o2.bookkeeping.Log + (*MinimalRun)(nil), // 5: o2.bookkeeping.MinimalRun + (*Tag)(nil), // 6: o2.bookkeeping.Tag + (*User)(nil), // 7: o2.bookkeeping.User +} +var file_protos_log_proto_depIdxs = []int32{ + 3, // 0: o2.bookkeeping.Log.attachments:type_name -> o2.bookkeeping.Attachment + 7, // 1: o2.bookkeeping.Log.author:type_name -> o2.bookkeeping.User + 0, // 2: o2.bookkeeping.Log.origin:type_name -> o2.bookkeeping.LogOrigin + 5, // 3: o2.bookkeeping.Log.runs:type_name -> o2.bookkeeping.MinimalRun + 1, // 4: o2.bookkeeping.Log.subtype:type_name -> o2.bookkeeping.LogSubtype + 6, // 5: o2.bookkeeping.Log.tags:type_name -> o2.bookkeeping.Tag + 2, // 6: o2.bookkeeping.LogService.Create:input_type -> o2.bookkeeping.LogCreationRequest + 4, // 7: o2.bookkeeping.LogService.Create:output_type -> o2.bookkeeping.Log + 7, // [7:8] is the sub-list for method output_type + 6, // [6:7] is the sub-list for method input_type + 6, // [6:6] is the sub-list for extension type_name + 6, // [6:6] is the sub-list for extension extendee + 0, // [0:6] is the sub-list for field type_name +} + +func init() { file_protos_log_proto_init() } +func file_protos_log_proto_init() { + if File_protos_log_proto != nil { + return + } + file_protos_bkcommon_proto_init() + if !protoimpl.UnsafeEnabled { + file_protos_log_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*LogCreationRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_log_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Attachment); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_log_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Log); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_log_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*MinimalRun); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_log_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Tag); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + file_protos_log_proto_msgTypes[0].OneofWrappers = []interface{}{} + file_protos_log_proto_msgTypes[2].OneofWrappers = []interface{}{} + file_protos_log_proto_msgTypes[4].OneofWrappers = []interface{}{} + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_protos_log_proto_rawDesc, + NumEnums: 2, + NumMessages: 5, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_protos_log_proto_goTypes, + DependencyIndexes: file_protos_log_proto_depIdxs, + EnumInfos: file_protos_log_proto_enumTypes, + MessageInfos: file_protos_log_proto_msgTypes, + }.Build() + File_protos_log_proto = out.File + file_protos_log_proto_rawDesc = nil + file_protos_log_proto_goTypes = nil + file_protos_log_proto_depIdxs = nil +} diff --git a/core/integration/bookkeeping/protos/log.proto b/core/integration/bookkeeping/protos/log.proto new file mode 100644 index 000000000..d61235b7d --- /dev/null +++ b/core/integration/bookkeeping/protos/log.proto @@ -0,0 +1,95 @@ +syntax = 'proto3'; + +import "protos/bkcommon.proto"; + +package o2.bookkeeping; +option go_package = "github.com/AliceO2Group/Control/core/integration/bookkeeping/protos;bkpb"; + +// Services + +service LogService { + rpc Create(LogCreationRequest) returns (Log); +} + +// High level messages + +message LogCreationRequest { + string title = 1; + string text = 2; + repeated int32 runNumbers = 3; + optional int32 parentLogId = 4; +} + +// Low-level messages and enums + +message Attachment { + // Unix timestamp when this entity was created. + int64 createdAt = 1; + string encoding = 2; + string fileName = 3; + int32 id = 4; + int32 logId = 5; + string mimeType = 6; + string originalName = 7; + string path = 8; + int32 size = 9; + // Unix timestamp when this entity was created. + int64 updatedAt = 10; +} + +message Log { + repeated Attachment attachments = 1; + User author = 2; + // Unix timestamp when this entity was created. + int64 CreatedAt = 3; + int32 id = 4; + optional LogOrigin origin = 5; + int32 parentLogId = 6; + int32 Replies = 7; + int32 rootLogId = 8; + // Array of minified Run objects. + repeated MinimalRun runs = 9; + LogSubtype subtype = 11; + repeated Tag tags = 12; + string text = 14; + string title = 15; + // Unix timestamp when this entity was last updated. + int64 updatedAt = 16; +} + +enum LogOrigin { + LOG_ORIGIN_NULL = 0; + LOG_ORIGIN_HUMAN = 1; + LOG_ORIGIN_PROCESS = 2; +} + +enum LogSubtype { + LOG_SUBTYPE_NULL = 0; + LOG_SUBTYPE_RUN = 1; + LOG_SUBTYPE_ANNOUNCEMENT = 3; + LOG_SUBTYPE_INTERVENTION = 4; + LOG_SUBTYPE_COMMENT = 5; +} + +message MinimalRun { + int32 runNumber = 1; + int32 id = 2; +} + +message Tag { + // Unix timestamp when this entity was created. + int64 createdAt = 1; + int32 id = 2; + // The label value of the tag. + string text = 3; + // The email for the tag. + string email = 4; + // All the mattermost tags for updates + string mattermost = 5; + // Unix timestamp when this entity was last updated. + int64 updatedAt = 6; + // The last person that edited the email/mattermost fields + string lastEditedName = 7; + // The description of the tag + optional string description = 8; +} diff --git a/core/integration/bookkeeping/protos/log_grpc.pb.go b/core/integration/bookkeeping/protos/log_grpc.pb.go new file mode 100644 index 000000000..b4925ff0b --- /dev/null +++ b/core/integration/bookkeeping/protos/log_grpc.pb.go @@ -0,0 +1,107 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.3.0 +// - protoc v5.29.3 +// source: protos/log.proto + +package bkpb + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +const ( + LogService_Create_FullMethodName = "/o2.bookkeeping.LogService/Create" +) + +// LogServiceClient is the client API for LogService service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type LogServiceClient interface { + Create(ctx context.Context, in *LogCreationRequest, opts ...grpc.CallOption) (*Log, error) +} + +type logServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewLogServiceClient(cc grpc.ClientConnInterface) LogServiceClient { + return &logServiceClient{cc} +} + +func (c *logServiceClient) Create(ctx context.Context, in *LogCreationRequest, opts ...grpc.CallOption) (*Log, error) { + out := new(Log) + err := c.cc.Invoke(ctx, LogService_Create_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// LogServiceServer is the server API for LogService service. +// All implementations should embed UnimplementedLogServiceServer +// for forward compatibility +type LogServiceServer interface { + Create(context.Context, *LogCreationRequest) (*Log, error) +} + +// UnimplementedLogServiceServer should be embedded to have forward compatible implementations. +type UnimplementedLogServiceServer struct { +} + +func (UnimplementedLogServiceServer) Create(context.Context, *LogCreationRequest) (*Log, error) { + return nil, status.Errorf(codes.Unimplemented, "method Create not implemented") +} + +// UnsafeLogServiceServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to LogServiceServer will +// result in compilation errors. +type UnsafeLogServiceServer interface { + mustEmbedUnimplementedLogServiceServer() +} + +func RegisterLogServiceServer(s grpc.ServiceRegistrar, srv LogServiceServer) { + s.RegisterService(&LogService_ServiceDesc, srv) +} + +func _LogService_Create_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(LogCreationRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(LogServiceServer).Create(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: LogService_Create_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(LogServiceServer).Create(ctx, req.(*LogCreationRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// LogService_ServiceDesc is the grpc.ServiceDesc for LogService service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var LogService_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "o2.bookkeeping.LogService", + HandlerType: (*LogServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Create", + Handler: _LogService_Create_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "protos/log.proto", +} diff --git a/core/integration/bookkeeping/protos/run.pb.go b/core/integration/bookkeeping/protos/run.pb.go new file mode 100644 index 000000000..fb2431032 --- /dev/null +++ b/core/integration/bookkeeping/protos/run.pb.go @@ -0,0 +1,808 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.34.1 +// protoc v5.29.3 +// source: protos/run.proto + +package bkpb + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type RunRelations int32 + +const ( + // IGNORED - Here to comply with proto specification + RunRelations_RUN_RELATIONS_NULL RunRelations = 0 + // Include the LHC fill related to the run + RunRelations_RUN_RELATIONS_LHC_FILL RunRelations = 1 +) + +// Enum value maps for RunRelations. +var ( + RunRelations_name = map[int32]string{ + 0: "RUN_RELATIONS_NULL", + 1: "RUN_RELATIONS_LHC_FILL", + } + RunRelations_value = map[string]int32{ + "RUN_RELATIONS_NULL": 0, + "RUN_RELATIONS_LHC_FILL": 1, + } +) + +func (x RunRelations) Enum() *RunRelations { + p := new(RunRelations) + *p = x + return p +} + +func (x RunRelations) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (RunRelations) Descriptor() protoreflect.EnumDescriptor { + return file_protos_run_proto_enumTypes[0].Descriptor() +} + +func (RunRelations) Type() protoreflect.EnumType { + return &file_protos_run_proto_enumTypes[0] +} + +func (x RunRelations) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use RunRelations.Descriptor instead. +func (RunRelations) EnumDescriptor() ([]byte, []int) { + return file_protos_run_proto_rawDescGZIP(), []int{0} +} + +type RunFetchRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + RunNumber int32 `protobuf:"varint,1,opt,name=runNumber,proto3" json:"runNumber,omitempty"` + // Specify the list of relations to fetch alongside the run + // For example if this contains RUN_RELATIONS_LHC_FILL then the returned RunWithRelation will have a populated + // lhcFill, else it will not be defined + Relations []RunRelations `protobuf:"varint,2,rep,packed,name=relations,proto3,enum=o2.bookkeeping.RunRelations" json:"relations,omitempty"` +} + +func (x *RunFetchRequest) Reset() { + *x = RunFetchRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_run_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RunFetchRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RunFetchRequest) ProtoMessage() {} + +func (x *RunFetchRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_run_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RunFetchRequest.ProtoReflect.Descriptor instead. +func (*RunFetchRequest) Descriptor() ([]byte, []int) { + return file_protos_run_proto_rawDescGZIP(), []int{0} +} + +func (x *RunFetchRequest) GetRunNumber() int32 { + if x != nil { + return x.RunNumber + } + return 0 +} + +func (x *RunFetchRequest) GetRelations() []RunRelations { + if x != nil { + return x.Relations + } + return nil +} + +type RunWithRelations struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Run *Run `protobuf:"bytes,1,opt,name=run,proto3" json:"run,omitempty"` + LhcFill *LHCFill `protobuf:"bytes,2,opt,name=lhcFill,proto3,oneof" json:"lhcFill,omitempty"` +} + +func (x *RunWithRelations) Reset() { + *x = RunWithRelations{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_run_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RunWithRelations) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RunWithRelations) ProtoMessage() {} + +func (x *RunWithRelations) ProtoReflect() protoreflect.Message { + mi := &file_protos_run_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RunWithRelations.ProtoReflect.Descriptor instead. +func (*RunWithRelations) Descriptor() ([]byte, []int) { + return file_protos_run_proto_rawDescGZIP(), []int{1} +} + +func (x *RunWithRelations) GetRun() *Run { + if x != nil { + return x.Run + } + return nil +} + +func (x *RunWithRelations) GetLhcFill() *LHCFill { + if x != nil { + return x.LhcFill + } + return nil +} + +type RunCreationRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + EnvironmentId string `protobuf:"bytes,1,opt,name=environmentId,proto3" json:"environmentId,omitempty"` + NDetectors int32 `protobuf:"varint,2,opt,name=nDetectors,proto3" json:"nDetectors,omitempty"` + NEpns int32 `protobuf:"varint,3,opt,name=nEpns,proto3" json:"nEpns,omitempty"` + NFlps int32 `protobuf:"varint,4,opt,name=nFlps,proto3" json:"nFlps,omitempty"` + RunNumber int32 `protobuf:"varint,5,opt,name=runNumber,proto3" json:"runNumber,omitempty"` + RunType RunType `protobuf:"varint,6,opt,name=runType,proto3,enum=o2.bookkeeping.RunType" json:"runType,omitempty"` + DdFlp bool `protobuf:"varint,7,opt,name=dd_flp,json=ddFlp,proto3" json:"dd_flp,omitempty"` + Dcs bool `protobuf:"varint,8,opt,name=dcs,proto3" json:"dcs,omitempty"` + Epn bool `protobuf:"varint,9,opt,name=epn,proto3" json:"epn,omitempty"` + EpnTopology string `protobuf:"bytes,10,opt,name=epnTopology,proto3" json:"epnTopology,omitempty"` + OdcTopologyFullName *string `protobuf:"bytes,11,opt,name=odcTopologyFullName,proto3,oneof" json:"odcTopologyFullName,omitempty"` + Detectors []Detector `protobuf:"varint,12,rep,packed,name=detectors,proto3,enum=o2.bookkeeping.Detector" json:"detectors,omitempty"` + // Optional users that stopped or started the run (can also be undefined) + UserO2Start *User `protobuf:"bytes,13,opt,name=userO2Start,proto3,oneof" json:"userO2Start,omitempty"` + UserO2Stop *User `protobuf:"bytes,14,opt,name=userO2Stop,proto3,oneof" json:"userO2Stop,omitempty"` +} + +func (x *RunCreationRequest) Reset() { + *x = RunCreationRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_run_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RunCreationRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RunCreationRequest) ProtoMessage() {} + +func (x *RunCreationRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_run_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RunCreationRequest.ProtoReflect.Descriptor instead. +func (*RunCreationRequest) Descriptor() ([]byte, []int) { + return file_protos_run_proto_rawDescGZIP(), []int{2} +} + +func (x *RunCreationRequest) GetEnvironmentId() string { + if x != nil { + return x.EnvironmentId + } + return "" +} + +func (x *RunCreationRequest) GetNDetectors() int32 { + if x != nil { + return x.NDetectors + } + return 0 +} + +func (x *RunCreationRequest) GetNEpns() int32 { + if x != nil { + return x.NEpns + } + return 0 +} + +func (x *RunCreationRequest) GetNFlps() int32 { + if x != nil { + return x.NFlps + } + return 0 +} + +func (x *RunCreationRequest) GetRunNumber() int32 { + if x != nil { + return x.RunNumber + } + return 0 +} + +func (x *RunCreationRequest) GetRunType() RunType { + if x != nil { + return x.RunType + } + return RunType_RUN_TYPE_NULL +} + +func (x *RunCreationRequest) GetDdFlp() bool { + if x != nil { + return x.DdFlp + } + return false +} + +func (x *RunCreationRequest) GetDcs() bool { + if x != nil { + return x.Dcs + } + return false +} + +func (x *RunCreationRequest) GetEpn() bool { + if x != nil { + return x.Epn + } + return false +} + +func (x *RunCreationRequest) GetEpnTopology() string { + if x != nil { + return x.EpnTopology + } + return "" +} + +func (x *RunCreationRequest) GetOdcTopologyFullName() string { + if x != nil && x.OdcTopologyFullName != nil { + return *x.OdcTopologyFullName + } + return "" +} + +func (x *RunCreationRequest) GetDetectors() []Detector { + if x != nil { + return x.Detectors + } + return nil +} + +func (x *RunCreationRequest) GetUserO2Start() *User { + if x != nil { + return x.UserO2Start + } + return nil +} + +func (x *RunCreationRequest) GetUserO2Stop() *User { + if x != nil { + return x.UserO2Stop + } + return nil +} + +type RunUpdateRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + RunNumber int32 `protobuf:"varint,1,opt,name=runNumber,proto3" json:"runNumber,omitempty"` + TimeO2Start *int64 `protobuf:"varint,3,opt,name=timeO2Start,proto3,oneof" json:"timeO2Start,omitempty"` + TimeO2End *int64 `protobuf:"varint,4,opt,name=timeO2End,proto3,oneof" json:"timeO2End,omitempty"` + TimeTrgStart *int64 `protobuf:"varint,5,opt,name=timeTrgStart,proto3,oneof" json:"timeTrgStart,omitempty"` + TimeTrgEnd *int64 `protobuf:"varint,6,opt,name=timeTrgEnd,proto3,oneof" json:"timeTrgEnd,omitempty"` + TriggerValue *string `protobuf:"bytes,7,opt,name=triggerValue,proto3,oneof" json:"triggerValue,omitempty"` + RawCtpTriggerConfiguration *string `protobuf:"bytes,19,opt,name=rawCtpTriggerConfiguration,proto3,oneof" json:"rawCtpTriggerConfiguration,omitempty"` + PdpConfigOption *string `protobuf:"bytes,8,opt,name=pdpConfigOption,proto3,oneof" json:"pdpConfigOption,omitempty"` + PdpTopologyDescriptionLibraryFile *string `protobuf:"bytes,9,opt,name=pdpTopologyDescriptionLibraryFile,proto3,oneof" json:"pdpTopologyDescriptionLibraryFile,omitempty"` + TfbDdMode *string `protobuf:"bytes,10,opt,name=tfbDdMode,proto3,oneof" json:"tfbDdMode,omitempty"` + LhcPeriod *string `protobuf:"bytes,11,opt,name=lhcPeriod,proto3,oneof" json:"lhcPeriod,omitempty"` + OdcTopologyFullName *string `protobuf:"bytes,12,opt,name=odcTopologyFullName,proto3,oneof" json:"odcTopologyFullName,omitempty"` + PdpWorkflowParameters *string `protobuf:"bytes,13,opt,name=pdpWorkflowParameters,proto3,oneof" json:"pdpWorkflowParameters,omitempty"` + PdpBeamType *string `protobuf:"bytes,14,opt,name=pdpBeamType,proto3,oneof" json:"pdpBeamType,omitempty"` + ReadoutCfgUri *string `protobuf:"bytes,15,opt,name=readoutCfgUri,proto3,oneof" json:"readoutCfgUri,omitempty"` + EorReasons []*EorReason `protobuf:"bytes,16,rep,name=eorReasons,proto3" json:"eorReasons,omitempty"` + // Optional users that stopped or started the run (can also be undefined) + UserO2Start *User `protobuf:"bytes,17,opt,name=userO2Start,proto3,oneof" json:"userO2Start,omitempty"` + UserO2Stop *User `protobuf:"bytes,18,opt,name=userO2Stop,proto3,oneof" json:"userO2Stop,omitempty"` // 19 is trigger raw configuration +} + +func (x *RunUpdateRequest) Reset() { + *x = RunUpdateRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_run_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RunUpdateRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RunUpdateRequest) ProtoMessage() {} + +func (x *RunUpdateRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_run_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RunUpdateRequest.ProtoReflect.Descriptor instead. +func (*RunUpdateRequest) Descriptor() ([]byte, []int) { + return file_protos_run_proto_rawDescGZIP(), []int{3} +} + +func (x *RunUpdateRequest) GetRunNumber() int32 { + if x != nil { + return x.RunNumber + } + return 0 +} + +func (x *RunUpdateRequest) GetTimeO2Start() int64 { + if x != nil && x.TimeO2Start != nil { + return *x.TimeO2Start + } + return 0 +} + +func (x *RunUpdateRequest) GetTimeO2End() int64 { + if x != nil && x.TimeO2End != nil { + return *x.TimeO2End + } + return 0 +} + +func (x *RunUpdateRequest) GetTimeTrgStart() int64 { + if x != nil && x.TimeTrgStart != nil { + return *x.TimeTrgStart + } + return 0 +} + +func (x *RunUpdateRequest) GetTimeTrgEnd() int64 { + if x != nil && x.TimeTrgEnd != nil { + return *x.TimeTrgEnd + } + return 0 +} + +func (x *RunUpdateRequest) GetTriggerValue() string { + if x != nil && x.TriggerValue != nil { + return *x.TriggerValue + } + return "" +} + +func (x *RunUpdateRequest) GetRawCtpTriggerConfiguration() string { + if x != nil && x.RawCtpTriggerConfiguration != nil { + return *x.RawCtpTriggerConfiguration + } + return "" +} + +func (x *RunUpdateRequest) GetPdpConfigOption() string { + if x != nil && x.PdpConfigOption != nil { + return *x.PdpConfigOption + } + return "" +} + +func (x *RunUpdateRequest) GetPdpTopologyDescriptionLibraryFile() string { + if x != nil && x.PdpTopologyDescriptionLibraryFile != nil { + return *x.PdpTopologyDescriptionLibraryFile + } + return "" +} + +func (x *RunUpdateRequest) GetTfbDdMode() string { + if x != nil && x.TfbDdMode != nil { + return *x.TfbDdMode + } + return "" +} + +func (x *RunUpdateRequest) GetLhcPeriod() string { + if x != nil && x.LhcPeriod != nil { + return *x.LhcPeriod + } + return "" +} + +func (x *RunUpdateRequest) GetOdcTopologyFullName() string { + if x != nil && x.OdcTopologyFullName != nil { + return *x.OdcTopologyFullName + } + return "" +} + +func (x *RunUpdateRequest) GetPdpWorkflowParameters() string { + if x != nil && x.PdpWorkflowParameters != nil { + return *x.PdpWorkflowParameters + } + return "" +} + +func (x *RunUpdateRequest) GetPdpBeamType() string { + if x != nil && x.PdpBeamType != nil { + return *x.PdpBeamType + } + return "" +} + +func (x *RunUpdateRequest) GetReadoutCfgUri() string { + if x != nil && x.ReadoutCfgUri != nil { + return *x.ReadoutCfgUri + } + return "" +} + +func (x *RunUpdateRequest) GetEorReasons() []*EorReason { + if x != nil { + return x.EorReasons + } + return nil +} + +func (x *RunUpdateRequest) GetUserO2Start() *User { + if x != nil { + return x.UserO2Start + } + return nil +} + +func (x *RunUpdateRequest) GetUserO2Stop() *User { + if x != nil { + return x.UserO2Stop + } + return nil +} + +var File_protos_run_proto protoreflect.FileDescriptor + +var file_protos_run_proto_rawDesc = []byte{ + 0x0a, 0x10, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x72, 0x75, 0x6e, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x12, 0x0e, 0x6f, 0x32, 0x2e, 0x62, 0x6f, 0x6f, 0x6b, 0x6b, 0x65, 0x65, 0x70, 0x69, + 0x6e, 0x67, 0x1a, 0x15, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x62, 0x6b, 0x63, 0x6f, 0x6d, + 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x6b, 0x0a, 0x0f, 0x52, 0x75, 0x6e, + 0x46, 0x65, 0x74, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, + 0x72, 0x75, 0x6e, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, + 0x09, 0x72, 0x75, 0x6e, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x3a, 0x0a, 0x09, 0x72, 0x65, + 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x1c, 0x2e, + 0x6f, 0x32, 0x2e, 0x62, 0x6f, 0x6f, 0x6b, 0x6b, 0x65, 0x65, 0x70, 0x69, 0x6e, 0x67, 0x2e, 0x52, + 0x75, 0x6e, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x09, 0x72, 0x65, 0x6c, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x7d, 0x0a, 0x10, 0x52, 0x75, 0x6e, 0x57, 0x69, 0x74, + 0x68, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x25, 0x0a, 0x03, 0x72, 0x75, + 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6f, 0x32, 0x2e, 0x62, 0x6f, 0x6f, + 0x6b, 0x6b, 0x65, 0x65, 0x70, 0x69, 0x6e, 0x67, 0x2e, 0x52, 0x75, 0x6e, 0x52, 0x03, 0x72, 0x75, + 0x6e, 0x12, 0x36, 0x0a, 0x07, 0x6c, 0x68, 0x63, 0x46, 0x69, 0x6c, 0x6c, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6f, 0x32, 0x2e, 0x62, 0x6f, 0x6f, 0x6b, 0x6b, 0x65, 0x65, 0x70, + 0x69, 0x6e, 0x67, 0x2e, 0x4c, 0x48, 0x43, 0x46, 0x69, 0x6c, 0x6c, 0x48, 0x00, 0x52, 0x07, 0x6c, + 0x68, 0x63, 0x46, 0x69, 0x6c, 0x6c, 0x88, 0x01, 0x01, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x6c, 0x68, + 0x63, 0x46, 0x69, 0x6c, 0x6c, 0x22, 0xd2, 0x04, 0x0a, 0x12, 0x52, 0x75, 0x6e, 0x43, 0x72, 0x65, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x24, 0x0a, 0x0d, + 0x65, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0d, 0x65, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, + 0x49, 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x6e, 0x44, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x6e, 0x44, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, + 0x72, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x6e, 0x45, 0x70, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x05, 0x6e, 0x45, 0x70, 0x6e, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x6e, 0x46, 0x6c, 0x70, + 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x6e, 0x46, 0x6c, 0x70, 0x73, 0x12, 0x1c, + 0x0a, 0x09, 0x72, 0x75, 0x6e, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x09, 0x72, 0x75, 0x6e, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x31, 0x0a, 0x07, + 0x72, 0x75, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x17, 0x2e, + 0x6f, 0x32, 0x2e, 0x62, 0x6f, 0x6f, 0x6b, 0x6b, 0x65, 0x65, 0x70, 0x69, 0x6e, 0x67, 0x2e, 0x52, + 0x75, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x07, 0x72, 0x75, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, + 0x15, 0x0a, 0x06, 0x64, 0x64, 0x5f, 0x66, 0x6c, 0x70, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x05, 0x64, 0x64, 0x46, 0x6c, 0x70, 0x12, 0x10, 0x0a, 0x03, 0x64, 0x63, 0x73, 0x18, 0x08, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x03, 0x64, 0x63, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x70, 0x6e, 0x18, + 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x03, 0x65, 0x70, 0x6e, 0x12, 0x20, 0x0a, 0x0b, 0x65, 0x70, + 0x6e, 0x54, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0b, 0x65, 0x70, 0x6e, 0x54, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x12, 0x35, 0x0a, 0x13, + 0x6f, 0x64, 0x63, 0x54, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x46, 0x75, 0x6c, 0x6c, 0x4e, + 0x61, 0x6d, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x13, 0x6f, 0x64, 0x63, + 0x54, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x46, 0x75, 0x6c, 0x6c, 0x4e, 0x61, 0x6d, 0x65, + 0x88, 0x01, 0x01, 0x12, 0x36, 0x0a, 0x09, 0x64, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, + 0x18, 0x0c, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x6f, 0x32, 0x2e, 0x62, 0x6f, 0x6f, 0x6b, + 0x6b, 0x65, 0x65, 0x70, 0x69, 0x6e, 0x67, 0x2e, 0x44, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, + 0x52, 0x09, 0x64, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x12, 0x3b, 0x0a, 0x0b, 0x75, + 0x73, 0x65, 0x72, 0x4f, 0x32, 0x53, 0x74, 0x61, 0x72, 0x74, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x14, 0x2e, 0x6f, 0x32, 0x2e, 0x62, 0x6f, 0x6f, 0x6b, 0x6b, 0x65, 0x65, 0x70, 0x69, 0x6e, + 0x67, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x48, 0x01, 0x52, 0x0b, 0x75, 0x73, 0x65, 0x72, 0x4f, 0x32, + 0x53, 0x74, 0x61, 0x72, 0x74, 0x88, 0x01, 0x01, 0x12, 0x39, 0x0a, 0x0a, 0x75, 0x73, 0x65, 0x72, + 0x4f, 0x32, 0x53, 0x74, 0x6f, 0x70, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6f, + 0x32, 0x2e, 0x62, 0x6f, 0x6f, 0x6b, 0x6b, 0x65, 0x65, 0x70, 0x69, 0x6e, 0x67, 0x2e, 0x55, 0x73, + 0x65, 0x72, 0x48, 0x02, 0x52, 0x0a, 0x75, 0x73, 0x65, 0x72, 0x4f, 0x32, 0x53, 0x74, 0x6f, 0x70, + 0x88, 0x01, 0x01, 0x42, 0x16, 0x0a, 0x14, 0x5f, 0x6f, 0x64, 0x63, 0x54, 0x6f, 0x70, 0x6f, 0x6c, + 0x6f, 0x67, 0x79, 0x46, 0x75, 0x6c, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, + 0x75, 0x73, 0x65, 0x72, 0x4f, 0x32, 0x53, 0x74, 0x61, 0x72, 0x74, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, + 0x75, 0x73, 0x65, 0x72, 0x4f, 0x32, 0x53, 0x74, 0x6f, 0x70, 0x22, 0xac, 0x09, 0x0a, 0x10, 0x52, + 0x75, 0x6e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x1c, 0x0a, 0x09, 0x72, 0x75, 0x6e, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x05, 0x52, 0x09, 0x72, 0x75, 0x6e, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x25, 0x0a, + 0x0b, 0x74, 0x69, 0x6d, 0x65, 0x4f, 0x32, 0x53, 0x74, 0x61, 0x72, 0x74, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x03, 0x48, 0x00, 0x52, 0x0b, 0x74, 0x69, 0x6d, 0x65, 0x4f, 0x32, 0x53, 0x74, 0x61, 0x72, + 0x74, 0x88, 0x01, 0x01, 0x12, 0x21, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x4f, 0x32, 0x45, 0x6e, + 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x48, 0x01, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x4f, + 0x32, 0x45, 0x6e, 0x64, 0x88, 0x01, 0x01, 0x12, 0x27, 0x0a, 0x0c, 0x74, 0x69, 0x6d, 0x65, 0x54, + 0x72, 0x67, 0x53, 0x74, 0x61, 0x72, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x48, 0x02, 0x52, + 0x0c, 0x74, 0x69, 0x6d, 0x65, 0x54, 0x72, 0x67, 0x53, 0x74, 0x61, 0x72, 0x74, 0x88, 0x01, 0x01, + 0x12, 0x23, 0x0a, 0x0a, 0x74, 0x69, 0x6d, 0x65, 0x54, 0x72, 0x67, 0x45, 0x6e, 0x64, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x03, 0x48, 0x03, 0x52, 0x0a, 0x74, 0x69, 0x6d, 0x65, 0x54, 0x72, 0x67, 0x45, + 0x6e, 0x64, 0x88, 0x01, 0x01, 0x12, 0x27, 0x0a, 0x0c, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, + 0x56, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x48, 0x04, 0x52, 0x0c, 0x74, + 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x88, 0x01, 0x01, 0x12, 0x43, + 0x0a, 0x1a, 0x72, 0x61, 0x77, 0x43, 0x74, 0x70, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x13, 0x20, 0x01, + 0x28, 0x09, 0x48, 0x05, 0x52, 0x1a, 0x72, 0x61, 0x77, 0x43, 0x74, 0x70, 0x54, 0x72, 0x69, 0x67, + 0x67, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x88, 0x01, 0x01, 0x12, 0x2d, 0x0a, 0x0f, 0x70, 0x64, 0x70, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x48, 0x06, 0x52, 0x0f, + 0x70, 0x64, 0x70, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x88, + 0x01, 0x01, 0x12, 0x51, 0x0a, 0x21, 0x70, 0x64, 0x70, 0x54, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, + 0x79, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x62, 0x72, + 0x61, 0x72, 0x79, 0x46, 0x69, 0x6c, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x48, 0x07, 0x52, + 0x21, 0x70, 0x64, 0x70, 0x54, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x44, 0x65, 0x73, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x46, 0x69, + 0x6c, 0x65, 0x88, 0x01, 0x01, 0x12, 0x21, 0x0a, 0x09, 0x74, 0x66, 0x62, 0x44, 0x64, 0x4d, 0x6f, + 0x64, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x48, 0x08, 0x52, 0x09, 0x74, 0x66, 0x62, 0x44, + 0x64, 0x4d, 0x6f, 0x64, 0x65, 0x88, 0x01, 0x01, 0x12, 0x21, 0x0a, 0x09, 0x6c, 0x68, 0x63, 0x50, + 0x65, 0x72, 0x69, 0x6f, 0x64, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x48, 0x09, 0x52, 0x09, 0x6c, + 0x68, 0x63, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x88, 0x01, 0x01, 0x12, 0x35, 0x0a, 0x13, 0x6f, + 0x64, 0x63, 0x54, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x46, 0x75, 0x6c, 0x6c, 0x4e, 0x61, + 0x6d, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x48, 0x0a, 0x52, 0x13, 0x6f, 0x64, 0x63, 0x54, + 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x46, 0x75, 0x6c, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x88, + 0x01, 0x01, 0x12, 0x39, 0x0a, 0x15, 0x70, 0x64, 0x70, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, + 0x77, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x18, 0x0d, 0x20, 0x01, 0x28, + 0x09, 0x48, 0x0b, 0x52, 0x15, 0x70, 0x64, 0x70, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, + 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x88, 0x01, 0x01, 0x12, 0x25, 0x0a, + 0x0b, 0x70, 0x64, 0x70, 0x42, 0x65, 0x61, 0x6d, 0x54, 0x79, 0x70, 0x65, 0x18, 0x0e, 0x20, 0x01, + 0x28, 0x09, 0x48, 0x0c, 0x52, 0x0b, 0x70, 0x64, 0x70, 0x42, 0x65, 0x61, 0x6d, 0x54, 0x79, 0x70, + 0x65, 0x88, 0x01, 0x01, 0x12, 0x29, 0x0a, 0x0d, 0x72, 0x65, 0x61, 0x64, 0x6f, 0x75, 0x74, 0x43, + 0x66, 0x67, 0x55, 0x72, 0x69, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x09, 0x48, 0x0d, 0x52, 0x0d, 0x72, + 0x65, 0x61, 0x64, 0x6f, 0x75, 0x74, 0x43, 0x66, 0x67, 0x55, 0x72, 0x69, 0x88, 0x01, 0x01, 0x12, + 0x39, 0x0a, 0x0a, 0x65, 0x6f, 0x72, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x73, 0x18, 0x10, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6f, 0x32, 0x2e, 0x62, 0x6f, 0x6f, 0x6b, 0x6b, 0x65, 0x65, + 0x70, 0x69, 0x6e, 0x67, 0x2e, 0x45, 0x6f, 0x72, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x52, 0x0a, + 0x65, 0x6f, 0x72, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x73, 0x12, 0x3b, 0x0a, 0x0b, 0x75, 0x73, + 0x65, 0x72, 0x4f, 0x32, 0x53, 0x74, 0x61, 0x72, 0x74, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x14, 0x2e, 0x6f, 0x32, 0x2e, 0x62, 0x6f, 0x6f, 0x6b, 0x6b, 0x65, 0x65, 0x70, 0x69, 0x6e, 0x67, + 0x2e, 0x55, 0x73, 0x65, 0x72, 0x48, 0x0e, 0x52, 0x0b, 0x75, 0x73, 0x65, 0x72, 0x4f, 0x32, 0x53, + 0x74, 0x61, 0x72, 0x74, 0x88, 0x01, 0x01, 0x12, 0x39, 0x0a, 0x0a, 0x75, 0x73, 0x65, 0x72, 0x4f, + 0x32, 0x53, 0x74, 0x6f, 0x70, 0x18, 0x12, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6f, 0x32, + 0x2e, 0x62, 0x6f, 0x6f, 0x6b, 0x6b, 0x65, 0x65, 0x70, 0x69, 0x6e, 0x67, 0x2e, 0x55, 0x73, 0x65, + 0x72, 0x48, 0x0f, 0x52, 0x0a, 0x75, 0x73, 0x65, 0x72, 0x4f, 0x32, 0x53, 0x74, 0x6f, 0x70, 0x88, + 0x01, 0x01, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x4f, 0x32, 0x53, 0x74, 0x61, + 0x72, 0x74, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x4f, 0x32, 0x45, 0x6e, 0x64, + 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x54, 0x72, 0x67, 0x53, 0x74, 0x61, 0x72, + 0x74, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x54, 0x72, 0x67, 0x45, 0x6e, 0x64, + 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x56, 0x61, 0x6c, 0x75, + 0x65, 0x42, 0x1d, 0x0a, 0x1b, 0x5f, 0x72, 0x61, 0x77, 0x43, 0x74, 0x70, 0x54, 0x72, 0x69, 0x67, + 0x67, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x42, 0x12, 0x0a, 0x10, 0x5f, 0x70, 0x64, 0x70, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x4f, 0x70, + 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x24, 0x0a, 0x22, 0x5f, 0x70, 0x64, 0x70, 0x54, 0x6f, 0x70, 0x6f, + 0x6c, 0x6f, 0x67, 0x79, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x4c, + 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x46, 0x69, 0x6c, 0x65, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x74, + 0x66, 0x62, 0x44, 0x64, 0x4d, 0x6f, 0x64, 0x65, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x6c, 0x68, 0x63, + 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x42, 0x16, 0x0a, 0x14, 0x5f, 0x6f, 0x64, 0x63, 0x54, 0x6f, + 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x46, 0x75, 0x6c, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x42, 0x18, + 0x0a, 0x16, 0x5f, 0x70, 0x64, 0x70, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x50, 0x61, + 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x70, 0x64, 0x70, + 0x42, 0x65, 0x61, 0x6d, 0x54, 0x79, 0x70, 0x65, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x72, 0x65, 0x61, + 0x64, 0x6f, 0x75, 0x74, 0x43, 0x66, 0x67, 0x55, 0x72, 0x69, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x75, + 0x73, 0x65, 0x72, 0x4f, 0x32, 0x53, 0x74, 0x61, 0x72, 0x74, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x75, + 0x73, 0x65, 0x72, 0x4f, 0x32, 0x53, 0x74, 0x6f, 0x70, 0x2a, 0x42, 0x0a, 0x0c, 0x52, 0x75, 0x6e, + 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x16, 0x0a, 0x12, 0x52, 0x55, 0x4e, + 0x5f, 0x52, 0x45, 0x4c, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x53, 0x5f, 0x4e, 0x55, 0x4c, 0x4c, 0x10, + 0x00, 0x12, 0x1a, 0x0a, 0x16, 0x52, 0x55, 0x4e, 0x5f, 0x52, 0x45, 0x4c, 0x41, 0x54, 0x49, 0x4f, + 0x4e, 0x53, 0x5f, 0x4c, 0x48, 0x43, 0x5f, 0x46, 0x49, 0x4c, 0x4c, 0x10, 0x01, 0x32, 0xda, 0x01, + 0x0a, 0x0a, 0x52, 0x75, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x48, 0x0a, 0x03, + 0x47, 0x65, 0x74, 0x12, 0x1f, 0x2e, 0x6f, 0x32, 0x2e, 0x62, 0x6f, 0x6f, 0x6b, 0x6b, 0x65, 0x65, + 0x70, 0x69, 0x6e, 0x67, 0x2e, 0x52, 0x75, 0x6e, 0x46, 0x65, 0x74, 0x63, 0x68, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6f, 0x32, 0x2e, 0x62, 0x6f, 0x6f, 0x6b, 0x6b, 0x65, + 0x65, 0x70, 0x69, 0x6e, 0x67, 0x2e, 0x52, 0x75, 0x6e, 0x57, 0x69, 0x74, 0x68, 0x52, 0x65, 0x6c, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x41, 0x0a, 0x06, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, + 0x12, 0x22, 0x2e, 0x6f, 0x32, 0x2e, 0x62, 0x6f, 0x6f, 0x6b, 0x6b, 0x65, 0x65, 0x70, 0x69, 0x6e, + 0x67, 0x2e, 0x52, 0x75, 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x6f, 0x32, 0x2e, 0x62, 0x6f, 0x6f, 0x6b, 0x6b, 0x65, + 0x65, 0x70, 0x69, 0x6e, 0x67, 0x2e, 0x52, 0x75, 0x6e, 0x12, 0x3f, 0x0a, 0x06, 0x55, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x12, 0x20, 0x2e, 0x6f, 0x32, 0x2e, 0x62, 0x6f, 0x6f, 0x6b, 0x6b, 0x65, 0x65, + 0x70, 0x69, 0x6e, 0x67, 0x2e, 0x52, 0x75, 0x6e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x6f, 0x32, 0x2e, 0x62, 0x6f, 0x6f, 0x6b, 0x6b, + 0x65, 0x65, 0x70, 0x69, 0x6e, 0x67, 0x2e, 0x52, 0x75, 0x6e, 0x42, 0x4a, 0x5a, 0x48, 0x67, 0x69, + 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x41, 0x6c, 0x69, 0x63, 0x65, 0x4f, 0x32, + 0x47, 0x72, 0x6f, 0x75, 0x70, 0x2f, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2f, 0x63, 0x6f, + 0x72, 0x65, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x62, + 0x6f, 0x6f, 0x6b, 0x6b, 0x65, 0x65, 0x70, 0x69, 0x6e, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x73, 0x3b, 0x62, 0x6b, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_protos_run_proto_rawDescOnce sync.Once + file_protos_run_proto_rawDescData = file_protos_run_proto_rawDesc +) + +func file_protos_run_proto_rawDescGZIP() []byte { + file_protos_run_proto_rawDescOnce.Do(func() { + file_protos_run_proto_rawDescData = protoimpl.X.CompressGZIP(file_protos_run_proto_rawDescData) + }) + return file_protos_run_proto_rawDescData +} + +var file_protos_run_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_protos_run_proto_msgTypes = make([]protoimpl.MessageInfo, 4) +var file_protos_run_proto_goTypes = []interface{}{ + (RunRelations)(0), // 0: o2.bookkeeping.RunRelations + (*RunFetchRequest)(nil), // 1: o2.bookkeeping.RunFetchRequest + (*RunWithRelations)(nil), // 2: o2.bookkeeping.RunWithRelations + (*RunCreationRequest)(nil), // 3: o2.bookkeeping.RunCreationRequest + (*RunUpdateRequest)(nil), // 4: o2.bookkeeping.RunUpdateRequest + (*Run)(nil), // 5: o2.bookkeeping.Run + (*LHCFill)(nil), // 6: o2.bookkeeping.LHCFill + (RunType)(0), // 7: o2.bookkeeping.RunType + (Detector)(0), // 8: o2.bookkeeping.Detector + (*User)(nil), // 9: o2.bookkeeping.User + (*EorReason)(nil), // 10: o2.bookkeeping.EorReason +} +var file_protos_run_proto_depIdxs = []int32{ + 0, // 0: o2.bookkeeping.RunFetchRequest.relations:type_name -> o2.bookkeeping.RunRelations + 5, // 1: o2.bookkeeping.RunWithRelations.run:type_name -> o2.bookkeeping.Run + 6, // 2: o2.bookkeeping.RunWithRelations.lhcFill:type_name -> o2.bookkeeping.LHCFill + 7, // 3: o2.bookkeeping.RunCreationRequest.runType:type_name -> o2.bookkeeping.RunType + 8, // 4: o2.bookkeeping.RunCreationRequest.detectors:type_name -> o2.bookkeeping.Detector + 9, // 5: o2.bookkeeping.RunCreationRequest.userO2Start:type_name -> o2.bookkeeping.User + 9, // 6: o2.bookkeeping.RunCreationRequest.userO2Stop:type_name -> o2.bookkeeping.User + 10, // 7: o2.bookkeeping.RunUpdateRequest.eorReasons:type_name -> o2.bookkeeping.EorReason + 9, // 8: o2.bookkeeping.RunUpdateRequest.userO2Start:type_name -> o2.bookkeeping.User + 9, // 9: o2.bookkeeping.RunUpdateRequest.userO2Stop:type_name -> o2.bookkeeping.User + 1, // 10: o2.bookkeeping.RunService.Get:input_type -> o2.bookkeeping.RunFetchRequest + 3, // 11: o2.bookkeeping.RunService.Create:input_type -> o2.bookkeeping.RunCreationRequest + 4, // 12: o2.bookkeeping.RunService.Update:input_type -> o2.bookkeeping.RunUpdateRequest + 2, // 13: o2.bookkeeping.RunService.Get:output_type -> o2.bookkeeping.RunWithRelations + 5, // 14: o2.bookkeeping.RunService.Create:output_type -> o2.bookkeeping.Run + 5, // 15: o2.bookkeeping.RunService.Update:output_type -> o2.bookkeeping.Run + 13, // [13:16] is the sub-list for method output_type + 10, // [10:13] is the sub-list for method input_type + 10, // [10:10] is the sub-list for extension type_name + 10, // [10:10] is the sub-list for extension extendee + 0, // [0:10] is the sub-list for field type_name +} + +func init() { file_protos_run_proto_init() } +func file_protos_run_proto_init() { + if File_protos_run_proto != nil { + return + } + file_protos_bkcommon_proto_init() + if !protoimpl.UnsafeEnabled { + file_protos_run_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RunFetchRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_run_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RunWithRelations); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_run_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RunCreationRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_run_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RunUpdateRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + file_protos_run_proto_msgTypes[1].OneofWrappers = []interface{}{} + file_protos_run_proto_msgTypes[2].OneofWrappers = []interface{}{} + file_protos_run_proto_msgTypes[3].OneofWrappers = []interface{}{} + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_protos_run_proto_rawDesc, + NumEnums: 1, + NumMessages: 4, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_protos_run_proto_goTypes, + DependencyIndexes: file_protos_run_proto_depIdxs, + EnumInfos: file_protos_run_proto_enumTypes, + MessageInfos: file_protos_run_proto_msgTypes, + }.Build() + File_protos_run_proto = out.File + file_protos_run_proto_rawDesc = nil + file_protos_run_proto_goTypes = nil + file_protos_run_proto_depIdxs = nil +} diff --git a/core/integration/bookkeeping/protos/run.proto b/core/integration/bookkeeping/protos/run.proto new file mode 100644 index 000000000..f9cdc79b7 --- /dev/null +++ b/core/integration/bookkeeping/protos/run.proto @@ -0,0 +1,79 @@ +syntax = 'proto3'; + +package o2.bookkeeping; +option go_package = "github.com/AliceO2Group/Control/core/integration/bookkeeping/protos;bkpb"; + +import "protos/bkcommon.proto"; + +// Services + +service RunService { + rpc Get(RunFetchRequest) returns (RunWithRelations); + rpc Create(RunCreationRequest) returns (Run); + rpc Update(RunUpdateRequest) returns (Run); +} + +// High level messages + +message RunFetchRequest { + int32 runNumber = 1; + // Specify the list of relations to fetch alongside the run + // For example if this contains RUN_RELATIONS_LHC_FILL then the returned RunWithRelation will have a populated + // lhcFill, else it will not be defined + repeated RunRelations relations = 2; +} + +message RunWithRelations { + Run run = 1; + optional LHCFill lhcFill = 2; +} + +message RunCreationRequest { + string environmentId = 1; + int32 nDetectors = 2; + int32 nEpns = 3; + int32 nFlps = 4; + int32 runNumber = 5; + RunType runType = 6; + bool dd_flp = 7; + bool dcs = 8; + bool epn = 9; + string epnTopology = 10; + optional string odcTopologyFullName = 11; + repeated Detector detectors = 12; + // Optional users that stopped or started the run (can also be undefined) + optional User userO2Start = 13; + optional User userO2Stop = 14; +} + +message RunUpdateRequest { + int32 runNumber = 1; + optional int64 timeO2Start = 3; + optional int64 timeO2End = 4; + optional int64 timeTrgStart = 5; + optional int64 timeTrgEnd = 6; + optional string triggerValue = 7; + optional string rawCtpTriggerConfiguration = 19; + optional string pdpConfigOption = 8; + optional string pdpTopologyDescriptionLibraryFile = 9; + optional string tfbDdMode = 10; + optional string lhcPeriod = 11; + optional string odcTopologyFullName = 12; + optional string pdpWorkflowParameters = 13; + optional string pdpBeamType = 14; + optional string readoutCfgUri = 15; + repeated EorReason eorReasons = 16; + // Optional users that stopped or started the run (can also be undefined) + optional User userO2Start = 17; + optional User userO2Stop = 18; + // 19 is trigger raw configuration +} + +// Enums + +enum RunRelations { + // IGNORED - Here to comply with proto specification + RUN_RELATIONS_NULL = 0; + // Include the LHC fill related to the run + RUN_RELATIONS_LHC_FILL = 1; +} diff --git a/core/integration/bookkeeping/protos/run_grpc.pb.go b/core/integration/bookkeeping/protos/run_grpc.pb.go new file mode 100644 index 000000000..39cff60d7 --- /dev/null +++ b/core/integration/bookkeeping/protos/run_grpc.pb.go @@ -0,0 +1,181 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.3.0 +// - protoc v5.29.3 +// source: protos/run.proto + +package bkpb + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +const ( + RunService_Get_FullMethodName = "/o2.bookkeeping.RunService/Get" + RunService_Create_FullMethodName = "/o2.bookkeeping.RunService/Create" + RunService_Update_FullMethodName = "/o2.bookkeeping.RunService/Update" +) + +// RunServiceClient is the client API for RunService service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type RunServiceClient interface { + Get(ctx context.Context, in *RunFetchRequest, opts ...grpc.CallOption) (*RunWithRelations, error) + Create(ctx context.Context, in *RunCreationRequest, opts ...grpc.CallOption) (*Run, error) + Update(ctx context.Context, in *RunUpdateRequest, opts ...grpc.CallOption) (*Run, error) +} + +type runServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewRunServiceClient(cc grpc.ClientConnInterface) RunServiceClient { + return &runServiceClient{cc} +} + +func (c *runServiceClient) Get(ctx context.Context, in *RunFetchRequest, opts ...grpc.CallOption) (*RunWithRelations, error) { + out := new(RunWithRelations) + err := c.cc.Invoke(ctx, RunService_Get_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *runServiceClient) Create(ctx context.Context, in *RunCreationRequest, opts ...grpc.CallOption) (*Run, error) { + out := new(Run) + err := c.cc.Invoke(ctx, RunService_Create_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *runServiceClient) Update(ctx context.Context, in *RunUpdateRequest, opts ...grpc.CallOption) (*Run, error) { + out := new(Run) + err := c.cc.Invoke(ctx, RunService_Update_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// RunServiceServer is the server API for RunService service. +// All implementations should embed UnimplementedRunServiceServer +// for forward compatibility +type RunServiceServer interface { + Get(context.Context, *RunFetchRequest) (*RunWithRelations, error) + Create(context.Context, *RunCreationRequest) (*Run, error) + Update(context.Context, *RunUpdateRequest) (*Run, error) +} + +// UnimplementedRunServiceServer should be embedded to have forward compatible implementations. +type UnimplementedRunServiceServer struct { +} + +func (UnimplementedRunServiceServer) Get(context.Context, *RunFetchRequest) (*RunWithRelations, error) { + return nil, status.Errorf(codes.Unimplemented, "method Get not implemented") +} +func (UnimplementedRunServiceServer) Create(context.Context, *RunCreationRequest) (*Run, error) { + return nil, status.Errorf(codes.Unimplemented, "method Create not implemented") +} +func (UnimplementedRunServiceServer) Update(context.Context, *RunUpdateRequest) (*Run, error) { + return nil, status.Errorf(codes.Unimplemented, "method Update not implemented") +} + +// UnsafeRunServiceServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to RunServiceServer will +// result in compilation errors. +type UnsafeRunServiceServer interface { + mustEmbedUnimplementedRunServiceServer() +} + +func RegisterRunServiceServer(s grpc.ServiceRegistrar, srv RunServiceServer) { + s.RegisterService(&RunService_ServiceDesc, srv) +} + +func _RunService_Get_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RunFetchRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(RunServiceServer).Get(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: RunService_Get_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(RunServiceServer).Get(ctx, req.(*RunFetchRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _RunService_Create_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RunCreationRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(RunServiceServer).Create(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: RunService_Create_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(RunServiceServer).Create(ctx, req.(*RunCreationRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _RunService_Update_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RunUpdateRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(RunServiceServer).Update(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: RunService_Update_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(RunServiceServer).Update(ctx, req.(*RunUpdateRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// RunService_ServiceDesc is the grpc.ServiceDesc for RunService service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var RunService_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "o2.bookkeeping.RunService", + HandlerType: (*RunServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Get", + Handler: _RunService_Get_Handler, + }, + { + MethodName: "Create", + Handler: _RunService_Create_Handler, + }, + { + MethodName: "Update", + Handler: _RunService_Update_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "protos/run.proto", +} diff --git a/core/integration/bookkeeping/utils.go b/core/integration/bookkeeping/utils.go new file mode 100644 index 000000000..7813ed844 --- /dev/null +++ b/core/integration/bookkeeping/utils.go @@ -0,0 +1,57 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2021 CERN and copyright holders of ALICE O². + * Author: Miltiadis Alexis + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package bookkeeping + +import ( + "net/http" + + "github.com/spf13/viper" +) + +func getJWTAPIToken() (jwtToken string) { + req, err := http.NewRequest("GET", viper.GetString("bookkeepingBaseUri"), nil) + if err != nil { + log.WithField("error", err.Error()). + Error("cannot create http GET request") + return + } + client := new(http.Client) + client.CheckRedirect = func(req *http.Request, via []*http.Request) error { + if token, ok := req.URL.Query()["token"]; ok { + log.WithField("JWT token", token[0]). + Debug("bookkeeping jwt token") + jwtToken = token[0] + } + return nil + } + + _, err = client.Do(req) + if err != nil { + log.WithField("error", err.Error()). + Error("cannot execute http request") + return + } + return +} diff --git a/core/integration/ccdb/plugin.go b/core/integration/ccdb/plugin.go new file mode 100644 index 000000000..763fd4883 --- /dev/null +++ b/core/integration/ccdb/plugin.go @@ -0,0 +1,575 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2022 CERN and copyright holders of ALICE O². + * Author: Piotr Konopka + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +// Package ccdb provides integration with the Condition and Calibration Database (CCDB) +// for creating and managing General Run Parameters (GRP) objects. +package ccdb + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "go/types" + "net/url" + "os/exec" + "strconv" + "strings" + "time" + + "github.com/AliceO2Group/Control/common/logger/infologger" + "github.com/AliceO2Group/Control/common/monitoring" + + "github.com/AliceO2Group/Control/core/environment" + + "github.com/AliceO2Group/Control/common/logger" + "github.com/AliceO2Group/Control/common/runtype" + "github.com/AliceO2Group/Control/common/utils/uid" + "github.com/AliceO2Group/Control/core/integration" + "github.com/AliceO2Group/Control/core/workflow/callable" + "github.com/sirupsen/logrus" + "github.com/spf13/viper" +) + +var log = logger.New(logrus.StandardLogger(), "ccdbclient") + +type GeneralRunParameters struct { + runNumber uint32 + runType runtype.RunType + runStartTimeMs string // we keep it as string, to avoid converting back and forth from time.Time + runEndCompletionTimeMs string // we keep it as string, to avoid converting back and forth from time.Time + trgStartTimeMs string // we keep it as string, to avoid converting back and forth from time.Time + trgEndTimeMs string // we keep it as string, to avoid converting back and forth from time.Time + detectors []string + continuousReadoutDetectors []string + triggeringDetectors []string + hbfPerTf uint32 // number of HeartBeatFrames per TimeFrame + lhcPeriod string + flpIdList []string + originalRunNumber uint32 // in case it is a replay run, CTP needs the replayed run number (O2-5921) +} + +func parseDetectors(detectorsParam string) (detectors []string, err error) { + detectorsSlice := make([]string, 0) + bytes := []byte(detectorsParam) + err = json.Unmarshal(bytes, &detectorsSlice) + if err != nil { + log.WithError(err). + Error("error processing the detectors list") + return + } + return detectorsSlice, nil +} + +func getFlpIdList(envId string) (flps []string, err error) { + parsedEnvId, err := uid.FromString(envId) + if err != nil { + return []string{}, err + } + envMan := environment.ManagerInstance() + if envMan == nil { + return []string{}, fmt.Errorf("environment manager not initialized") + } + env, err := envMan.Environment(parsedEnvId) + if err != nil { + return []string{}, err + } + flpHostnames := env.GetFLPs() + + const flpPrefix = "alio2-cr1-flp" // this way we accept only P2 setups + flpIds := make([]string, 0) + for _, flp := range flpHostnames { + if !strings.HasPrefix(flp, flpPrefix) { + continue + } + id := strings.TrimPrefix(flp, flpPrefix) + if len(id) > 0 { + flpIds = append(flpIds, id) + } + } + return flpIds, nil +} + +func NewGRPObject(varStack map[string]string) *GeneralRunParameters { + envId, ok := varStack["environment_id"] + if !ok { + log.WithField("level", infologger.IL_Support). + Error("cannot acquire environment ID") + return nil + } + + runNumberStr, ok := varStack["run_number"] + if !ok { + log.WithField("level", infologger.IL_Support). + WithField("partition", envId). + Debug("cannot acquire run number for GRP object") + return nil + } + runNumber, err := strconv.ParseUint(runNumberStr, 10, 32) + if err != nil { + log.WithError(err). + WithField("partition", envId). + WithField("level", infologger.IL_Support). + WithField("run", runNumberStr). + Errorf("cannot convert run number '%s' to an integer", runNumberStr) + return nil + } + + runType := runtype.NONE + runTypeStr, ok := varStack["run_type"] + if ok { + intRt, err := runtype.RunTypeString(runTypeStr) + if err == nil { + runType = intRt + } + } + + runStartTime := varStack["run_start_time_ms"] + runEndCompletionTime := varStack["run_end_completion_time_ms"] + + // use the fake run start time if available + pdpOverrideRunStartTime, ok := varStack["pdp_override_run_start_time"] + if ok && len(pdpOverrideRunStartTime) > 0 { + if strings.Contains(runTypeStr, "SYNTHETIC") { + log.WithField("partition", envId). + WithField("runType", runTypeStr). + WithField("level", infologger.IL_Support). + WithField("run", runNumber). + Infof("overriding run start time in the GRP object to %s for SYNTHETIC run", pdpOverrideRunStartTime) + } else { + log.WithField("partition", envId). + WithField("runType", runTypeStr). + WithField("level", infologger.IL_Support). + WithField("run", runNumber). + Warnf("overriding run start time to %s for non-SYNTHETIC run", pdpOverrideRunStartTime) + } + if len(runEndCompletionTime) > 0 { + // calculate eor time as pdp_override_run_start_time + real run duration + startTimeNumber, err := strconv.ParseUint(runStartTime, 10, 64) + if err != nil { + log.WithError(err). + WithField("partition", envId). + WithField("level", infologger.IL_Support). + WithField("run", runNumber). + Errorf("could not parse runStartTime: %s", runStartTime) + } + pdpOverrideRunStartTimeNumber, err := strconv.ParseUint(pdpOverrideRunStartTime, 10, 64) + if err != nil { + log.WithError(err). + WithField("partition", envId). + WithField("level", infologger.IL_Support). + WithField("run", runNumber). + Errorf("could not parse pdpOverrideRunStartTimeNumber: %s", pdpOverrideRunStartTime) + } + endTimeNumber, err := strconv.ParseUint(runEndCompletionTime, 10, 64) + if err != nil { + log.WithError(err). + WithField("partition", envId). + WithField("level", infologger.IL_Support). + WithField("run", runNumber). + Errorf("could not parse runEndCompletionTime: %s", runEndCompletionTime) + } + if endTimeNumber <= startTimeNumber { + log.WithError(err). + WithField("partition", envId). + WithField("level", infologger.IL_Support). + WithField("run", runNumber). + Errorf("endTimeNumber (%d) is smaller or equal to startTimeNumber (%d)", endTimeNumber, startTimeNumber) + } + + runDuration := endTimeNumber - startTimeNumber + runEndCompletionTime = strconv.FormatUint(pdpOverrideRunStartTimeNumber+runDuration, 10) + } + runStartTime = pdpOverrideRunStartTime + } else if strings.Contains(runTypeStr, "SYNTHETIC") { + log.WithField("partition", envId). + WithField("runType", runTypeStr). + WithField("run", runNumber). + WithField("level", infologger.IL_Ops). + Warnf("requested SYNTHETIC run but run start time override not provided") + } + + // as indicated by Ruben in OCTRL-720, these two are not needed online, + // so it is OK the GRP is sent before the trigger start at SOR (thus without trigger start and stop), + // but we should send these at EOR. + trgStartTime := varStack["trg_start_time_ms"] + trgEndTime := varStack["trg_end_time_ms"] + + detectorsStr, ok := varStack["detectors"] + if !ok { + log.WithField("partition", envId). + WithField("run", runNumber). + WithField("level", infologger.IL_Support). + Error("cannot acquire general detector list from varStack") + } + detectorsSlice, err := parseDetectors(detectorsStr) + if err != nil { + log.WithField("partition", envId). + WithField("run", runNumber). + WithField("level", infologger.IL_Support). + Error("cannot parse general detector list") + return nil + } + + // Special case: if ctp_readout_enabled, we must add TRG to the detectors list + ctpReadoutEnabled := "false" + ctpReadoutEnabled, ok = varStack["ctp_readout_enabled"] + if ok && strings.ToLower(strings.TrimSpace(ctpReadoutEnabled)) == "true" { + detectorsSlice = append(detectorsSlice, "TRG") + } + + // TODO fill once we have those available + var continuousReadoutDetectors []string + var triggeringDetectors []string + + hbfPerTf, err := strconv.ParseUint(varStack["pdp_n_hbf_per_tf"], 10, 32) + if err != nil { + log.WithError(err). + WithField("partition", envId). + WithField("run", runNumber). + WithField("level", infologger.IL_Support). + Error("cannot acquire pdp_n_hbf_per_tf") + return nil + } + + lhcPeriod, ok := varStack["lhc_period"] + if !ok { + log.WithField("partition", envId). + WithField("run", runNumber). + WithField("level", infologger.IL_Support). + Debug("CCDB interface could not retrieve 'lhc_period', putting 'Unknown'.") + lhcPeriod = "Unknown" + } + + flpIds, err := getFlpIdList(envId) + if err != nil { + log.WithField("partition", envId). + WithField("run", runNumber). + WithField("level", infologger.IL_Support). + WithError(err). + Warningf("could not parse env id, FLP list will be empty") + } + + originalRunNumber := uint64(0) + originalRunNumberStr, ok := varStack["original_run_number"] + if ok && len(originalRunNumberStr) > 0 { + if runType != runtype.SYNTHETIC { + log.WithField("partition", envId). + WithField("run", runNumber). + WithField("level", infologger.IL_Support). + Warningf("original run number was set for a non-SYNTHETIC run, ignoring it") + } else { + originalRunNumber, err = strconv.ParseUint(originalRunNumberStr, 10, 32) + if err != nil { + log.WithError(err). + WithField("partition", envId). + WithField("level", infologger.IL_Support). + WithField("run", runNumberStr). + Errorf("cannot convert original run number '%s' to an integer", originalRunNumberStr) + originalRunNumber = 0 + } + } + } + + return &GeneralRunParameters{ + uint32(runNumber), + runType, + runStartTime, + runEndCompletionTime, + trgStartTime, + trgEndTime, + detectorsSlice, + continuousReadoutDetectors, + triggeringDetectors, + uint32(hbfPerTf), + lhcPeriod, + flpIds, + uint32(originalRunNumber), + } +} + +type Plugin struct { + ccdbUrl string + existingRuns map[uint32]types.Nil // using map, because it is more convenient to add, find, delete elements than slice +} + +func NewPlugin(endpoint string) integration.Plugin { + _, err := url.Parse(endpoint) + if err != nil { + log.WithField("endpoint", endpoint). + WithError(err). + Error("bad CCDB endpoint, General Run Parameters objects will not be uploaded!") + return nil + } + + return &Plugin{ + ccdbUrl: endpoint, + existingRuns: make(map[uint32]types.Nil), + } +} + +func (p *Plugin) GetName() string { + return "ccdb" +} + +func (p *Plugin) GetPrettyName() string { + return "CCDB" +} + +func (p *Plugin) GetEndpoint() string { + return viper.GetString("ccdbEndpoint") +} + +func (p *Plugin) GetConnectionState() string { + return "READY" +} + +func (p *Plugin) GetData(_ []any) string { + return "" +} + +func (p *Plugin) GetEnvironmentsData(_ []uid.ID) map[uid.ID]string { + return nil +} + +func (p *Plugin) GetEnvironmentsShortData(_ []uid.ID) map[uid.ID]string { + return nil +} + +func (p *Plugin) Init(instanceId string) error { + return nil +} + +func (p *Plugin) ObjectStack(_ map[string]string, _ map[string]string) (stack map[string]interface{}) { + stack = make(map[string]interface{}) + return stack +} + +func (p *Plugin) NewCcdbGrpWriteCommand(grp *GeneralRunParameters, ccdbUrl string, refresh bool) (cmd string, err error) { + // o2-ecs-grp-create -h + // Create GRP-ECS object and upload to the CCDB + // Usage: + // o2-ecs-grp-create: + // -h [ --help ] Print this help message + // -p [ --period ] arg data taking period + // -r [ --run ] arg run number + // -t [ --run-type ] arg (=0) run type + // -n [ --hbf-per-tf ] arg (=128) number of HBFs per TF + // -d [ --detectors ] arg (=all) comma separated list of detectors + // -c [ --continuous ] arg (=ITS,TPC,TOF,MFT,MCH,MID,ZDC,FT0,FV0,FDD,CTP) + // comma separated list of detectors in + // continuous readout mode + // -g [ --triggering ] arg (=FT0,FV0) comma separated list of detectors + // providing a trigger + // -f [ --flps ] arg comma separated list of FLPs in the + // data taking + // -s [ --start-time ] arg (=0) ECS run start time in ms, now() if 0 + // -e [ --end-time ] arg (=0) ECS run end time in ms, + // start-time+3days is used if 0 + // --start-time-ctp arg (=0) run start CTP time in ms, same as ECS + // if not set or 0 + // --end-time-ctp arg (=0) run end CTP time in ms, same as ECS if + // not set or 0 + // --ccdb-server arg (=http://alice-ccdb.cern.ch) + // CCDB server for upload, local file if + // empty + // --ccdb-server-input arg CCDB server for inputs (if needed, e.g. + // CTPConfig), dy default ccdb-server is + // used + // -m [ --meta-data ] arg metadata as key1=value1;key2=value2;.. + // --refresh [=arg(=async)] refresh server cache after upload: + // "none" (or ""), "async" (non-blocking) + // and "sync" (blocking) + // --marginSOR arg (=345600000) validity at SOR + // --marginEOR arg (=600000) validity margin to add after EOR + // -o [ --original-run ] arg (=0) if >0, use as the source run to create + // CTP/Config/Config object + + cmd = "source /etc/profile.d/o2.sh && o2-ecs-grp-create" + if len(grp.lhcPeriod) == 0 { + return "", fmt.Errorf("could not create a command for CCDB interface because LHC period is empty") + } + cmd += " -p " + grp.lhcPeriod + if grp.runNumber == 0 { + return "", fmt.Errorf("could not create a command for CCDB interface because run number is 0") + } + cmd += " -r " + strconv.FormatUint(uint64(grp.runNumber), 10) + if refresh { + cmd += " --refresh" + } + if grp.hbfPerTf != 0 { + cmd += " -n " + strconv.FormatUint(uint64(grp.hbfPerTf), 10) + } + if grp.runType != runtype.NONE { + cmd += " -t " + strconv.Itoa(int(grp.runType)) + } + if len(grp.detectors) != 0 { + cmd += " -d \"" + strings.Join(grp.detectors, ",") + "\"" + } + if len(grp.continuousReadoutDetectors) != 0 { + cmd += " -c \"" + strings.Join(grp.continuousReadoutDetectors, ",") + "\"" + } + if len(grp.triggeringDetectors) != 0 { + cmd += " -g \"" + strings.Join(grp.triggeringDetectors, ",") + "\"" + } + if len(grp.runStartTimeMs) > 0 { + cmd += " -s " + grp.runStartTimeMs + } + if len(grp.runEndCompletionTimeMs) > 0 { + cmd += " -e " + grp.runEndCompletionTimeMs + } + if len(grp.trgStartTimeMs) > 0 { + cmd += " --start-time-ctp " + grp.trgStartTimeMs + } + if len(grp.trgEndTimeMs) > 0 { + cmd += " --end-time-ctp " + grp.trgEndTimeMs + } + if len(grp.flpIdList) > 0 { + cmd += " -f \"" + strings.Join(grp.flpIdList, ",") + "\"" + } + if grp.originalRunNumber > 0 { + cmd += " -o " + strconv.FormatUint(uint64(grp.originalRunNumber), 10) + } + + cmd += " --ccdb-server " + ccdbUrl + return +} + +func (p *Plugin) CallStack(data interface{}) (stack map[string]interface{}) { + call, ok := data.(*callable.Call) + if !ok { + return + } + varStack := call.VarStack + envId, ok := varStack["environment_id"] + if !ok { + log.WithField("level", infologger.IL_Support). + Error("cannot acquire environment ID") + return + } + + stack = make(map[string]interface{}) + stack["RunStart"] = func() (out string) { // must formally return string even when we return nothing + log.WithField("call", "RunStart"). + WithField("partition", envId). + WithField("level", infologger.IL_Support). + Debug("performing CCDB interface Run Start") + + grp := NewGRPObject(varStack) + if grp == nil { + return + } + p.existingRuns[grp.runNumber] = types.Nil{} + err := p.uploadCurrentGRP(grp, envId, true, varStack, "RunStart") + if err != nil { + log.WithField("call", "RunStop"). + WithField("run", grp.runNumber). + WithField("level", infologger.IL_Support). + WithField("partition", envId).Error(err.Error()) + } + return + } + stack["RunStop"] = func() (out string) { + log.WithField("call", "RunStop"). + WithField("level", infologger.IL_Devel). + WithField("partition", envId).Debug("checking if a CCDB End Of Run GRP should be published") + + grp := NewGRPObject(varStack) + if grp == nil { + log.WithField("call", "RunStop"). + WithField("partition", envId). + WithField("level", infologger.IL_Devel). + Debug("probably went to ERROR while not in RUNNING, doing nothing") + return + } + _, runExists := p.existingRuns[grp.runNumber] + if runExists { + delete(p.existingRuns, grp.runNumber) + err := p.uploadCurrentGRP(grp, envId, false, varStack, "RunStop") + if err != nil { + log.WithField("call", "RunStop"). + WithField("run", grp.runNumber). + WithField("level", infologger.IL_Support). + WithField("partition", envId).Error(err.Error()) + } + } else { + log.WithField("call", "RunStop"). + WithField("run", grp.runNumber). + WithField("partition", envId). + WithField("level", infologger.IL_Devel). + Debugf("most likely a GRP EOR object for run %d already has been published, doing nothing", grp.runNumber) + } + return + } + return +} + +func (p *Plugin) uploadCurrentGRP(grp *GeneralRunParameters, envId string, refresh bool, varStack map[string]string, callName string) error { + if grp == nil { + return errors.New(fmt.Sprintf("Failed to create a GRP object")) + } + log.WithField("partition", envId). + WithField("run", grp.runNumber). + WithField("level", infologger.IL_Devel). + Debugf("GRP: %d, %s, %s, %s, %s, %s, %d, %s, %s, %s, %s", + grp.runNumber, grp.runType.String(), grp.runStartTimeMs, grp.runEndCompletionTimeMs, grp.trgStartTimeMs, grp.trgEndTimeMs, grp.hbfPerTf, grp.lhcPeriod, + strings.Join(grp.detectors, ","), strings.Join(grp.triggeringDetectors, ","), strings.Join(grp.continuousReadoutDetectors, ",")) + cmdStr, err := p.NewCcdbGrpWriteCommand(grp, p.ccdbUrl, refresh) + if err != nil { + return errors.New("Failed to build a GRP to CCDB upload command: " + err.Error()) + } + log.WithField("partition", envId). + WithField("run", grp.runNumber). + WithField("level", infologger.IL_Devel). + Debugf("CCDB GRP upload command: '%s'", cmdStr) + + const timeoutSeconds = 10 + ctx, cancel := context.WithTimeout(context.Background(), timeoutSeconds*time.Second) + defer cancel() + + metric := monitoring.NewMetric("ccdb") + metric.AddTag("envId", envId) + metric.AddTag("runtype", integration.ExtractRunTypeOrUndefined(varStack)) + metric.AddTag("call", callName) + defer monitoring.TimerSendSingle(&metric, monitoring.Millisecond)() + + cmd := exec.CommandContext(ctx, "bash", "-c", cmdStr) + // execute the DPL command in the repo of the workflow used + cmd.Dir = "/tmp" + cmdOut, err := cmd.CombinedOutput() + log.WithField("level", infologger.IL_Support). + WithField("run", grp.runNumber). + Debug("CCDB GRP upload command out: " + string(cmdOut)) + if ctx.Err() == context.DeadlineExceeded { + return errors.New("The command to upload GRP to CCDB timed out (" + strconv.Itoa(timeoutSeconds) + "s).") + } + if err != nil { + return errors.New("Failed to run the command to upload GRP to CCDB: " + err.Error() + "\ncommand out : " + string(cmdOut)) + } + return nil +} + +func (p *Plugin) Destroy() error { + return nil +} diff --git a/core/integration/ccdb/plugin_test.go b/core/integration/ccdb/plugin_test.go new file mode 100644 index 000000000..65b5f5ed1 --- /dev/null +++ b/core/integration/ccdb/plugin_test.go @@ -0,0 +1,266 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2025 CERN and copyright holders of ALICE O². + * Author: Piotr Konopka + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package ccdb + +import ( + "github.com/AliceO2Group/Control/common/runtype" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "testing" +) + +var _ = Describe("NewGRPObject", func() { + var varStack map[string]string + + BeforeEach(func() { + varStack = map[string]string{ + "environment_id": "2oDvieFrVTi", + "run_number": "123456", + "run_type": "PHYSICS", + "run_start_time_ms": "10000", + "run_end_completion_time_ms": "20000", + "trg_start_time_ms": "10100", + "trg_end_time_ms": "19900", + "detectors": `["ITS","TPC"]`, + "pdp_n_hbf_per_tf": "128", + "lhc_period": "LHC22a", + "ctp_readout_enabled": "true", + } + }) + + It("should create a valid GRP object with all fields set", func() { + grp := NewGRPObject(varStack) + Expect(grp).ToNot(BeNil()) + Expect(grp.runNumber).To(Equal(uint32(123456))) + Expect(grp.lhcPeriod).To(Equal("LHC22a")) + Expect(grp.runType).To(Equal(runtype.PHYSICS)) + Expect(grp.runStartTimeMs).To(Equal("10000")) + Expect(grp.runEndCompletionTimeMs).To(Equal("20000")) + Expect(grp.trgStartTimeMs).To(Equal("10100")) + Expect(grp.trgEndTimeMs).To(Equal("19900")) + Expect(grp.detectors).To(ContainElements("ITS", "TPC", "TRG")) // TRG added due to ctp_readout_enabled + Expect(grp.hbfPerTf).To(Equal(uint32(128))) + }) + + It("should return nil when environment_id is missing", func() { + delete(varStack, "environment_id") + grp := NewGRPObject(varStack) + Expect(grp).To(BeNil()) + }) + + It("should return nil when run_number is missing", func() { + delete(varStack, "run_number") + grp := NewGRPObject(varStack) + Expect(grp).To(BeNil()) + }) + + It("should handle invalid run_number format", func() { + varStack["run_number"] = "invalid" + grp := NewGRPObject(varStack) + Expect(grp).To(BeNil()) + }) + + It("should handle missing run_type", func() { + delete(varStack, "run_type") + grp := NewGRPObject(varStack) + Expect(grp).ToNot(BeNil()) + Expect(grp.runType).To(Equal(runtype.NONE)) + }) + + It("should return nil when pdp_n_hbf_per_tf is missing", func() { + delete(varStack, "pdp_n_hbf_per_tf") + grp := NewGRPObject(varStack) + Expect(grp).To(BeNil()) + }) + + It("should override run start time for synthetic runs and compute run end time accordingly", func() { + varStack["run_type"] = "SYNTHETIC" + varStack["pdp_override_run_start_time"] = "15000" + grp := NewGRPObject(varStack) + Expect(grp).ToNot(BeNil()) + Expect(grp.runStartTimeMs).To(Equal("15000")) + Expect(grp.runEndCompletionTimeMs).To(Equal("25000")) + }) + + It("should log a warning for run start time override in non-synthetic runs, but override it anyway", func() { + // overriding is not really a strong requirement, it could be changed if requested + varStack["pdp_override_run_start_time"] = "15000" + grp := NewGRPObject(varStack) + Expect(grp).ToNot(BeNil()) + Expect(grp.runStartTimeMs).To(Equal("15000")) + Expect(grp.runEndCompletionTimeMs).To(Equal("25000")) + }) + + It("should survive empty detectors list", func() { + varStack["detectors"] = "[]" + varStack["ctp_readout_enabled"] = "false" + grp := NewGRPObject(varStack) + Expect(grp).ToNot(BeNil()) + Expect(grp.detectors).To(BeEmpty()) + }) + + It("should return nil when there is invalid detectors JSON", func() { + varStack["detectors"] = "invalid json" + grp := NewGRPObject(varStack) + Expect(grp).To(BeNil()) + }) + + It("should handle missing trg_start_time_ms and trg_end_time_ms", func() { + delete(varStack, "trg_start_time_ms") + delete(varStack, "trg_end_time_ms") + grp := NewGRPObject(varStack) + Expect(grp).ToNot(BeNil()) + Expect(grp.trgStartTimeMs).To(BeEmpty()) + Expect(grp.trgEndTimeMs).To(BeEmpty()) + }) + + It("should handle synthetic run with original run number", func() { + varStack["run_type"] = "SYNTHETIC" + varStack["original_run_number"] = "654321" + grp := NewGRPObject(varStack) + Expect(grp).ToNot(BeNil()) + Expect(grp.originalRunNumber).To(Equal(uint32(654321))) + Expect(grp.runNumber).To(Equal(uint32(123456))) + }) + + It("should ignore original run number for non-synthetic runs", func() { + varStack["original_run_number"] = "654321" + grp := NewGRPObject(varStack) + Expect(grp).ToNot(BeNil()) + Expect(grp.originalRunNumber).To(Equal(uint32(0))) + }) + + // fixme: we do not test extracting the list of FLPs, because we would need to mock an env manager with a realistic enough environment + // once it is easier to mock it, we should add a test for it +}) + +var _ = Describe("NewCcdbGrpWriteCommand", func() { + var plugin *Plugin + var grp *GeneralRunParameters + + BeforeEach(func() { + plugin = &Plugin{ + ccdbUrl: "http://ccdb-test:8080", + } + grp = &GeneralRunParameters{ + runNumber: 123456, + runType: runtype.PHYSICS, + detectors: []string{"ITS", "TPC"}, + runStartTimeMs: "10000", + runEndCompletionTimeMs: "20000", + trgStartTimeMs: "10100", + trgEndTimeMs: "19900", + hbfPerTf: 128, + lhcPeriod: "LHC22a", + } + }) + + It("should create basic command with required fields", func() { + cmd, err := plugin.NewCcdbGrpWriteCommand(grp, "http://ccdb-test:8080", false) + Expect(err).ToNot(HaveOccurred()) + Expect(cmd).To(ContainSubstring(" -r 123456")) + Expect(cmd).To(ContainSubstring(" -p LHC22a")) + Expect(cmd).To(ContainSubstring(" -t 1")) // PHYSICS enum value + Expect(cmd).To(ContainSubstring(" -n 128")) + Expect(cmd).To(ContainSubstring(" -s 10000")) + Expect(cmd).To(ContainSubstring(" -e 20000")) + Expect(cmd).To(ContainSubstring(" --start-time-ctp 10100")) + Expect(cmd).To(ContainSubstring(" --end-time-ctp 19900")) + Expect(cmd).To(ContainSubstring(" --ccdb-server http://ccdb-test:8080")) + }) + + It("should return an error when run number is 0", func() { + grp.runNumber = 0 + cmd, err := plugin.NewCcdbGrpWriteCommand(grp, "http://ccdb-test:8080", false) + Expect(err).To(HaveOccurred()) + Expect(cmd).To(BeEmpty()) + }) + + It("should return an error when LHC period is missing", func() { + grp.lhcPeriod = "" + cmd, err := plugin.NewCcdbGrpWriteCommand(grp, "http://ccdb-test:8080", false) + Expect(err).To(HaveOccurred()) + Expect(cmd).To(BeEmpty()) + }) + + It("should add the refresh flag when requested", func() { + cmd, err := plugin.NewCcdbGrpWriteCommand(grp, "http://ccdb-test:8080", true) + Expect(err).ToNot(HaveOccurred()) + Expect(cmd).To(ContainSubstring(" --refresh")) + }) + + It("should not add the refresh flag when not requested", func() { + cmd, err := plugin.NewCcdbGrpWriteCommand(grp, "http://ccdb-test:8080", false) + Expect(err).ToNot(HaveOccurred()) + Expect(cmd).ToNot(ContainSubstring(" --refresh")) + }) + + It("should include detector lists when present", func() { + grp.continuousReadoutDetectors = []string{"ITS"} + grp.triggeringDetectors = []string{"TPC"} + cmd, err := plugin.NewCcdbGrpWriteCommand(grp, "http://ccdb-test:8080", false) + Expect(err).ToNot(HaveOccurred()) + Expect(cmd).To(ContainSubstring(` -d "ITS,TPC"`)) + Expect(cmd).To(ContainSubstring(` -c "ITS"`)) + Expect(cmd).To(ContainSubstring(` -g "TPC"`)) + }) + + It("should include FLP list when present", func() { + grp.flpIdList = []string{"flp-1", "flp-2"} + cmd, err := plugin.NewCcdbGrpWriteCommand(grp, "http://ccdb-test:8080", false) + Expect(err).ToNot(HaveOccurred()) + Expect(cmd).To(ContainSubstring(` -f "flp-1,flp-2"`)) + }) + + It("should include original run number for synthetic runs", func() { + grp.originalRunNumber = 654321 + cmd, err := plugin.NewCcdbGrpWriteCommand(grp, "http://ccdb-test:8080", false) + Expect(err).ToNot(HaveOccurred()) + Expect(cmd).To(ContainSubstring(" -o 654321")) + }) + + It("should skip optional fields when empty", func() { + grp = &GeneralRunParameters{ + runNumber: 123456, + lhcPeriod: "LHC22a", + } + cmd, err := plugin.NewCcdbGrpWriteCommand(grp, "http://ccdb-test:8080", false) + Expect(err).ToNot(HaveOccurred()) + Expect(cmd).NotTo(ContainSubstring(" -d")) + Expect(cmd).NotTo(ContainSubstring(" -c")) + Expect(cmd).NotTo(ContainSubstring(" -g")) + Expect(cmd).NotTo(ContainSubstring(" -f")) + Expect(cmd).NotTo(ContainSubstring(" -t")) + Expect(cmd).NotTo(ContainSubstring(" -s")) + Expect(cmd).NotTo(ContainSubstring(" -e")) + Expect(cmd).NotTo(ContainSubstring(" --start-time-ctp")) + Expect(cmd).NotTo(ContainSubstring(" --end-time-ctp")) + }) +}) + +func TestCcdbGrpPlugin(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "CCDB GRP integration plugin Test Suite") +} diff --git a/core/integration/dcs/client.go b/core/integration/dcs/client.go new file mode 100644 index 000000000..6a529bd59 --- /dev/null +++ b/core/integration/dcs/client.go @@ -0,0 +1,152 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2021 CERN and copyright holders of ALICE O². + * Author: Teo Mrnjavac + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package dcs + +import ( + "context" + "time" + + "github.com/AliceO2Group/Control/common/logger" + "github.com/AliceO2Group/Control/common/monitoring" + dcspb "github.com/AliceO2Group/Control/core/integration/dcs/protos" + "github.com/sirupsen/logrus" + "github.com/spf13/viper" + "google.golang.org/grpc" + "google.golang.org/grpc/backoff" + "google.golang.org/grpc/connectivity" + "google.golang.org/grpc/keepalive" +) + +var log = logger.New(logrus.StandardLogger(), "dcsclient") + +func convertMethodName(method string) (converted string) { + switch method { + case dcspb.Configurator_Subscribe_FullMethodName: + converted = "Subscribe" + case dcspb.Configurator_PrepareForRun_FullMethodName: + converted = "PFR" + case dcspb.Configurator_StartOfRun_FullMethodName: + converted = "SOR" + case dcspb.Configurator_EndOfRun_FullMethodName: + converted = "EOR" + default: + converted = "Unknown" + } + return +} + +type RpcClient struct { + dcspb.ConfiguratorClient + conn *grpc.ClientConn + cancel context.CancelFunc +} + +func NewClient(cxt context.Context, cancel context.CancelFunc, endpoint string) *RpcClient { + log.WithFields(logrus.Fields{ + "endpoint": endpoint, + }).Debug("dialing DCS endpoint") + + dialOptions := []grpc.DialOption{ + grpc.WithInsecure(), + grpc.WithConnectParams(grpc.ConnectParams{ + Backoff: backoff.Config{ + BaseDelay: backoff.DefaultConfig.BaseDelay, + Multiplier: backoff.DefaultConfig.Multiplier, + Jitter: backoff.DefaultConfig.Jitter, + MaxDelay: 20 * time.Second, + }, + MinConnectTimeout: 10 * time.Second, + }), + grpc.WithKeepaliveParams(keepalive.ClientParameters{ + Time: 5 * time.Second, + Timeout: time.Second, + PermitWithoutStream: true, + }), + grpc.WithStreamInterceptor(monitoring.SetupStreamClientInterceptor("dcsgrpcstream", convertMethodName)), + } + if !viper.GetBool("dcsServiceUseSystemProxy") { + dialOptions = append(dialOptions, grpc.WithNoProxy()) + } + + conn, err := grpc.DialContext(cxt, + endpoint, + dialOptions..., + ) + if err != nil { + log.WithField("error", err.Error()). + WithField("endpoint", endpoint). + Errorf("cannot dial RPC endpoint") + cancel() + return nil + } + + go func() { + connState := connectivity.Idle + stateChangedNotify := make(chan bool) + notifyFunc := func(st connectivity.State) { + ok := conn.WaitForStateChange(cxt, st) + stateChangedNotify <- ok + } + go notifyFunc(connState) + + for { + select { + case ok := <-stateChangedNotify: + if !ok { + return + } + connState = conn.GetState() + log.Debugf("DCS client %s", connState.String()) + go notifyFunc(connState) + case <-time.After(2 * time.Minute): + if conn.GetState() != connectivity.Ready { + conn.ResetConnectBackoff() + } + case <-cxt.Done(): + return + } + } + }() + + client := &RpcClient{ + ConfiguratorClient: dcspb.NewConfiguratorClient(conn), + conn: conn, + cancel: cancel, + } + + return client +} + +func (m *RpcClient) GetConnState() connectivity.State { + if m.conn == nil { + return connectivity.Idle + } + return m.conn.GetState() +} + +func (m *RpcClient) Close() error { + m.cancel() + return m.conn.Close() +} diff --git a/core/integration/dcs/dcsutil.go b/core/integration/dcs/dcsutil.go new file mode 100644 index 000000000..d1fb888b0 --- /dev/null +++ b/core/integration/dcs/dcsutil.go @@ -0,0 +1,100 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2022-2023 CERN and copyright holders of ALICE O². + * Author: Teo Mrnjavac + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package dcs + +import ( + "fmt" + "github.com/AliceO2Group/Control/common/logger/infologger" + "github.com/AliceO2Group/Control/core/the" + "strings" + + dcspb "github.com/AliceO2Group/Control/core/integration/dcs/protos" + "github.com/sirupsen/logrus" +) + +func resolveDefaults(detectorArgMap map[string]string, varStack map[string]string, ecsDetector string, theLog *logrus.Entry) map[string]string { + // Do we have any default expressions for defaultable values? + defaultableKeys := []string{} // at the moment we do not have any defaultable keys + + for _, key := range defaultableKeys { + if defaultableValue, ok := detectorArgMap[key]; ok { + if strings.TrimSpace(strings.ToLower(defaultableValue)) == "default" { // if one of the defaultable keys has value `default`... + defaultPayloadKey := strings.ToLower(ecsDetector) + "_default_" + key // e.g. tof_default_ddl_list + if defaultPayload, ok := varStack[defaultPayloadKey]; ok { + detectorArgMap[key] = defaultPayload + } else { + theLog.Warnf("requested default value for DCS parameter %s but no payload found at key %s: the string 'default' will be sent instead", key, defaultPayloadKey) + } + } + } + } + return detectorArgMap +} + +func addEnabledLinks(detectorArgMap map[string]string, varStack map[string]string, ecsDetector string, theLog *logrus.Entry) map[string]string { + sendDetLinks, ok := varStack[strings.ToLower(ecsDetector)+"_dcs_send_enabled_links"] + if !ok || sendDetLinks != "true" { + return detectorArgMap + } + linkIDs, err := the.ConfSvc().GetAliasedLinkIDsForDetector(ecsDetector, true) + if err != nil { + theLog.WithError(err).Errorf("failed to get aliased link IDs for detector '%s'", ecsDetector) + return detectorArgMap + } + linksJoined := strings.Join(linkIDs, ",") + theLog.WithField(infologger.Level, infologger.IL_Devel). + Infof("adding enabled link IDs for detector '%s' to DCS payload '%s'", ecsDetector, linksJoined) + detectorArgMap["ddl_list"] = linksJoined + + return detectorArgMap +} + +func ecsToDcsDetector(ecsDetector string) (dcspb.Detector, error) { + var err error + det := strings.ToUpper(strings.TrimSpace(ecsDetector)) + + // Special case: if TST is present, we send AGD to DCS + if det == "TST" { + det = "AGD" + } + + intDet, ok := dcspb.Detector_value[det] + if !ok { + err = fmt.Errorf("invalid DCS detector %s", det) + log.WithError(err).Error("bad DCS detector entry") + return dcspb.Detector_NULL_DETECTOR, err + } + + return dcspb.Detector(intDet), nil +} + +func dcsToEcsDetector(dcsDetector dcspb.Detector) string { + det := dcsDetector.String() + // Special case: if AGD is present, we send TST to ECS + if det == "AGD" { + det = "TST" + } + return det +} diff --git a/core/integration/dcs/plugin.go b/core/integration/dcs/plugin.go new file mode 100644 index 000000000..0c202e2bf --- /dev/null +++ b/core/integration/dcs/plugin.go @@ -0,0 +1,2338 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2021-2025 CERN and copyright holders of ALICE O². + * Author: Teo Mrnjavac + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +//go:generate protoc --go_out=. --go_opt=paths=source_relative --go-grpc_opt=paths=source_relative --go-grpc_out=require_unimplemented_servers=false:. protos/dcs.proto + +// Package dcs provides integration with the ALICE Detector Control System (DCS) +// for managing detector operations and state compatibility checks. +package dcs + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "io" + "net/url" + "sort" + "strconv" + "strings" + "sync" + "time" + + "dario.cat/mergo" + "github.com/AliceO2Group/Control/common/event/topic" + "github.com/AliceO2Group/Control/common/logger/infologger" + "github.com/AliceO2Group/Control/common/monitoring" + pb "github.com/AliceO2Group/Control/common/protos" + "github.com/AliceO2Group/Control/common/runtype" + "github.com/AliceO2Group/Control/common/utils/uid" + "github.com/AliceO2Group/Control/core/environment" + "github.com/AliceO2Group/Control/core/integration" + dcspb "github.com/AliceO2Group/Control/core/integration/dcs/protos" + "github.com/AliceO2Group/Control/core/the" + "github.com/AliceO2Group/Control/core/workflow/callable" + "github.com/jinzhu/copier" + "github.com/spf13/viper" + "golang.org/x/exp/maps" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/connectivity" + "google.golang.org/grpc/status" +) + +const ( + DCS_DIAL_TIMEOUT = 2 * time.Hour + DCS_GENERAL_OP_TIMEOUT = 45 * time.Second + DCS_TIME_FORMAT = "2006-01-02 15:04:05.000" + TOPIC = topic.IntegratedService + topic.Separator + "dcs" + + DCS_RESULT_OK = "ok" + DCS_RESULT_TIMEOUT = "timeout" + DCS_RESULT_GRPC_TIMEOUT = "gRPC_timeout" + DCS_RESULT_GRPC_UNKNOWN = "gRPC_unknown" + DCS_RESULT_GRPC_ERROR = "gRPC_error" +) + +type Plugin struct { + dcsHost string + dcsPort int + + dcsClient *RpcClient + pendingEORs map[string] /*envId*/ int64 + + detectorMap DCSDetectorInfoMap + detectorMapMu sync.RWMutex +} + +type DCSDetectors []dcspb.Detector + +type DCSDetectorOpAvailabilityMap map[dcspb.Detector]dcspb.DetectorState + +type DCSDetectorInfoMap map[dcspb.Detector]*dcspb.DetectorInfo + +type ECSDetectorInfoMap map[string]ECSDetectorInfo + +func NewPlugin(endpoint string) integration.Plugin { + u, err := url.Parse(endpoint) + if err != nil { + log.WithField("endpoint", endpoint). + WithError(err). + Error("bad service endpoint") + return nil + } + + portNumber, _ := strconv.Atoi(u.Port()) + + newPlugin := &Plugin{ + dcsHost: u.Hostname(), + dcsPort: portNumber, + dcsClient: nil, + pendingEORs: make(map[string]int64), + detectorMap: make(DCSDetectorInfoMap), + } + return newPlugin +} + +func (p *Plugin) GetName() string { + return "dcs" +} + +func (p *Plugin) GetPrettyName() string { + return "DCS" +} + +func (p *Plugin) GetEndpoint() string { + return viper.GetString("dcsServiceEndpoint") +} + +func (p *Plugin) GetConnectionState() string { + if p == nil || p.dcsClient == nil { + return "UNKNOWN" + } + return p.dcsClient.conn.GetState().String() +} + +func (p *Plugin) GetData(_ []any) string { + if p == nil || p.dcsClient == nil { + return "" + } + + environmentIds := environment.ManagerInstance().Ids() + + outMap := make(map[string]interface{}) + outMap["partitions"] = p.pendingEorsForEnvs(environmentIds) + + p.detectorMapMu.RLock() + outMap["detectors"] = p.detectorMap.ToEcsDetectors() + p.detectorMapMu.RUnlock() + + out, err := json.Marshal(outMap) + if err != nil { + return "" + } + return string(out[:]) +} + +func (p *Plugin) GetEnvironmentsData(environmentIds []uid.ID) map[uid.ID]string { + if p == nil || p.dcsClient == nil { + return nil + } + + envMan := environment.ManagerInstance() + pendingEors := p.pendingEorsForEnvs(environmentIds) + + out := make(map[uid.ID]string) + + detectorMap := p.detectorMap.ToEcsDetectors() + for _, envId := range environmentIds { + env, err := envMan.Environment(envId) + if err != nil { + log.WithField("partition", envId). + WithError(err). + Error("DCS client cannot acquire environment") + continue + } + + includedDetectors := env.GetActiveDetectors().StringList() + includedDetectorsMap := detectorMap.Filtered(includedDetectors) + + pi := PartitionInfo{ + Detectors: includedDetectorsMap, + } + if pendingEorStatus, pendingEorExists := pendingEors[envId]; pendingEorExists { + pi.SorSuccessful = pendingEorStatus + } + + marshalled, err := json.Marshal(pi) + if err != nil { + continue + } + out[envId] = string(marshalled[:]) + } + + return out +} + +func (p *Plugin) GetEnvironmentsShortData(environmentIds []uid.ID) map[uid.ID]string { + return nil +} + +func (p *Plugin) pendingEorsForEnvs(envIds []uid.ID) map[uid.ID]bool { + out := make(map[uid.ID]bool) + for _, envId := range envIds { + _, pendingEorExists := p.pendingEORs[envId.String()] + out[envId] = pendingEorExists + } + return out +} + +func (p *Plugin) updateLastKnownDetectorStates(detectorMatrix []*dcspb.DetectorInfo) { + p.detectorMapMu.Lock() + defer p.detectorMapMu.Unlock() + + for _, detInfo := range detectorMatrix { + dcsDet := detInfo.GetDetector() + if _, ok := p.detectorMap[dcsDet]; !ok { + p.detectorMap[dcsDet] = detInfo + } else { + // If we're getting a PFR or SOR availability information within the State field of an incoming STATE_CHANGE_EVENT, + // before processing it as any other state change, we need to update the availability fields + if detInfo.State == dcspb.DetectorState_PFR_AVAILABLE || detInfo.State == dcspb.DetectorState_PFR_UNAVAILABLE { + p.detectorMap[dcsDet].PfrAvailability = detInfo.State + } + if detInfo.State == dcspb.DetectorState_SOR_AVAILABLE || detInfo.State == dcspb.DetectorState_SOR_UNAVAILABLE { + p.detectorMap[dcsDet].SorAvailability = detInfo.State + } + + // if we're getting a STATE_CHANGE event with any non-null state + if detInfo.State != dcspb.DetectorState_NULL_STATE { + p.detectorMap[dcsDet].State = detInfo.State + timestamp, err := time.Parse(DCS_TIME_FORMAT, detInfo.Timestamp) + if err == nil { + p.detectorMap[dcsDet].Timestamp = fmt.Sprintf("%d", timestamp.UnixMilli()) + } + } + } + } +} + +func (p *Plugin) updateDetectorOpAvailabilities(detectorMatrix []*dcspb.DetectorInfo) { + p.detectorMapMu.Lock() + defer p.detectorMapMu.Unlock() + + pfrAvailabilityChangedDetectors := map[dcspb.Detector]struct{}{} + sorAvailabilityChangedDetectors := map[dcspb.Detector]struct{}{} + + for _, detInfo := range detectorMatrix { + dcsDet := detInfo.GetDetector() + if _, ok := p.detectorMap[dcsDet]; !ok { + pfrAvailabilityChangedDetectors[dcsDet] = struct{}{} + sorAvailabilityChangedDetectors[dcsDet] = struct{}{} + + p.detectorMap[dcsDet] = detInfo + } else { + if p.detectorMap[dcsDet].PfrAvailability != detInfo.PfrAvailability { + pfrAvailabilityChangedDetectors[dcsDet] = struct{}{} + } + if p.detectorMap[dcsDet].SorAvailability != detInfo.SorAvailability { + sorAvailabilityChangedDetectors[dcsDet] = struct{}{} + } + p.detectorMap[dcsDet].PfrAvailability = detInfo.PfrAvailability + p.detectorMap[dcsDet].SorAvailability = detInfo.SorAvailability + timestamp, err := time.Parse(DCS_TIME_FORMAT, detInfo.Timestamp) + if err == nil { + p.detectorMap[dcsDet].Timestamp = fmt.Sprintf("%d", timestamp.UnixMilli()) + } + } + } + + // build payload for event + payload := map[string]interface{}{ + "detectors": p.detectorMap.ToEcsDetectors(), + "changed": map[string]interface{}{ + "pfrAvailability": DCSDetectors(maps.Keys(pfrAvailabilityChangedDetectors)).EcsDetectorsSlice(), + "sorAvailability": DCSDetectors(maps.Keys(sorAvailabilityChangedDetectors)).EcsDetectorsSlice(), + }, + } + + payloadJson, _ := json.Marshal(payload) + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: "dcs.updateDetectorOpAvailabilities", + Payload: string(payloadJson[:]), + }) +} + +func (p *Plugin) Init(instanceId string) error { + if p.dcsClient == nil { + cxt, cancel := context.WithCancel(context.Background()) + p.dcsClient = NewClient(cxt, cancel, viper.GetString("dcsServiceEndpoint")) + if p.dcsClient == nil { + return fmt.Errorf("failed to connect to DCS service on %s", viper.GetString("dcsServiceEndpoint")) + } + + in := &dcspb.SubscriptionRequest{ + InstanceId: instanceId, + } + + // Always start the goroutine, even if initial subscription fails + go func() { + var evStream dcspb.Configurator_SubscribeClient + var err error + + for { + // Try to establish subscription if we don't have one + if evStream == nil { + log.WithField("endpoint", viper.GetString("dcsServiceEndpoint")). + Debug("attempting to subscribe to DCS service") + + evStream, err = p.dcsClient.Subscribe(context.Background(), in, grpc.EmptyCallOption{}) + if err != nil { + log.WithField("endpoint", viper.GetString("dcsServiceEndpoint")). + WithError(err). + Warnf("failed to subscribe to DCS service, possible network issue or DCS gateway malfunction") + time.Sleep(3 * time.Second) + continue + } else { + log.WithField("endpoint", viper.GetString("dcsServiceEndpoint")). + WithField("level", infologger.IL_Support). + Info("successfully subscribed to DCS service") + } + } + + // Process events from the stream + for { + if evStream == nil { + break + } + ev, streamErr := evStream.Recv() + if streamErr == io.EOF { + log.Info("unexpected EOF from DCS service, possible DCS gateway malfunction") + evStream = nil + break + } + + if streamErr != nil { + log.WithError(streamErr). + Error("stream error or bad event from DCS service, dropping stream") + evStream = nil + time.Sleep(3 * time.Second) + break + } + + if ev != nil && ev.Eventtype == dcspb.EventType_HEARTBEAT { + log.Trace("received DCS heartbeat event") + if dm := ev.GetDetectorMatrix(); len(dm) > 0 { + p.updateDetectorOpAvailabilities(dm) + } + continue + } + + if ev != nil && ev.Eventtype == dcspb.EventType_STATE_CHANGE_EVENT { + log.Trace("received DCS state change event") + if dm := ev.GetDetectorMatrix(); len(dm) > 0 { + p.updateLastKnownDetectorStates(dm) + } + continue + } + + log.WithField("event", ev.String()). + Debug("received DCS event") + } + + // If we reach here, the stream was dropped and evStream is nil + // The loop will continue and try to reestablish the subscription + log.WithField("endpoint", viper.GetString("dcsServiceEndpoint")). + Info("DCS stream dropped, attempting reconnect") + } + }() + } + if p.dcsClient == nil { + return fmt.Errorf("failed to start DCS client on %s", viper.GetString("dcsServiceEndpoint")) + } + log.Debug("DCS plugin initialized") + return nil +} + +func (p *Plugin) ObjectStack(_ map[string]string, _ map[string]string) (stack map[string]interface{}) { + stack = make(map[string]interface{}) + return stack +} + +func (p *Plugin) getDetectorsPfrAvailability(dcsDetectors DCSDetectors) DCSDetectorOpAvailabilityMap { + p.detectorMapMu.RLock() + defer p.detectorMapMu.RUnlock() + + availabilityMap := make(DCSDetectorOpAvailabilityMap) + + for _, dcsDet := range dcsDetectors { + if dcsDet == dcspb.Detector_DCS { + continue + } + availabilityMap[dcsDet] = dcspb.DetectorState_NULL_STATE + + if _, contains := p.detectorMap[dcsDet]; contains { + availabilityMap[dcsDet] = p.detectorMap[dcsDet].PfrAvailability + } + } + + return availabilityMap +} + +func (p *Plugin) getDetectorsSorAvailability(dcsDetectors DCSDetectors) DCSDetectorOpAvailabilityMap { + p.detectorMapMu.RLock() + defer p.detectorMapMu.RUnlock() + + availabilityMap := make(DCSDetectorOpAvailabilityMap) + + for _, dcsDet := range dcsDetectors { + if dcsDet == dcspb.Detector_DCS { + continue + } + availabilityMap[dcsDet] = dcspb.DetectorState_NULL_STATE + + if _, contains := p.detectorMap[dcsDet]; contains { + availabilityMap[dcsDet] = p.detectorMap[dcsDet].SorAvailability + } + } + + return availabilityMap +} + +func (p *Plugin) CallStack(data interface{}) (stack map[string]interface{}) { + call, callOk := data.(*callable.Call) + if !callOk { + return + } + varStack := call.VarStack + envId, envOk := varStack["environment_id"] + if !envOk { + log.WithField("call", "PrepareForRun"). + Error("cannot acquire environment ID") + return + } + + stack = make(map[string]interface{}) + stack["PrepareForRun"] = func() (out string) { // must formally return string even when we return nothing + var err error + log := log.WithField("partition", envId).WithField("call", "PrepareForRun") + callFailedStr := "DCS PrepareForRun call failed" + + dcsDetectorsParam, ok := varStack["dcs_detectors"] + if !ok { + log.Debug("empty DCS detectors list provided") + dcsDetectorsParam = "[\"NULL_DETECTOR\"]" + } + + dcsDetectors, err := p.parseDetectors(dcsDetectorsParam) + if err != nil { + log.WithError(err). + WithField("level", infologger.IL_Ops). + Error("DCS error") + + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + + return + } + + // We acquire a grace period during which we hope that DCS will become compatible with the operation. + // During this period we'll keep checking our internal state for op compatibility as reported by DCS + // at 1Hz, and if we don't get a compatible state for all included detectors within the grace period, + // we go ahead with a partial PFR. + pfrGracePeriod := time.Duration(0) + pfrGracePeriodS, ok := varStack["dcs_pfr_grace_period"] + if ok { + pfrGracePeriod, err = time.ParseDuration(pfrGracePeriodS) + if err != nil { + log.WithError(err). + WithField("level", infologger.IL_Ops). + Warnf("cannot parse DCS PFR grace period, assuming 0 seconds") + } + } else { + log.WithField("level", infologger.IL_Ops). + Info("DCS PFR grace period not set, defaulting to 0 seconds") + } + + payload := map[string]interface{}{ + "detectors": dcsDetectors.EcsDetectorsSlice(), + } + payloadJson, _ := json.Marshal(payload) + + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_STARTED, + OperationStep: "acquire detectors availability", + OperationStepStatus: pb.OpStatus_STARTED, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + }) + + pfrGraceTimeout := time.Now().Add(pfrGracePeriod) + isCompatibleWithOperation := false + + var dcsDetectorsCompatibleWithPfr, dcsDetectorsIncompatibleWithPfr DCSDetectors + + knownDetectorStates := p.getDetectorsPfrAvailability(dcsDetectors) + isCompatibleWithOperation, dcsDetectorsCompatibleWithPfr, dcsDetectorsIncompatibleWithPfr, err = knownDetectorStates.compatibleWithDCSOperation(dcspb.DetectorState_PFR_AVAILABLE) + + for { + if isCompatibleWithOperation { + break + } else { + log.WithField("level", infologger.IL_Ops). + WithField("grace_period", pfrGracePeriod.String()). + WithField("remaining_grace_period", pfrGraceTimeout.Sub(time.Now()).String()). + Infof("waiting for DCS operation readiness: %s", err.Error()) + time.Sleep(1 * time.Second) + } + + if time.Now().Before(pfrGraceTimeout) { + knownDetectorStates = p.getDetectorsPfrAvailability(dcsDetectors) + isCompatibleWithOperation, dcsDetectorsCompatibleWithPfr, dcsDetectorsIncompatibleWithPfr, err = knownDetectorStates.compatibleWithDCSOperation(dcspb.DetectorState_PFR_AVAILABLE) + } else { + break + } + } + + ecsDetectorsIncompatibleWithPfr := dcsDetectorsIncompatibleWithPfr.EcsDetectorsSlice() + sort.Strings(ecsDetectorsIncompatibleWithPfr) + + if !isCompatibleWithOperation { // some detectors are not ready for PFR + if len(dcsDetectorsCompatibleWithPfr) == 0 { // if actually none are ready, we bail + log.WithError(err). + WithField("level", infologger.IL_Ops). + Error("DCS error") + + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_ERROR, + OperationStep: "acquire detectors availability", + OperationStepStatus: pb.OpStatus_DONE_ERROR, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + Error: err.Error(), + }) + + return + } + } else if isCompatibleWithOperation && err != nil { + log.WithField("level", infologger.IL_Ops). + Warnf("cannot determine PFR readiness: %s", err.Error()) + } + + payload["detectorsReadiness"] = knownDetectorStates.EcsDetectorsMap() + payloadJson, _ = json.Marshal(payload) + + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_ONGOING, + OperationStep: "acquire detectors availability", + OperationStepStatus: pb.OpStatus_DONE_OK, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + }) + + // By now the DCS must be in a compatible state for at least some detectors, so we proceed + // with gathering params for the operation + + if len(ecsDetectorsIncompatibleWithPfr) > 0 { + log.WithField("level", infologger.IL_Ops). + Warnf("skipping DCS PFR for detectors: %s - a detector state compatible with PFR was not reached within %s", strings.Join(ecsDetectorsIncompatibleWithPfr, " "), pfrGracePeriod) + } + log.WithField("level", infologger.IL_Ops). + Infof("performing DCS PFR for detectors: %s", strings.Join(dcsDetectorsCompatibleWithPfr.EcsDetectorsSlice(), " ")) + + parameters, ok := varStack["dcs_sor_parameters"] + if !ok { + log.Debug("no DCS SOR parameters set") + parameters = "{}" + } + + argMap := make(map[string]string) + bytes := []byte(parameters) + err = json.Unmarshal(bytes, &argMap) + if err != nil { + err = fmt.Errorf("error processing DCS PFR parameters: %w", err) + log.WithError(err). + WithField("level", infologger.IL_Ops). + Error("DCS error") + + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + + return + } + + rt := dcspb.RunType_TECHNICAL + runTypeS, ok := varStack["run_type"] + if ok { + // a detector is defined in the var stack + // so we convert from the provided string to the correct enum value in common/runtype + intRt, err := runtype.RunTypeString(runTypeS) + if err == nil { + if intRt == runtype.COSMICS { + // special case for COSMICS runs: there is no DCS run type for it, so we send PHYSICS + rt = dcspb.RunType_PHYSICS + } else { + // the runType was correctly matched to the common/runtype enum, but since the DCS enum is + // kept compatible, we can directly convert the runtype.RunType to a dcspb.RunType enum value + rt = dcspb.RunType(intRt) + } + } + } + + // Preparing the per-detector request payload + in := dcspb.PfrRequest{ + RunType: rt, + PartitionId: envId, + Detectors: make([]*dcspb.DetectorOperationRequest, len(dcsDetectorsCompatibleWithPfr)), + } + for i, dcsDet := range dcsDetectorsCompatibleWithPfr { + ecsDet := dcsToEcsDetector(dcsDet) + perDetectorParameters, okParam := varStack[strings.ToLower(ecsDet)+"_dcs_sor_parameters"] + if !okParam { + log.Debug("empty DCS detectors list provided") + perDetectorParameters = "{}" + } + detectorArgMap := make(map[string]string) + bytes = []byte(perDetectorParameters) + err = json.Unmarshal(bytes, &detectorArgMap) + if err != nil { + err = fmt.Errorf("error processing %s DCS SOR parameter map: %w", ecsDet, err) + + log.WithError(err). + WithField("level", infologger.IL_Ops). + WithField("detector", ecsDet). + Error("DCS error") + + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + + return + } + + // Per-detector parameters override any general dcs_sor_parameters + err = mergo.Merge(&detectorArgMap, argMap) + if err != nil { + err = fmt.Errorf("error processing %s DCS SOR general parameters override: %w", dcsDet.String(), err) + + log.WithError(err). + WithField("level", infologger.IL_Ops). + WithField("detector", ecsDet). + Error("DCS error") + + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + + return + } + + // We parse the consolidated per-detector payload for any defaultable parameters with value "default" + detectorArgMap = resolveDefaults(detectorArgMap, varStack, ecsDet, + log.WithField("detector", ecsDet)) + + detectorArgMap = addEnabledLinks(detectorArgMap, varStack, ecsDet, + log.WithField("detector", ecsDet)) + + // a state sequence that can be defined in order to get an expected answer, useful for testing. + // format: MILLIS_DELAY:DETECTOR_STATE[,MILLIS_DELAY:DETECTOR_STATE]* + pfrStateSequence, ok := varStack["dcs_pfr_state_sequence"] + if ok { + detectorArgMap["STATE_SEQUENCE"] = pfrStateSequence + } + + in.Detectors[i] = &dcspb.DetectorOperationRequest{ + Detector: dcsDet, + ExtraParameters: detectorArgMap, + } + } + + if p.dcsClient == nil { + err = fmt.Errorf("DCS plugin not initialized, PrepareForRun impossible") + + log.WithError(err). + WithField("level", infologger.IL_Ops). + WithField("endpoint", viper.GetString("dcsServiceEndpoint")). + Error("DCS error") + + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + + return + } + + if p.dcsClient.GetConnState() == connectivity.Shutdown { + err = fmt.Errorf("DCS client connection not available, PrepareForRun impossible") + + log.WithError(err). + WithField("level", infologger.IL_Ops). + WithField("endpoint", viper.GetString("dcsServiceEndpoint")). + Error("DCS error") + + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + + return + } + + var stream dcspb.Configurator_StartOfRunClient + timeout := callable.AcquireTimeout(DCS_GENERAL_OP_TIMEOUT, varStack, "PFR", envId) + ctx, cancel := integration.NewContext(envId, varStack, timeout) + defer cancel() + + detectorStatusMap := make(map[dcspb.Detector]dcspb.DetectorState) + for _, v := range dcsDetectorsCompatibleWithPfr { + detectorStatusMap[v] = dcspb.DetectorState_NULL_STATE + } + + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_ONGOING, + OperationStep: "perform DCS call: PrepareForRun", + OperationStepStatus: pb.OpStatus_STARTED, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + }) + + // Point of no return + // The gRPC call below is expected to return immediately, with any actual responses arriving subsequently via + // the response stream. + // Regardless of DCS SOR success or failure, once the StartOfRun call returns, an EndOfRun **must** be enqueued + // for later, either during STOP_ACTIVITY or cleanup. + stream, err = p.dcsClient.PrepareForRun(ctx, &in, grpc.EmptyCallOption{}) + if err != nil { + log.WithError(err). + WithField("level", infologger.IL_Ops). + WithField("endpoint", viper.GetString("dcsServiceEndpoint")). + Error("DCS error") + + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_ERROR, + OperationStep: "perform DCS call: PrepareForRun", + OperationStepStatus: pb.OpStatus_DONE_ERROR, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + Error: err.Error(), + }) + + return + } + + err, payloadJson = PFRgRPCCommunicationLoop(ctx, timeout, call, envId, payloadJson, stream, + detectorStatusMap, callFailedStr, payload, integration.ExtractRunTypeOrUndefined(varStack)) + + dcsFailedEcsDetectors := make([]string, 0) + dcsopOk := true + for _, v := range dcsDetectorsCompatibleWithPfr { + if detectorStatusMap[v] != dcspb.DetectorState_RUN_OK { + dcsopOk = false + dcsFailedEcsDetectors = append(dcsFailedEcsDetectors, dcsToEcsDetector(v)) + } + } + if dcsopOk { + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_OK, + OperationStep: "perform DCS call: PrepareForRun", + OperationStepStatus: pb.OpStatus_DONE_OK, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + }) + } else { + logErr := fmt.Errorf("PFR failed for %s", strings.Join(dcsFailedEcsDetectors, ", ")) + if err != nil { + logErr = fmt.Errorf("%v : %v", err, logErr) + } + + log.WithError(logErr). + WithField("level", infologger.IL_Ops). + WithField("endpoint", viper.GetString("dcsServiceEndpoint")). + Error("DCS error") + + call.VarStack["__call_error_reason"] = logErr.Error() + call.VarStack["__call_error"] = callFailedStr + + payload["failedDetectors"] = dcsFailedEcsDetectors + payloadJson, _ = json.Marshal(payload) + + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_ERROR, + OperationStep: "perform DCS call: PrepareForRun", + OperationStepStatus: pb.OpStatus_DONE_ERROR, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + Error: logErr.Error(), + }) + } + return + } + stack["StartOfRun"] = func() (out string) { // must formally return string even when we return nothing + var err error + callFailedStr := "DCS StartOfRun call failed" + log := log.WithField("partition", envId).WithField("call", "StartOfRun") + + rn := varStack["run_number"] + var runNumber64 int64 + runNumber64, err = strconv.ParseInt(rn, 10, 32) + if err != nil { + log.WithError(err). + Error("cannot acquire run number for DCS SOR") + } + log = log.WithField("run", runNumber64) + + dcsDetectorsParam, ok := varStack["dcs_detectors"] + if !ok { + log.Debug("empty DCS detectors list provided") + dcsDetectorsParam = "[\"NULL_DETECTOR\"]" + } + + dcsDetectors, err := p.parseDetectors(dcsDetectorsParam) + if err != nil { + log.WithError(err). + WithField("level", infologger.IL_Ops). + Error("DCS error") + + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + + return + } + + // We acquire a grace period during which we hope that DCS will become compatible with the operation. + // During this period we'll keep checking our internal state for op compatibility as reported by DCS at 1Hz, + // and if we don't get a compatible state within the grace period, we declare the operation failed. + sorGracePeriod := time.Duration(0) + sorGracePeriodS, ok := varStack["dcs_sor_grace_period"] + if ok { + sorGracePeriod, err = time.ParseDuration(sorGracePeriodS) + if err != nil { + log.WithError(err). + WithField("level", infologger.IL_Ops). + Warnf("cannot parse DCS SOR grace period, assuming 0 seconds") + } + } else { + log.WithField("level", infologger.IL_Ops). + Info("DCS SOR grace period not set, defaulting to 0 seconds") + } + + payload := map[string]interface{}{ + "detectors": dcsDetectors.EcsDetectorsSlice(), + "runNumber": runNumber64, + } + payloadJson, _ := json.Marshal(payload) + + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_STARTED, + OperationStep: "acquire detectors availability", + OperationStepStatus: pb.OpStatus_STARTED, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + }) + + sorGraceTimeout := time.Now().Add(sorGracePeriod) + isCompatibleWithOperation := false + + knownDetectorStates := p.getDetectorsSorAvailability(dcsDetectors) + isCompatibleWithOperation, _, _, err = knownDetectorStates.compatibleWithDCSOperation(dcspb.DetectorState_SOR_AVAILABLE) + + for { + if isCompatibleWithOperation { + break + } else { + log.WithField("level", infologger.IL_Ops). + WithField("grace_period", sorGracePeriod.String()). + WithField("remaining_grace_period", sorGraceTimeout.Sub(time.Now()).String()). + Infof("waiting for DCS operation readiness: %s", err.Error()) + time.Sleep(1 * time.Second) + } + + if time.Now().Before(sorGraceTimeout) { + knownDetectorStates = p.getDetectorsSorAvailability(dcsDetectors) + isCompatibleWithOperation, _, _, err = knownDetectorStates.compatibleWithDCSOperation(dcspb.DetectorState_SOR_AVAILABLE) + } else { + break + } + } + + if !isCompatibleWithOperation { + log.WithError(err). + WithField("level", infologger.IL_Ops). + Error("DCS error") + + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_ERROR, + OperationStep: "acquire detectors availability", + OperationStepStatus: pb.OpStatus_DONE_ERROR, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + Error: err.Error(), + }) + + return + } else if isCompatibleWithOperation && err != nil { + log.WithField("level", infologger.IL_Ops). + Warnf("cannot determine SOR readiness: %s", err.Error()) + } + + payload["detectorsReadiness"] = knownDetectorStates.EcsDetectorsMap() + payloadJson, _ = json.Marshal(payload) + + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_ONGOING, + OperationStep: "acquire detectors availability", + OperationStepStatus: pb.OpStatus_DONE_OK, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + }) + + // By now the DCS must be in a compatible state, so we proceed with gathering params for the operation + + log.WithField("level", infologger.IL_Ops). + Infof("performing DCS SOR for detectors: %s", strings.Join(dcsDetectors.EcsDetectorsSlice(), " ")) + + parameters, ok := varStack["dcs_sor_parameters"] + if !ok { + log.Debug("no DCS SOR parameters set") + parameters = "{}" + } + + argMap := make(map[string]string) + bytes := []byte(parameters) + err = json.Unmarshal(bytes, &argMap) + if err != nil { + err = fmt.Errorf("error processing DCS SOR parameters: %w", err) + log.WithError(err). + WithField("level", infologger.IL_Ops). + Error("DCS error") + + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + + return + } + + rt := dcspb.RunType_TECHNICAL + runTypeS, ok := varStack["run_type"] + if ok { + // a detector is defined in the var stack + // so we convert from the provided string to the correct enum value in common/runtype + intRt, err := runtype.RunTypeString(runTypeS) + if err == nil { + if intRt == runtype.COSMICS { + // special case for COSMICS runs: there is no DCS run type for it, so we send PHYSICS + rt = dcspb.RunType_PHYSICS + } else { + // the runType was correctly matched to the common/runtype enum, but since the DCS enum is + // kept compatible, we can directly convert the runtype.RunType to a dcspb.RunType enum value + rt = dcspb.RunType(intRt) + } + } + } + + // Preparing the per-detector request payload + in := dcspb.SorRequest{ + RunType: rt, + RunNumber: int32(runNumber64), + Detectors: make([]*dcspb.DetectorOperationRequest, len(dcsDetectors)), + } + for i, dcsDet := range dcsDetectors { + ecsDet := dcsToEcsDetector(dcsDet) + perDetectorParameters, okParam := varStack[strings.ToLower(ecsDet)+"_dcs_sor_parameters"] + if !okParam { + log.Debug("empty DCS detectors list provided") + perDetectorParameters = "{}" + } + detectorArgMap := make(map[string]string) + bytes := []byte(perDetectorParameters) + err = json.Unmarshal(bytes, &detectorArgMap) + if err != nil { + err = fmt.Errorf("error processing %s DCS SOR parameter map: %w", ecsDet, err) + + log.WithError(err). + WithField("level", infologger.IL_Ops). + WithField("detector", ecsDet). + Error("DCS error") + + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + + return + } + + // Per-detector parameters override any general dcs_sor_parameters + err = mergo.Merge(&detectorArgMap, argMap) + if err != nil { + err = fmt.Errorf("error processing %s DCS SOR general parameters override: %w", dcsDet.String(), err) + + log.WithError(err). + WithField("level", infologger.IL_Ops). + WithField("detector", ecsDet). + Error("DCS error") + + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + + return + } + + // We parse the consolidated per-detector payload for any defaultable parameters with value "default" + detectorArgMap = resolveDefaults(detectorArgMap, varStack, ecsDet, + log.WithField("detector", ecsDet)) + + detectorArgMap = addEnabledLinks(detectorArgMap, varStack, ecsDet, + log.WithField("detector", ecsDet)) + + // a state sequence that can be defined in order to get an expected answer, useful for testing. + // format: MILLIS_DELAY:DETECTOR_STATE[,MILLIS_DELAY:DETECTOR_STATE]* + sorStateSequence, ok := varStack["dcs_sor_state_sequence"] + if ok { + detectorArgMap["STATE_SEQUENCE"] = sorStateSequence + } + + in.Detectors[i] = &dcspb.DetectorOperationRequest{ + Detector: dcsDet, + ExtraParameters: detectorArgMap, + } + } + + if p.dcsClient == nil { + err = fmt.Errorf("DCS plugin not initialized, StartOfRun impossible") + + log.WithError(err). + WithField("level", infologger.IL_Ops). + WithField("endpoint", viper.GetString("dcsServiceEndpoint")). + Error("DCS error") + + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + + return + } + + if p.dcsClient.GetConnState() == connectivity.Shutdown { + err = fmt.Errorf("DCS client connection not available, StartOfRun impossible") + + log.WithError(err). + WithField("level", infologger.IL_Ops). + WithField("endpoint", viper.GetString("dcsServiceEndpoint")). + Error("DCS error") + + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + + return + } + + var stream dcspb.Configurator_StartOfRunClient + timeout := callable.AcquireTimeout(DCS_GENERAL_OP_TIMEOUT, varStack, "SOR", envId) + ctx, cancel := integration.NewContext(envId, varStack, timeout) + defer cancel() + + detectorStatusMap := make(map[dcspb.Detector]dcspb.DetectorState) + for _, v := range dcsDetectors { + detectorStatusMap[v] = dcspb.DetectorState_NULL_STATE + } + + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_ONGOING, + OperationStep: "perform DCS call: StartOfRun", + OperationStepStatus: pb.OpStatus_STARTED, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + }) + + // Point of no return + // The gRPC call below is expected to return immediately, with any actual responses arriving subsequently via + // the response stream. + // Regardless of DCS SOR success or failure, once the StartOfRun call returns, an EndOfRun **must** be enqueued + // for later, either during STOP_ACTIVITY or cleanup. + stream, err = p.dcsClient.StartOfRun(ctx, &in, grpc.EmptyCallOption{}) + if err != nil { + log.WithError(err). + WithField("level", infologger.IL_Ops). + WithField("endpoint", viper.GetString("dcsServiceEndpoint")). + Error("DCS error") + + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_ERROR, + OperationStep: "perform DCS call: StartOfRun", + OperationStepStatus: pb.OpStatus_DONE_ERROR, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + Error: err.Error(), + }) + + return + } + p.pendingEORs[envId] = runNumber64 // make sure the corresponding EOR runs sooner or later + + err, payloadJson = SORgRPCCommunicationLoop(ctx, timeout, call, envId, payloadJson, stream, + detectorStatusMap, callFailedStr, payload, integration.ExtractRunTypeOrUndefined(varStack)) + + dcsFailedEcsDetectors := make([]string, 0) + dcsopOk := true + for _, v := range dcsDetectors { + if detectorStatusMap[v] != dcspb.DetectorState_RUN_OK { + dcsopOk = false + dcsFailedEcsDetectors = append(dcsFailedEcsDetectors, dcsToEcsDetector(v)) + } + } + if dcsopOk { + p.pendingEORs[envId] = runNumber64 + + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_OK, + OperationStep: "perform DCS call: StartOfRun", + OperationStepStatus: pb.OpStatus_DONE_OK, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + }) + } else { + logErr := fmt.Errorf("SOR failed for %s, DCS EOR will run anyway for this run", strings.Join(dcsFailedEcsDetectors, ", ")) + if err != nil { + logErr = fmt.Errorf("%v : %v", err, logErr) + } + + log.WithError(logErr). + WithField("level", infologger.IL_Ops). + WithField("endpoint", viper.GetString("dcsServiceEndpoint")). + Error("DCS error") + + call.VarStack["__call_error_reason"] = logErr.Error() + call.VarStack["__call_error"] = callFailedStr + + payload["failedDetectors"] = dcsFailedEcsDetectors + payloadJson, _ = json.Marshal(payload) + + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_ERROR, + OperationStep: "perform DCS call: StartOfRun", + OperationStepStatus: pb.OpStatus_DONE_ERROR, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + Error: logErr.Error(), + }) + } + return + } + eorFunc := func(runNumber64 int64) (out string) { // must formally return string even when we return nothing + callFailedStr := "DCS EndOfRun call failed" + log := log.WithField("partition", envId).WithField("call", "EndOfRun").WithField("run", runNumber64) + + dcsDetectorsParam, ok := varStack["dcs_detectors"] + if !ok { + log.Debug("empty DCS detectors list provided") + dcsDetectorsParam = "[\"NULL_DETECTOR\"]" + } + + dcsDetectors, err := p.parseDetectors(dcsDetectorsParam) + if err != nil { + log.WithError(err). + WithField("level", infologger.IL_Ops). + Error("DCS error") + + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + + return + } + + log.WithField("level", infologger.IL_Ops). + Infof("performing DCS EOR for detectors: %s", strings.Join(dcsDetectors.EcsDetectorsSlice(), " ")) + + parameters, ok := varStack["dcs_eor_parameters"] + if !ok { + log.WithField("partition", envId). + Debug("no DCS EOR parameters set") + parameters = "{}" + } + + argMap := make(map[string]string) + bytes := []byte(parameters) + err = json.Unmarshal(bytes, &argMap) + if err != nil { + err = fmt.Errorf("error processing DCS EOR parameters: %w", err) + + log.WithError(err). + WithField("level", infologger.IL_Ops). + Error("DCS error") + + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + + return + } + + // Preparing the per-detector request payload + in := dcspb.EorRequest{ + RunNumber: int32(runNumber64), + Detectors: make([]*dcspb.DetectorOperationRequest, len(dcsDetectors)), + } + for i, dcsDet := range dcsDetectors { + ecsDet := dcsToEcsDetector(dcsDet) + perDetectorParameters, okParam := varStack[strings.ToLower(ecsDet)+"_dcs_eor_parameters"] + if !okParam { + log.Debug("empty DCS detectors list provided") + perDetectorParameters = "{}" + } + detectorArgMap := make(map[string]string) + bytes := []byte(perDetectorParameters) + err = json.Unmarshal(bytes, &detectorArgMap) + if err != nil { + err = fmt.Errorf("error processing %s DCS EOR parameter map: %w", dcsDet.String(), err) + + log.WithError(err). + WithField("level", infologger.IL_Ops). + WithField("detector", ecsDet). + Error("DCS error") + + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + + return + } + + // Per-detector parameters override any general dcs_eor_parameters + err = mergo.Merge(&detectorArgMap, argMap) + if err != nil { + err = fmt.Errorf("error processing %s DCS EOR general parameters override: %w", dcsDet.String(), err) + + log.WithError(err). + WithField("level", infologger.IL_Ops). + WithField("detector", ecsDet). + Error("DCS error") + + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + + return + } + // a state sequence that can be defined in order to get an expected answer, useful for testing. + // format: MILLIS_DELAY:DETECTOR_STATE[,MILLIS_DELAY:DETECTOR_STATE]* + eorStateSequence, ok := varStack["dcs_eor_state_sequence"] + if ok { + detectorArgMap["STATE_SEQUENCE"] = eorStateSequence + } + + in.Detectors[i] = &dcspb.DetectorOperationRequest{ + Detector: dcsDet, + ExtraParameters: detectorArgMap, + } + } + + if p.dcsClient == nil { + err = fmt.Errorf("DCS plugin not initialized, EndOfRun impossible") + + log.WithError(err). + WithField("level", infologger.IL_Ops). + WithField("endpoint", viper.GetString("dcsServiceEndpoint")). + Error("DCS error") + + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + + return + } + if p.dcsClient.GetConnState() == connectivity.Shutdown { + err = fmt.Errorf("DCS client connection not available, EndOfRun impossible") + + log.WithError(err). + WithField("level", infologger.IL_Ops). + WithField("endpoint", viper.GetString("dcsServiceEndpoint")). + Error("DCS error") + + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + + return + } + + var stream dcspb.Configurator_EndOfRunClient + timeout := callable.AcquireTimeout(DCS_GENERAL_OP_TIMEOUT, varStack, "EOR", envId) + ctx, cancel := integration.NewContext(envId, varStack, timeout) + defer cancel() + + payload := map[string]interface{}{ + "detectors": dcsDetectors.EcsDetectorsSlice(), + "runNumber": runNumber64, + } + payloadJson, _ := json.Marshal(payload) + + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_STARTED, + OperationStep: "perform DCS call: EndOfRun", + OperationStepStatus: pb.OpStatus_STARTED, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + }) + + // Point of no return + // The gRPC call below is expected to return immediately, with any actual responses arriving subsequently via + // the response stream. + // Regardless of DCS EOR success or failure, it must run once and only once, therefore if this call returns + // a nil error, we immediately dequeue the pending EOR. + stream, err = p.dcsClient.EndOfRun(ctx, &in, grpc.EmptyCallOption{}) + if err != nil { + log.WithError(err). + WithField("level", infologger.IL_Ops). + WithField("endpoint", viper.GetString("dcsServiceEndpoint")). + Error("DCS error") + + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_ERROR, + OperationStep: "perform DCS call: EndOfRun", + OperationStepStatus: pb.OpStatus_DONE_ERROR, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + Error: err.Error(), + }) + + return + } + delete(p.pendingEORs, envId) // make sure this EOR never runs again + + log.WithField("level", infologger.IL_Ops). + WithField("endpoint", viper.GetString("dcsServiceEndpoint")). + Debug("DCS EndOfRun returned stream, awaiting responses (DCS cleanup will not run for this environment)") + + detectorStatusMap := make(map[dcspb.Detector]dcspb.DetectorState) + for _, v := range dcsDetectors { + detectorStatusMap[v] = dcspb.DetectorState_NULL_STATE + } + + err, payloadJson = EORgRPCCommunicationLoop(ctx, timeout, call, envId, payloadJson, stream, + detectorStatusMap, callFailedStr, payload, integration.ExtractRunTypeOrUndefined(varStack)) + + dcsFailedEcsDetectors := make([]string, 0) + dcsopOk := true + for _, v := range dcsDetectors { + if detectorStatusMap[v] != dcspb.DetectorState_RUN_OK { + dcsopOk = false + dcsFailedEcsDetectors = append(dcsFailedEcsDetectors, dcsToEcsDetector(v)) + } + } + if dcsopOk { + delete(p.pendingEORs, envId) + + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_OK, + OperationStep: "perform DCS call: EndOfRun", + OperationStepStatus: pb.OpStatus_DONE_OK, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + }) + } else { + logErr := fmt.Errorf("EOR failed for %s, DCS EOR will NOT run again for this run", strings.Join(dcsFailedEcsDetectors, ", ")) + if err != nil { + logErr = fmt.Errorf("%v : %v", err, logErr) + } + + log.WithError(logErr). + WithField("level", infologger.IL_Ops). + WithField("endpoint", viper.GetString("dcsServiceEndpoint")). + Error("DCS error") + + call.VarStack["__call_error_reason"] = logErr.Error() + call.VarStack["__call_error"] = callFailedStr + + payload["failedDetectors"] = dcsFailedEcsDetectors + payloadJson, _ = json.Marshal(payload) + + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_ERROR, + OperationStep: "perform DCS call: EndOfRun", + OperationStepStatus: pb.OpStatus_DONE_ERROR, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + Error: logErr.Error(), + }) + } + return + } + stack["EndOfRun"] = func() (out string) { + rn := varStack["run_number"] + var runNumber64 int64 + var err error + runNumber64, err = strconv.ParseInt(rn, 10, 32) + if err != nil { + log.WithError(err). + WithField("partition", envId). + Error("cannot acquire run number for DCS EOR") + } + return eorFunc(runNumber64) + } + stack["Cleanup"] = func() (out string) { + envId, ok := varStack["environment_id"] + if !ok { + log.WithField("partition", envId). + WithField("level", infologger.IL_Devel). + Warn("no environment_id found for DCS cleanup") + return + } + + runNumber, ok := p.pendingEORs[envId] + if !ok { + log.WithField("partition", envId). + WithField("level", infologger.IL_Devel). + Debug("DCS cleanup: nothing to do") + return + } + + log.WithField("run", runNumber). + WithField("partition", envId). + WithField("level", infologger.IL_Devel). + WithField("call", "Cleanup"). + Debug("pending DCS EOR found, performing cleanup") + + out = eorFunc(runNumber) + delete(p.pendingEORs, envId) + + return + } + + return +} + +func newMetric(runType, envId, method string) monitoring.Metric { + metric := monitoring.NewMetric("dcsecs") + metric.AddTag("method", method) + metric.AddTag("envId", envId) + metric.AddTag("runtype", runType) + return metric +} + +func EORgRPCCommunicationLoop(ctx context.Context, timeout time.Duration, call *callable.Call, envId string, + payloadJsonForKafka []byte, stream dcspb.Configurator_EndOfRunClient, detectorStatusMap map[dcspb.Detector]dcspb.DetectorState, + callFailedStr string, payload map[string]interface{}, runType string, +) (error, []byte) { + metric := newMetric(runType, envId, "EOR") + defer monitoring.TimerSendSingle(&metric, monitoring.Millisecond)() + eor := "EOR" + durationsPerDetector := map[dcspb.Detector]time.Duration{} + start := time.Now() + + totalCallDurationMetric := newMetric(runType, envId, eor) + totalCallDurationMetric.AddTag("detector", "All") + defer monitoring.TimerSendSingle(&totalCallDurationMetric, monitoring.Millisecond)() + + var dcsEvent *dcspb.RunEvent + var err error + for { + // fixme: consider removing this check, since `stream.Recv()` will return a timeout error anyway if the context expires + if ctx.Err() != nil { + err = fmt.Errorf("DCS EndOfRun context timed out (%s), any future DCS events are ignored", timeout.String()) + + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_ONGOING, + OperationStep: "perform DCS call: EndOfRun", + OperationStepStatus: pb.OpStatus_DONE_TIMEOUT, + EnvironmentId: envId, + Payload: string(payloadJsonForKafka[:]), + Error: err.Error(), + }) + + addFunctionResult(&totalCallDurationMetric, DCS_RESULT_GRPC_TIMEOUT) + break + } + dcsEvent, err = stream.Recv() + if errors.Is(err, io.EOF) { // correct stream termination + log.Debug("DCS EOR event stream was closed from the DCS side (EOF)") + err = nil + + addFunctionResult(&totalCallDurationMetric, DCS_RESULT_OK) + break // no more data + } + if errors.Is(err, context.DeadlineExceeded) { + log.WithError(err). + WithField("timeout", timeout.String()). + Debug("DCS EOR timed out") + err = fmt.Errorf("DCS EOR timed out after %s: %w", timeout.String(), err) + + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_ONGOING, + OperationStep: "perform DCS call: EndOfRun", + OperationStepStatus: pb.OpStatus_DONE_TIMEOUT, + EnvironmentId: envId, + Payload: string(payloadJsonForKafka[:]), + Error: err.Error(), + }) + + addFunctionResult(&totalCallDurationMetric, DCS_RESULT_TIMEOUT) + break + } + if err != nil { // stream termination in case of unknown or gRPC error + got := status.Code(err) + + if got == codes.DeadlineExceeded { + log.WithError(err). + WithField("timeout", timeout.String()). + Debug("DCS EOR timed out") + err = fmt.Errorf("DCS EOR timed out after %s: %w", timeout.String(), err) + + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_ONGOING, + OperationStep: "perform DCS call: EndOfRun", + OperationStepStatus: pb.OpStatus_DONE_TIMEOUT, + EnvironmentId: envId, + Payload: string(payloadJsonForKafka[:]), + Error: err.Error(), + }) + addFunctionResult(&totalCallDurationMetric, DCS_RESULT_GRPC_TIMEOUT) + + } else if got == codes.Unknown { // unknown error, likely not a gRPC code + logMsg := "bad DCS EOR event received, any future DCS events are ignored" + log.WithError(err). + Warn(logMsg) + + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_ONGOING, + OperationStep: "perform DCS call: EndOfRun", + OperationStepStatus: pb.OpStatus_DONE_ERROR, + EnvironmentId: envId, + Payload: string(payloadJsonForKafka[:]), + Error: logMsg, + }) + addFunctionResult(&totalCallDurationMetric, DCS_RESULT_GRPC_UNKNOWN) + } else { // some other gRPC error code + log.WithError(err). + Debug("DCS EOR call error") + err = fmt.Errorf("DCS EOR call error: %w", err) + + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_ONGOING, + OperationStep: "perform DCS call: EndOfRun", + OperationStepStatus: pb.OpStatus_DONE_ERROR, + EnvironmentId: envId, + Payload: string(payloadJsonForKafka[:]), + Error: err.Error(), + }) + addFunctionResult(&totalCallDurationMetric, DCS_RESULT_GRPC_ERROR) + } + + break + } + if dcsEvent == nil { + log.Warn("nil DCS EOR event received, skipping to next DCS event") + continue + } + if dcsEvent.GetDetector() == dcspb.Detector_DCS { + log.WithField(infologger.Level, infologger.IL_Support). + Warnf("Received an event for DCS detector (%s), which is unexpected, ignoring", dcsEvent.GetState().String()) + continue + } + + detectorStatusMap[dcsEvent.GetDetector()] = dcsEvent.GetState() + durationsPerDetector[dcsEvent.GetDetector()] = time.Since(start) + ecsDet := dcsToEcsDetector(dcsEvent.GetDetector()) + + if dcsEvent.GetState() == dcspb.DetectorState_EOR_FAILURE { + logErr := fmt.Errorf("%s EOR failure reported by DCS", ecsDet) + log.WithError(logErr). + WithField("event", dcsEvent). + WithField("detector", ecsDet). + WithField("level", infologger.IL_Ops). + WithField("endpoint", viper.GetString("dcsServiceEndpoint")). + Error("DCS error") + + call.VarStack["__call_error_reason"] = logErr.Error() + call.VarStack["__call_error"] = callFailedStr + + payload["detector"] = ecsDet + payload["dcsEvent"] = dcsEvent + payload["state"] = dcspb.DetectorState_name[int32(dcsEvent.GetState())] + payloadJsonForKafka, _ = json.Marshal(payload) + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_ONGOING, + OperationStep: "perform DCS call: EndOfRun", + OperationStepStatus: pb.OpStatus_DONE_ERROR, + EnvironmentId: envId, + Payload: string(payloadJsonForKafka[:]), + Error: logErr.Error(), + }) + + continue + } + + if dcsEvent.GetState() == dcspb.DetectorState_TIMEOUT { + logErr := fmt.Errorf("%s EOR timeout reported by DCS", ecsDet) + if err != nil { + logErr = fmt.Errorf("%v : %v", err, logErr) + } + log.WithError(logErr). + WithField("event", dcsEvent). + WithField("detector", ecsDet). + WithField("level", infologger.IL_Ops). + WithField("endpoint", viper.GetString("dcsServiceEndpoint")). + Error("DCS error") + + call.VarStack["__call_error_reason"] = logErr.Error() + call.VarStack["__call_error"] = callFailedStr + + payload["detector"] = ecsDet + payload["dcsEvent"] = dcsEvent + payload["state"] = dcspb.DetectorState_name[int32(dcsEvent.GetState())] + payloadJsonForKafka, _ = json.Marshal(payload) + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_ONGOING, + OperationStep: "perform DCS call: EndOfRun", + OperationStepStatus: pb.OpStatus_DONE_TIMEOUT, + EnvironmentId: envId, + Payload: string(payloadJsonForKafka[:]), + Error: logErr.Error(), + }) + + continue + } + + if dcsEvent.GetState() == dcspb.DetectorState_RUN_OK { + log.WithField("event", dcsEvent). + WithField("level", infologger.IL_Support). + WithField("detector", dcsToEcsDetector(dcsEvent.GetDetector())). + Info("ALIECS EOR operation : completed DCS EOR for ") + + detPayload := map[string]interface{}{} + _ = copier.Copy(&detPayload, payload) + detPayload["detector"] = ecsDet + detPayload["state"] = dcspb.DetectorState_name[int32(dcsEvent.GetState())] + detPayload["dcsEvent"] = dcsEvent + detPayloadJson, _ := json.Marshal(detPayload) + + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_ONGOING, + OperationStep: "perform DCS call: EndOfRun", + OperationStepStatus: pb.OpStatus_ONGOING, + EnvironmentId: envId, + Payload: string(detPayloadJson[:]), + }) + } else { + log.WithField("event", dcsEvent). + WithField("level", infologger.IL_Devel). + WithField("detector", dcsToEcsDetector(dcsEvent.GetDetector())). + Info("ALIECS EOR operation : processing DCS EOR for ") + + detPayload := map[string]interface{}{} + _ = copier.Copy(&detPayload, payload) + detPayload["detector"] = ecsDet + detPayload["state"] = dcspb.DetectorState_name[int32(dcsEvent.GetState())] + detPayload["dcsEvent"] = dcsEvent + detPayloadJson, _ := json.Marshal(detPayload) + + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_ONGOING, + OperationStep: "perform DCS call: EndOfRun", + OperationStepStatus: pb.OpStatus_ONGOING, + EnvironmentId: envId, + Payload: string(detPayloadJson[:]), + }) + } + } + + convertAndSendDetectorDurationsAndStates(eor, detectorStatusMap, durationsPerDetector, envId, runType, &totalCallDurationMetric) + + return err, payloadJsonForKafka +} + +func SORgRPCCommunicationLoop(ctx context.Context, timeout time.Duration, call *callable.Call, envId string, + payloadJsonForKafka []byte, stream dcspb.Configurator_StartOfRunClient, detectorStatusMap map[dcspb.Detector]dcspb.DetectorState, + callFailedStr string, payload map[string]interface{}, runType string, +) (error, []byte) { + sor := "SOR" + detectorDurations := map[dcspb.Detector]time.Duration{} + start := time.Now() + + totalCallDurationMetric := newMetric(runType, envId, sor) + totalCallDurationMetric.AddTag("detector", "All") + defer monitoring.TimerSendSingle(&totalCallDurationMetric, monitoring.Millisecond)() + + var dcsEvent *dcspb.RunEvent + var err error + for { + // fixme: consider removing this check, since `stream.Recv()` will return a timeout error anyway if the context expires + if ctx.Err() != nil { + err = fmt.Errorf("DCS StartOfRun context timed out (%s), any future DCS events are ignored", timeout.String()) + + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_ONGOING, + OperationStep: "perform DCS call: StartOfRun", + OperationStepStatus: pb.OpStatus_DONE_TIMEOUT, + EnvironmentId: envId, + Payload: string(payloadJsonForKafka[:]), + Error: err.Error(), + }) + + addFunctionResult(&totalCallDurationMetric, DCS_RESULT_TIMEOUT) + break + } + dcsEvent, err = stream.Recv() + if errors.Is(err, io.EOF) { // correct stream termination + log.Debug("DCS SOR event stream was closed from the DCS side (EOF)") + err = nil + + addFunctionResult(&totalCallDurationMetric, DCS_RESULT_OK) + break // no more data + } + if errors.Is(err, context.DeadlineExceeded) { + log.WithError(err). + WithField("timeout", timeout.String()). + Debug("DCS SOR timed out") + err = fmt.Errorf("DCS SOR timed out after %s: %w", timeout.String(), err) + + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_ONGOING, + OperationStep: "perform DCS call: StartOfRun", + OperationStepStatus: pb.OpStatus_DONE_TIMEOUT, + EnvironmentId: envId, + Payload: string(payloadJsonForKafka[:]), + Error: err.Error(), + }) + + addFunctionResult(&totalCallDurationMetric, DCS_RESULT_TIMEOUT) + break + } + if err != nil { // stream termination in case of unknown or gRPC error + got := status.Code(err) + + if got == codes.DeadlineExceeded { + log.WithError(err). + WithField("timeout", timeout.String()). + Debug("DCS SOR timed out") + err = fmt.Errorf("DCS SOR timed out after %s: %w", timeout.String(), err) + + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_ONGOING, + OperationStep: "perform DCS call: StartOfRun", + OperationStepStatus: pb.OpStatus_DONE_TIMEOUT, + EnvironmentId: envId, + Payload: string(payloadJsonForKafka[:]), + Error: err.Error(), + }) + + addFunctionResult(&totalCallDurationMetric, DCS_RESULT_GRPC_TIMEOUT) + } else if got == codes.Unknown { // unknown error, likely not a gRPC code + logMsg := "bad DCS SOR event received, any future DCS events are ignored" + log.WithError(err). + Warn(logMsg) + + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_ONGOING, + OperationStep: "perform DCS call: StartOfRun", + OperationStepStatus: pb.OpStatus_DONE_ERROR, + EnvironmentId: envId, + Payload: string(payloadJsonForKafka[:]), + Error: logMsg, + }) + addFunctionResult(&totalCallDurationMetric, DCS_RESULT_GRPC_UNKNOWN) + } else { // some other gRPC error code + log.WithError(err). + Debug("DCS SOR call error") + err = fmt.Errorf("DCS SOR call error: %w", err) + + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_ONGOING, + OperationStep: "perform DCS call: StartOfRun", + OperationStepStatus: pb.OpStatus_DONE_ERROR, + EnvironmentId: envId, + Payload: string(payloadJsonForKafka[:]), + Error: err.Error(), + }) + addFunctionResult(&totalCallDurationMetric, DCS_RESULT_GRPC_ERROR) + } + + break + } + if dcsEvent == nil { + log.Warn("nil DCS SOR event received, skipping to next DCS event") + continue + } + if dcsEvent.GetDetector() == dcspb.Detector_DCS { + log.WithField(infologger.Level, infologger.IL_Support). + Warnf("Received an event for DCS detector (%s), which is unexpected, ignoring", dcsEvent.GetState().String()) + continue + } + + detectorStatusMap[dcsEvent.GetDetector()] = dcsEvent.GetState() + detectorDurations[dcsEvent.GetDetector()] = time.Since(start) + ecsDet := dcsToEcsDetector(dcsEvent.GetDetector()) + + if dcsEvent.GetState() == dcspb.DetectorState_SOR_FAILURE { + logErr := fmt.Errorf("%s SOR failure reported by DCS", ecsDet) + if err != nil { + logErr = fmt.Errorf("%v : %v", err, logErr) + } + log.WithError(logErr). + WithField("event", dcsEvent). + WithField("detector", ecsDet). + WithField("level", infologger.IL_Ops). + WithField("endpoint", viper.GetString("dcsServiceEndpoint")). + Error("DCS error") + + call.VarStack["__call_error_reason"] = logErr.Error() + call.VarStack["__call_error"] = callFailedStr + + payload["detector"] = ecsDet + payload["dcsEvent"] = dcsEvent + payload["state"] = dcspb.DetectorState_name[int32(dcsEvent.GetState())] + payloadJsonForKafka, _ = json.Marshal(payload) + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_ONGOING, + OperationStep: "perform DCS call: StartOfRun", + OperationStepStatus: pb.OpStatus_DONE_ERROR, + EnvironmentId: envId, + Payload: string(payloadJsonForKafka[:]), + Error: logErr.Error(), + }) + + continue + } + + if dcsEvent.GetState() == dcspb.DetectorState_SOR_UNAVAILABLE { + logErr := fmt.Errorf("%s SOR unavailable reported by DCS", ecsDet) + if err != nil { + logErr = fmt.Errorf("%v : %v", err, logErr) + } + log.WithError(logErr). + WithField("event", dcsEvent). + WithField("detector", ecsDet). + WithField("level", infologger.IL_Ops). + WithField("endpoint", viper.GetString("dcsServiceEndpoint")). + Error("DCS error") + + call.VarStack["__call_error_reason"] = logErr.Error() + call.VarStack["__call_error"] = callFailedStr + + payload["detector"] = ecsDet + payload["dcsEvent"] = dcsEvent + payload["state"] = dcspb.DetectorState_name[int32(dcsEvent.GetState())] + payloadJsonForKafka, _ = json.Marshal(payload) + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_ONGOING, + OperationStep: "perform DCS call: StartOfRun", + OperationStepStatus: pb.OpStatus_DONE_ERROR, + EnvironmentId: envId, + Payload: string(payloadJsonForKafka[:]), + Error: logErr.Error(), + }) + + continue + } + + if dcsEvent.GetState() == dcspb.DetectorState_TIMEOUT { + logErr := fmt.Errorf("%s SOR timeout reported by DCS", ecsDet) + if err != nil { + logErr = fmt.Errorf("%v : %v", err, logErr) + } + log.WithError(logErr). + WithField("event", dcsEvent). + WithField("detector", ecsDet). + WithField("level", infologger.IL_Ops). + WithField("endpoint", viper.GetString("dcsServiceEndpoint")). + Error("DCS error") + + call.VarStack["__call_error_reason"] = logErr.Error() + call.VarStack["__call_error"] = callFailedStr + + payload["detector"] = ecsDet + payload["dcsEvent"] = dcsEvent + payload["state"] = dcspb.DetectorState_name[int32(dcsEvent.GetState())] + payloadJsonForKafka, _ = json.Marshal(payload) + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_ONGOING, + OperationStep: "perform DCS call: StartOfRun", + OperationStepStatus: pb.OpStatus_DONE_TIMEOUT, + EnvironmentId: envId, + Payload: string(payloadJsonForKafka[:]), + Error: logErr.Error(), + }) + + continue + } + + if dcsEvent.GetState() == dcspb.DetectorState_RUN_OK { + log.WithField("event", dcsEvent). + WithField("level", infologger.IL_Support). + WithField("detector", dcsToEcsDetector(dcsEvent.GetDetector())). + Info("ALIECS SOR operation : completed DCS SOR for ") + + detPayload := map[string]interface{}{} + _ = copier.Copy(&detPayload, payload) + detPayload["detector"] = ecsDet + detPayload["state"] = dcspb.DetectorState_name[int32(dcsEvent.GetState())] + detPayload["dcsEvent"] = dcsEvent + detPayloadJson, _ := json.Marshal(detPayload) + + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_ONGOING, + OperationStep: "perform DCS call: StartOfRun", + OperationStepStatus: pb.OpStatus_ONGOING, + EnvironmentId: envId, + Payload: string(detPayloadJson[:]), + }) + } else { + log.WithField("event", dcsEvent). + WithField("level", infologger.IL_Devel). + WithField("detector", dcsToEcsDetector(dcsEvent.GetDetector())). + Info("ALIECS SOR operation : processing DCS SOR for ") + + ecsDet := dcsToEcsDetector(dcsEvent.GetDetector()) + detPayload := map[string]interface{}{} + _ = copier.Copy(&detPayload, payload) + detPayload["detector"] = ecsDet + detPayload["state"] = dcspb.DetectorState_name[int32(dcsEvent.GetState())] + detPayload["dcsEvent"] = dcsEvent + detPayloadJson, _ := json.Marshal(detPayload) + + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_ONGOING, + OperationStep: "perform DCS call: StartOfRun", + OperationStepStatus: pb.OpStatus_ONGOING, + EnvironmentId: envId, + Payload: string(detPayloadJson[:]), + }) + } + } + + convertAndSendDetectorDurationsAndStates(sor, detectorStatusMap, detectorDurations, envId, runType, &totalCallDurationMetric) + + return err, payloadJsonForKafka +} + +func convertAndSendDetectorDurationsAndStates(method string, detectorStatusMap map[dcspb.Detector]dcspb.DetectorState, detectorDurations map[dcspb.Detector]time.Duration, envId, runType string, totalCallMetric *monitoring.Metric) { + resultsMap := make(map[dcspb.DetectorState]int) + for dcsDet, state := range detectorStatusMap { + metric := newMetric(runType, envId, method) + det := dcsToEcsDetector(dcsDet) + metric.AddTag("detector", det) + metric.AddTag("state", dcspb.DetectorState_name[int32(state)]) + resultsMap[state] += 1 + if duration, ok := detectorDurations[dcsDet]; ok { + metric.SetFieldInt64("execution_time_ms", duration.Milliseconds()) + monitoring.Send(&metric) + } + } + for detectorState, detectorCount := range resultsMap { + totalCallMetric.SetFieldInt64(dcspb.DetectorState_name[int32(detectorState)], int64(detectorCount)) + } +} + +func addFunctionResult(metric *monitoring.Metric, result string) { + metric.AddTag("result", result) +} + +func PFRgRPCCommunicationLoop(ctx context.Context, timeout time.Duration, call *callable.Call, envId string, + payloadJsonForKafka []byte, stream dcspb.Configurator_StartOfRunClient, detectorStatusMap map[dcspb.Detector]dcspb.DetectorState, + callFailedStr string, payload map[string]interface{}, runType string, +) (error, []byte) { + pfr := "PFR" + detectorDurations := map[dcspb.Detector]time.Duration{} + start := time.Now() + + totalCallDurationMetric := newMetric(runType, envId, pfr) + totalCallDurationMetric.AddTag("detector", "All") + defer monitoring.TimerSendSingle(&totalCallDurationMetric, monitoring.Millisecond)() + + var err error + var dcsEvent *dcspb.RunEvent + for { + // fixme: consider removing this check, since `stream.Recv()` will return a timeout error anyway if the context expires + if ctx.Err() != nil { + err = fmt.Errorf("DCS PrepareForRun context timed out (%s), any future DCS events are ignored", timeout.String()) + + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_ONGOING, + OperationStep: "perform DCS call: PrepareForRun", + OperationStepStatus: pb.OpStatus_DONE_TIMEOUT, + EnvironmentId: envId, + Payload: string(payloadJsonForKafka[:]), + Error: err.Error(), + }) + + addFunctionResult(&totalCallDurationMetric, DCS_RESULT_TIMEOUT) + break + } + dcsEvent, err = stream.Recv() + if errors.Is(err, io.EOF) { // correct stream termination + log.Debug("DCS PFR event stream was closed from the DCS side (EOF)") + err = nil + + addFunctionResult(&totalCallDurationMetric, DCS_RESULT_OK) + break // no more data + } + if errors.Is(err, context.DeadlineExceeded) { + log.WithError(err). + WithField("timeout", timeout.String()). + Debug("DCS PFR timed out") + err = fmt.Errorf("DCS PFR timed out after %s: %w", timeout.String(), err) + + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_ONGOING, + OperationStep: "perform DCS call: PrepareForRun", + OperationStepStatus: pb.OpStatus_DONE_TIMEOUT, + EnvironmentId: envId, + Payload: string(payloadJsonForKafka[:]), + Error: err.Error(), + }) + + addFunctionResult(&totalCallDurationMetric, DCS_RESULT_TIMEOUT) + break + } + if err != nil { // stream termination in case of unknown or gRPC error + got := status.Code(err) + + if got == codes.DeadlineExceeded { + log.WithError(err). + WithField("timeout", timeout.String()). + Debug("DCS PFR timed out") + err = fmt.Errorf("DCS PFR timed out after %s: %w", timeout.String(), err) + + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_ONGOING, + OperationStep: "perform DCS call: PrepareForRun", + OperationStepStatus: pb.OpStatus_DONE_TIMEOUT, + EnvironmentId: envId, + Payload: string(payloadJsonForKafka[:]), + Error: err.Error(), + }) + addFunctionResult(&totalCallDurationMetric, DCS_RESULT_GRPC_TIMEOUT) + } else if got == codes.Unknown { // unknown error, likely not a gRPC code + logMsg := "bad DCS PFR event received, any future DCS events are ignored" + log.WithError(err). + WithField("partition", envId). + Warn(logMsg) + + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_ONGOING, + OperationStep: "perform DCS call: PrepareForRun", + OperationStepStatus: pb.OpStatus_DONE_ERROR, + EnvironmentId: envId, + Payload: string(payloadJsonForKafka[:]), + Error: logMsg, + }) + addFunctionResult(&totalCallDurationMetric, DCS_RESULT_GRPC_UNKNOWN) + } else { // some other gRPC error code + log.WithError(err). + Error("DCS PFR call error") + err = fmt.Errorf("DCS PFR call error: %w", err) + + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_ONGOING, + OperationStep: "perform DCS call: PrepareForRun", + OperationStepStatus: pb.OpStatus_DONE_ERROR, + EnvironmentId: envId, + Payload: string(payloadJsonForKafka[:]), + Error: err.Error(), + }) + addFunctionResult(&totalCallDurationMetric, DCS_RESULT_GRPC_ERROR) + } + + break + } + if dcsEvent == nil { + log.WithField("partition", envId). + Warn("nil DCS PFR event received, skipping to next DCS event") + continue + } + if dcsEvent.GetDetector() == dcspb.Detector_DCS { + log.WithField(infologger.Level, infologger.IL_Support). + Warnf("Received an event for DCS detector (%s), which is unexpected, ignoring", dcsEvent.GetState().String()) + continue + } + + detectorStatusMap[dcsEvent.GetDetector()] = dcsEvent.GetState() + detectorDurations[dcsEvent.GetDetector()] = time.Since(start) + ecsDet := dcsToEcsDetector(dcsEvent.GetDetector()) + + if dcsEvent.GetState() == dcspb.DetectorState_SOR_FAILURE { + logErr := fmt.Errorf("%s PFR failure reported by DCS", ecsDet) + if err != nil { + logErr = fmt.Errorf("%v : %v", err, logErr) + } + log.WithError(logErr). + WithField("event", dcsEvent). + WithField("detector", ecsDet). + WithField("level", infologger.IL_Ops). + WithField("endpoint", viper.GetString("dcsServiceEndpoint")). + Error("DCS error") + + call.VarStack["__call_error_reason"] = logErr.Error() + call.VarStack["__call_error"] = callFailedStr + + payload["detector"] = ecsDet + payload["dcsEvent"] = dcsEvent + payload["state"] = dcspb.DetectorState_name[int32(dcsEvent.GetState())] + payloadJsonForKafka, _ = json.Marshal(payload) + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_ONGOING, + OperationStep: "perform DCS call: PrepareForRun", + OperationStepStatus: pb.OpStatus_DONE_ERROR, + EnvironmentId: envId, + Payload: string(payloadJsonForKafka[:]), + Error: logErr.Error(), + }) + + continue + } + + if dcsEvent.GetState() == dcspb.DetectorState_PFR_UNAVAILABLE { + logErr := fmt.Errorf("%s PFR unavailable reported by DCS", ecsDet) + if err != nil { + logErr = fmt.Errorf("%v : %v", err, logErr) + } + log.WithError(logErr). + WithField("event", dcsEvent). + WithField("detector", ecsDet). + WithField("level", infologger.IL_Ops). + WithField("endpoint", viper.GetString("dcsServiceEndpoint")). + Error("DCS error") + + call.VarStack["__call_error_reason"] = logErr.Error() + call.VarStack["__call_error"] = callFailedStr + + payload["detector"] = ecsDet + payload["dcsEvent"] = dcsEvent + payload["state"] = dcspb.DetectorState_name[int32(dcsEvent.GetState())] + payloadJsonForKafka, _ = json.Marshal(payload) + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_ONGOING, + OperationStep: "perform DCS call: PrepareForRun", + OperationStepStatus: pb.OpStatus_DONE_ERROR, + EnvironmentId: envId, + Payload: string(payloadJsonForKafka[:]), + Error: logErr.Error(), + }) + + continue + } + + if dcsEvent.GetState() == dcspb.DetectorState_TIMEOUT { + logErr := fmt.Errorf("%s PFR timeout reported by DCS", ecsDet) + if err != nil { + logErr = fmt.Errorf("%v : %v", err, logErr) + } + log.WithError(logErr). + WithField("event", dcsEvent). + WithField("detector", ecsDet). + WithField("level", infologger.IL_Ops). + WithField("endpoint", viper.GetString("dcsServiceEndpoint")). + Error("DCS error") + + call.VarStack["__call_error_reason"] = logErr.Error() + call.VarStack["__call_error"] = callFailedStr + + payload["detector"] = ecsDet + payload["dcsEvent"] = dcsEvent + payload["state"] = dcspb.DetectorState_name[int32(dcsEvent.GetState())] + payloadJsonForKafka, _ = json.Marshal(payload) + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_ONGOING, + OperationStep: "perform DCS call: PrepareForRun", + OperationStepStatus: pb.OpStatus_DONE_TIMEOUT, + EnvironmentId: envId, + Payload: string(payloadJsonForKafka[:]), + Error: logErr.Error(), + }) + + continue + } + + if dcsEvent.GetState() == dcspb.DetectorState_RUN_OK { + log.WithField("event", dcsEvent). + WithField("level", infologger.IL_Support). + WithField("detector", ecsDet). + Info("ALIECS PFR operation : completed DCS PFR for ") + + detPayload := map[string]interface{}{} + _ = copier.Copy(&detPayload, payload) + detPayload["detector"] = ecsDet + detPayload["state"] = dcspb.DetectorState_name[int32(dcsEvent.GetState())] + detPayload["dcsEvent"] = dcsEvent + detPayloadJson, _ := json.Marshal(detPayload) + + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_ONGOING, + OperationStep: "perform DCS call: PrepareForRun", + OperationStepStatus: pb.OpStatus_ONGOING, + EnvironmentId: envId, + Payload: string(detPayloadJson[:]), + }) + } else { + log.WithField("event", dcsEvent). + WithField("level", infologger.IL_Devel). + WithField("detector", ecsDet). + Info("ALIECS PFR operation : processing DCS PFR for ") + + detPayload := map[string]interface{}{} + _ = copier.Copy(&detPayload, payload) + detPayload["detector"] = ecsDet + detPayload["state"] = dcspb.DetectorState_name[int32(dcsEvent.GetState())] + detPayload["dcsEvent"] = dcsEvent + detPayloadJson, _ := json.Marshal(detPayload) + + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_ONGOING, + OperationStep: "perform DCS call: PrepareForRun", + OperationStepStatus: pb.OpStatus_ONGOING, + EnvironmentId: envId, + Payload: string(detPayloadJson[:]), + }) + } + } + + convertAndSendDetectorDurationsAndStates(pfr, detectorStatusMap, detectorDurations, envId, runType, &totalCallDurationMetric) + + return err, payloadJsonForKafka +} + +func (p *Plugin) parseDetectors(dcsDetectorsParam string) (detectors DCSDetectors, err error) { + detectorsSlice := make([]string, 0) + bytes := []byte(dcsDetectorsParam) + err = json.Unmarshal(bytes, &detectorsSlice) + if err != nil { + log.WithError(err). + Error("error processing DCS detectors list") + return + } + + // Now we process the stringSlice into a slice of detector enum values + detectors = make(DCSDetectors, len(detectorsSlice)) + for i, det := range detectorsSlice { + dcsDetector := dcspb.Detector_NULL_DETECTOR + dcsDetector, err = ecsToDcsDetector(det) + if err != nil { + return + } + + // detector string correctly matched to DCS enum + detectors[i] = dcsDetector + } + return +} + +func (p *Plugin) Destroy() error { + return p.dcsClient.Close() +} diff --git a/core/integration/dcs/protos/dcs.pb.go b/core/integration/dcs/protos/dcs.pb.go new file mode 100644 index 000000000..a7607b9aa --- /dev/null +++ b/core/integration/dcs/protos/dcs.pb.go @@ -0,0 +1,1582 @@ +// +// === This file is part of ALICE O² === +// +// Copyright 2020-2021 CERN and copyright holders of ALICE O². +// Authors: Teo Mrnjavac +// Ombretta Pinazza +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// In applying this license CERN does not waive the privileges and +// immunities granted to it by virtue of its status as an +// Intergovernmental Organization or submit itself to any jurisdiction. + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.34.1 +// protoc v5.29.3 +// source: protos/dcs.proto + +package dcspb + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type EventType int32 + +const ( + EventType_NULL_EVENT EventType = 0 + EventType_HEARTBEAT EventType = 1 + EventType_STATE_CHANGE_EVENT EventType = 2 + EventType_ERROR_EVENT EventType = 3 + EventType_ACK_EVENT EventType = 4 + EventType_SOR_EVENT EventType = 20 + EventType_EOR_EVENT EventType = 30 + EventType_EVENT_TIMEOUT EventType = 40 + EventType_PFR_EVENT EventType = 50 +) + +// Enum value maps for EventType. +var ( + EventType_name = map[int32]string{ + 0: "NULL_EVENT", + 1: "HEARTBEAT", + 2: "STATE_CHANGE_EVENT", + 3: "ERROR_EVENT", + 4: "ACK_EVENT", + 20: "SOR_EVENT", + 30: "EOR_EVENT", + 40: "EVENT_TIMEOUT", + 50: "PFR_EVENT", + } + EventType_value = map[string]int32{ + "NULL_EVENT": 0, + "HEARTBEAT": 1, + "STATE_CHANGE_EVENT": 2, + "ERROR_EVENT": 3, + "ACK_EVENT": 4, + "SOR_EVENT": 20, + "EOR_EVENT": 30, + "EVENT_TIMEOUT": 40, + "PFR_EVENT": 50, + } +) + +func (x EventType) Enum() *EventType { + p := new(EventType) + *p = x + return p +} + +func (x EventType) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (EventType) Descriptor() protoreflect.EnumDescriptor { + return file_protos_dcs_proto_enumTypes[0].Descriptor() +} + +func (EventType) Type() protoreflect.EnumType { + return &file_protos_dcs_proto_enumTypes[0] +} + +func (x EventType) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use EventType.Descriptor instead. +func (EventType) EnumDescriptor() ([]byte, []int) { + return file_protos_dcs_proto_rawDescGZIP(), []int{0} +} + +type Detector int32 + +const ( + Detector_NULL_DETECTOR Detector = 0 + Detector_EMC Detector = 2 + Detector_FDD Detector = 3 + Detector_FT0 Detector = 4 + Detector_FV0 Detector = 5 + Detector_ITS Detector = 6 + Detector_HMP Detector = 7 + Detector_MCH Detector = 8 + Detector_MFT Detector = 9 + Detector_MID Detector = 10 + Detector_TOF Detector = 12 + Detector_TPC Detector = 13 + Detector_TRD Detector = 14 + Detector_ZDC Detector = 15 + Detector_LHC Detector = 16 + Detector_DCS Detector = 17 + Detector_AGD Detector = 18 +) + +// Enum value maps for Detector. +var ( + Detector_name = map[int32]string{ + 0: "NULL_DETECTOR", + 2: "EMC", + 3: "FDD", + 4: "FT0", + 5: "FV0", + 6: "ITS", + 7: "HMP", + 8: "MCH", + 9: "MFT", + 10: "MID", + 12: "TOF", + 13: "TPC", + 14: "TRD", + 15: "ZDC", + 16: "LHC", + 17: "DCS", + 18: "AGD", + } + Detector_value = map[string]int32{ + "NULL_DETECTOR": 0, + "EMC": 2, + "FDD": 3, + "FT0": 4, + "FV0": 5, + "ITS": 6, + "HMP": 7, + "MCH": 8, + "MFT": 9, + "MID": 10, + "TOF": 12, + "TPC": 13, + "TRD": 14, + "ZDC": 15, + "LHC": 16, + "DCS": 17, + "AGD": 18, + } +) + +func (x Detector) Enum() *Detector { + p := new(Detector) + *p = x + return p +} + +func (x Detector) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (Detector) Descriptor() protoreflect.EnumDescriptor { + return file_protos_dcs_proto_enumTypes[1].Descriptor() +} + +func (Detector) Type() protoreflect.EnumType { + return &file_protos_dcs_proto_enumTypes[1] +} + +func (x Detector) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use Detector.Descriptor instead. +func (Detector) EnumDescriptor() ([]byte, []int) { + return file_protos_dcs_proto_rawDescGZIP(), []int{1} +} + +type DetectorState int32 + +const ( + DetectorState_NULL_STATE DetectorState = 0 + DetectorState_READY DetectorState = 1 + DetectorState_RUN_OK DetectorState = 2 + DetectorState_RUN_FAILURE DetectorState = 3 + DetectorState_RUN_INHIBIT DetectorState = 4 + DetectorState_SOR_PROGRESSING DetectorState = 5 + DetectorState_EOR_PROGRESSING DetectorState = 6 + DetectorState_SOR_FAILURE DetectorState = 7 + DetectorState_EOR_FAILURE DetectorState = 8 + DetectorState_ERROR DetectorState = 9 + DetectorState_DEAD DetectorState = 10 + DetectorState_SOR_UNAVAILABLE DetectorState = 11 + DetectorState_EOR_UNAVAILABLE DetectorState = 12 + DetectorState_PREPARING DetectorState = 13 + DetectorState_SOR_AVAILABLE DetectorState = 14 + DetectorState_EOR_AVAILABLE DetectorState = 15 + DetectorState_PFR_AVAILABLE DetectorState = 16 + DetectorState_PFR_UNAVAILABLE DetectorState = 17 + DetectorState_TIMEOUT DetectorState = 18 +) + +// Enum value maps for DetectorState. +var ( + DetectorState_name = map[int32]string{ + 0: "NULL_STATE", + 1: "READY", + 2: "RUN_OK", + 3: "RUN_FAILURE", + 4: "RUN_INHIBIT", + 5: "SOR_PROGRESSING", + 6: "EOR_PROGRESSING", + 7: "SOR_FAILURE", + 8: "EOR_FAILURE", + 9: "ERROR", + 10: "DEAD", + 11: "SOR_UNAVAILABLE", + 12: "EOR_UNAVAILABLE", + 13: "PREPARING", + 14: "SOR_AVAILABLE", + 15: "EOR_AVAILABLE", + 16: "PFR_AVAILABLE", + 17: "PFR_UNAVAILABLE", + 18: "TIMEOUT", + } + DetectorState_value = map[string]int32{ + "NULL_STATE": 0, + "READY": 1, + "RUN_OK": 2, + "RUN_FAILURE": 3, + "RUN_INHIBIT": 4, + "SOR_PROGRESSING": 5, + "EOR_PROGRESSING": 6, + "SOR_FAILURE": 7, + "EOR_FAILURE": 8, + "ERROR": 9, + "DEAD": 10, + "SOR_UNAVAILABLE": 11, + "EOR_UNAVAILABLE": 12, + "PREPARING": 13, + "SOR_AVAILABLE": 14, + "EOR_AVAILABLE": 15, + "PFR_AVAILABLE": 16, + "PFR_UNAVAILABLE": 17, + "TIMEOUT": 18, + } +) + +func (x DetectorState) Enum() *DetectorState { + p := new(DetectorState) + *p = x + return p +} + +func (x DetectorState) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (DetectorState) Descriptor() protoreflect.EnumDescriptor { + return file_protos_dcs_proto_enumTypes[2].Descriptor() +} + +func (DetectorState) Type() protoreflect.EnumType { + return &file_protos_dcs_proto_enumTypes[2] +} + +func (x DetectorState) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use DetectorState.Descriptor instead. +func (DetectorState) EnumDescriptor() ([]byte, []int) { + return file_protos_dcs_proto_rawDescGZIP(), []int{2} +} + +type RunType int32 + +const ( + RunType_NULL_RUNTYPE RunType = 0 + RunType_PHYSICS RunType = 1 + RunType_TECHNICAL RunType = 2 + RunType_PEDESTALS RunType = 3 + RunType_PULSER RunType = 4 + RunType_LASER RunType = 5 + RunType_CALIBRATION_ITHR_TUNING RunType = 6 + RunType_CALIBRATION_VCASN_TUNING RunType = 7 + RunType_CALIBRATION_THR_SCAN RunType = 8 + RunType_CALIBRATION_DIGITAL_SCAN RunType = 9 + RunType_CALIBRATION_ANALOG_SCAN RunType = 10 + RunType_CALIBRATION_FHR RunType = 11 + RunType_CALIBRATION_ALPIDE_SCAN RunType = 12 + RunType_NOISE RunType = 16 + RunType_CALIBRATION_PULSE_LENGTH RunType = 17 + RunType_CALIBRATION_VRESETD RunType = 18 + RunType_ANY RunType = 300 +) + +// Enum value maps for RunType. +var ( + RunType_name = map[int32]string{ + 0: "NULL_RUNTYPE", + 1: "PHYSICS", + 2: "TECHNICAL", + 3: "PEDESTALS", + 4: "PULSER", + 5: "LASER", + 6: "CALIBRATION_ITHR_TUNING", + 7: "CALIBRATION_VCASN_TUNING", + 8: "CALIBRATION_THR_SCAN", + 9: "CALIBRATION_DIGITAL_SCAN", + 10: "CALIBRATION_ANALOG_SCAN", + 11: "CALIBRATION_FHR", + 12: "CALIBRATION_ALPIDE_SCAN", + 16: "NOISE", + 17: "CALIBRATION_PULSE_LENGTH", + 18: "CALIBRATION_VRESETD", + 300: "ANY", + } + RunType_value = map[string]int32{ + "NULL_RUNTYPE": 0, + "PHYSICS": 1, + "TECHNICAL": 2, + "PEDESTALS": 3, + "PULSER": 4, + "LASER": 5, + "CALIBRATION_ITHR_TUNING": 6, + "CALIBRATION_VCASN_TUNING": 7, + "CALIBRATION_THR_SCAN": 8, + "CALIBRATION_DIGITAL_SCAN": 9, + "CALIBRATION_ANALOG_SCAN": 10, + "CALIBRATION_FHR": 11, + "CALIBRATION_ALPIDE_SCAN": 12, + "NOISE": 16, + "CALIBRATION_PULSE_LENGTH": 17, + "CALIBRATION_VRESETD": 18, + "ANY": 300, + } +) + +func (x RunType) Enum() *RunType { + p := new(RunType) + *p = x + return p +} + +func (x RunType) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (RunType) Descriptor() protoreflect.EnumDescriptor { + return file_protos_dcs_proto_enumTypes[3].Descriptor() +} + +func (RunType) Type() protoreflect.EnumType { + return &file_protos_dcs_proto_enumTypes[3] +} + +func (x RunType) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use RunType.Descriptor instead. +func (RunType) EnumDescriptor() ([]byte, []int) { + return file_protos_dcs_proto_rawDescGZIP(), []int{3} +} + +type TriggerMode int32 + +const ( + TriggerMode_CONTINUOUS TriggerMode = 0 + TriggerMode_TRIGGERED TriggerMode = 1 +) + +// Enum value maps for TriggerMode. +var ( + TriggerMode_name = map[int32]string{ + 0: "CONTINUOUS", + 1: "TRIGGERED", + } + TriggerMode_value = map[string]int32{ + "CONTINUOUS": 0, + "TRIGGERED": 1, + } +) + +func (x TriggerMode) Enum() *TriggerMode { + p := new(TriggerMode) + *p = x + return p +} + +func (x TriggerMode) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (TriggerMode) Descriptor() protoreflect.EnumDescriptor { + return file_protos_dcs_proto_enumTypes[4].Descriptor() +} + +func (TriggerMode) Type() protoreflect.EnumType { + return &file_protos_dcs_proto_enumTypes[4] +} + +func (x TriggerMode) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use TriggerMode.Descriptor instead. +func (TriggerMode) EnumDescriptor() ([]byte, []int) { + return file_protos_dcs_proto_rawDescGZIP(), []int{4} +} + +type SorMode int32 + +const ( + SorMode_FULL SorMode = 0 + SorMode_FAST SorMode = 1 +) + +// Enum value maps for SorMode. +var ( + SorMode_name = map[int32]string{ + 0: "FULL", + 1: "FAST", + } + SorMode_value = map[string]int32{ + "FULL": 0, + "FAST": 1, + } +) + +func (x SorMode) Enum() *SorMode { + p := new(SorMode) + *p = x + return p +} + +func (x SorMode) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (SorMode) Descriptor() protoreflect.EnumDescriptor { + return file_protos_dcs_proto_enumTypes[5].Descriptor() +} + +func (SorMode) Type() protoreflect.EnumType { + return &file_protos_dcs_proto_enumTypes[5] +} + +func (x SorMode) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use SorMode.Descriptor instead. +func (SorMode) EnumDescriptor() ([]byte, []int) { + return file_protos_dcs_proto_rawDescGZIP(), []int{5} +} + +// A DetectorInfo is a full status report for a single detector, multiple such +// payloads are then aggregated in a StatusReply or Subscribe Event. +type DetectorInfo struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Detector Detector `protobuf:"varint,1,opt,name=detector,proto3,enum=dcs.Detector" json:"detector,omitempty"` // 1 detector per DetectorInfo + State DetectorState `protobuf:"varint,2,opt,name=state,proto3,enum=dcs.DetectorState" json:"state,omitempty"` // state of this detector at the time of response generation + Timestamp string `protobuf:"bytes,3,opt,name=timestamp,proto3" json:"timestamp,omitempty"` // time when the detector state is as declared + AllowedRunTypes []RunType `protobuf:"varint,4,rep,packed,name=allowedRunTypes,proto3,enum=dcs.RunType" json:"allowedRunTypes,omitempty"` // a list of run types that are currently + // possible with the given detector, taking into + // account all low-level information; + // for example, at a given moment TECHNICAL + // runs might be possible but not PHYSICS + PfrAvailability DetectorState `protobuf:"varint,5,opt,name=pfrAvailability,proto3,enum=dcs.DetectorState" json:"pfrAvailability,omitempty"` // availability of this detector for PFR at the time of response generation + SorAvailability DetectorState `protobuf:"varint,6,opt,name=sorAvailability,proto3,enum=dcs.DetectorState" json:"sorAvailability,omitempty"` // availability of this detector for SOR at the time of response generation +} + +func (x *DetectorInfo) Reset() { + *x = DetectorInfo{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_dcs_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DetectorInfo) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DetectorInfo) ProtoMessage() {} + +func (x *DetectorInfo) ProtoReflect() protoreflect.Message { + mi := &file_protos_dcs_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DetectorInfo.ProtoReflect.Descriptor instead. +func (*DetectorInfo) Descriptor() ([]byte, []int) { + return file_protos_dcs_proto_rawDescGZIP(), []int{0} +} + +func (x *DetectorInfo) GetDetector() Detector { + if x != nil { + return x.Detector + } + return Detector_NULL_DETECTOR +} + +func (x *DetectorInfo) GetState() DetectorState { + if x != nil { + return x.State + } + return DetectorState_NULL_STATE +} + +func (x *DetectorInfo) GetTimestamp() string { + if x != nil { + return x.Timestamp + } + return "" +} + +func (x *DetectorInfo) GetAllowedRunTypes() []RunType { + if x != nil { + return x.AllowedRunTypes + } + return nil +} + +func (x *DetectorInfo) GetPfrAvailability() DetectorState { + if x != nil { + return x.PfrAvailability + } + return DetectorState_NULL_STATE +} + +func (x *DetectorInfo) GetSorAvailability() DetectorState { + if x != nil { + return x.SorAvailability + } + return DetectorState_NULL_STATE +} + +type SubscriptionRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + InstanceId string `protobuf:"bytes,1,opt,name=instanceId,proto3" json:"instanceId,omitempty"` +} + +func (x *SubscriptionRequest) Reset() { + *x = SubscriptionRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_dcs_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SubscriptionRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SubscriptionRequest) ProtoMessage() {} + +func (x *SubscriptionRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_dcs_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SubscriptionRequest.ProtoReflect.Descriptor instead. +func (*SubscriptionRequest) Descriptor() ([]byte, []int) { + return file_protos_dcs_proto_rawDescGZIP(), []int{1} +} + +func (x *SubscriptionRequest) GetInstanceId() string { + if x != nil { + return x.InstanceId + } + return "" +} + +// The Subscribe response stream returns Event payloads: +// 1. as needed, Events of type ERROR_EVENT, TIMEOUT, etc. when something unexpected +// happens, especially outside of a SOR or EOR operation (which are already served +// by operation-specific streams) +// 2. periodically, Events of type HEARTBEAT with a full detectorMatrix to republish +// the full status of all detectors in the system +// 3. as needed, Events of type STATE_CHANGE_EVENT and similar, with a partial +// detectorMatrix that only reflects the changes in some detectors, usually when +// the internal state of a detector has changed on the DCS side in some way that +// makes a new RunType possible, or that inhibits a RunType that was previously +// possible and now isn't any more. +type Event struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Eventtype EventType `protobuf:"varint,1,opt,name=eventtype,proto3,enum=dcs.EventType" json:"eventtype,omitempty"` // type of event being published by the DCS service + DetectorMatrix []*DetectorInfo `protobuf:"bytes,2,rep,name=detectorMatrix,proto3" json:"detectorMatrix,omitempty"` // detector-specific status payloads (optional depending + // on event type) + ExtraParameters map[string]string `protobuf:"bytes,3,rep,name=extraParameters,proto3" json:"extraParameters,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` // general key-value map just in case + Timestamp string `protobuf:"bytes,4,opt,name=timestamp,proto3" json:"timestamp,omitempty"` // this corresponds to the moment where this Event-answer + // was valid from the source + Message string `protobuf:"bytes,5,opt,name=message,proto3" json:"message,omitempty"` // optional user-facing error message, if the DCS wants to +} + +func (x *Event) Reset() { + *x = Event{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_dcs_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Event) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Event) ProtoMessage() {} + +func (x *Event) ProtoReflect() protoreflect.Message { + mi := &file_protos_dcs_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Event.ProtoReflect.Descriptor instead. +func (*Event) Descriptor() ([]byte, []int) { + return file_protos_dcs_proto_rawDescGZIP(), []int{2} +} + +func (x *Event) GetEventtype() EventType { + if x != nil { + return x.Eventtype + } + return EventType_NULL_EVENT +} + +func (x *Event) GetDetectorMatrix() []*DetectorInfo { + if x != nil { + return x.DetectorMatrix + } + return nil +} + +func (x *Event) GetExtraParameters() map[string]string { + if x != nil { + return x.ExtraParameters + } + return nil +} + +func (x *Event) GetTimestamp() string { + if x != nil { + return x.Timestamp + } + return "" +} + +func (x *Event) GetMessage() string { + if x != nil { + return x.Message + } + return "" +} + +// Each PfrRequest/SorRequest/EorRequest contains multiple DetectorOperationRequests, one per detector. +// This is needed in order to pass per-detector parameters within a single PFR/SOR/EOR operation. +type DetectorOperationRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Detector Detector `protobuf:"varint,1,opt,name=detector,proto3,enum=dcs.Detector" json:"detector,omitempty"` + ExtraParameters map[string]string `protobuf:"bytes,2,rep,name=extraParameters,proto3" json:"extraParameters,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` // detector-specific parameters +} + +func (x *DetectorOperationRequest) Reset() { + *x = DetectorOperationRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_dcs_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DetectorOperationRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DetectorOperationRequest) ProtoMessage() {} + +func (x *DetectorOperationRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_dcs_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DetectorOperationRequest.ProtoReflect.Descriptor instead. +func (*DetectorOperationRequest) Descriptor() ([]byte, []int) { + return file_protos_dcs_proto_rawDescGZIP(), []int{3} +} + +func (x *DetectorOperationRequest) GetDetector() Detector { + if x != nil { + return x.Detector + } + return Detector_NULL_DETECTOR +} + +func (x *DetectorOperationRequest) GetExtraParameters() map[string]string { + if x != nil { + return x.ExtraParameters + } + return nil +} + +// A PfrRequest encapsulates a list of detector-specific request payloads, +// plus a common runType any future runs. +type PfrRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Detectors []*DetectorOperationRequest `protobuf:"bytes,1,rep,name=detectors,proto3" json:"detectors,omitempty"` + RunType RunType `protobuf:"varint,2,opt,name=runType,proto3,enum=dcs.RunType" json:"runType,omitempty"` // default: NULL_RUNTYPE, should probably never be sent + PartitionId string `protobuf:"bytes,3,opt,name=partitionId,proto3" json:"partitionId,omitempty"` // unique id for a pfr request +} + +func (x *PfrRequest) Reset() { + *x = PfrRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_dcs_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PfrRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PfrRequest) ProtoMessage() {} + +func (x *PfrRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_dcs_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PfrRequest.ProtoReflect.Descriptor instead. +func (*PfrRequest) Descriptor() ([]byte, []int) { + return file_protos_dcs_proto_rawDescGZIP(), []int{4} +} + +func (x *PfrRequest) GetDetectors() []*DetectorOperationRequest { + if x != nil { + return x.Detectors + } + return nil +} + +func (x *PfrRequest) GetRunType() RunType { + if x != nil { + return x.RunType + } + return RunType_NULL_RUNTYPE +} + +func (x *PfrRequest) GetPartitionId() string { + if x != nil { + return x.PartitionId + } + return "" +} + +// A SorRequest encapsulates a list of detector-specific request payloads, +// plus a common runType and a runNumber for the run about to start. +type SorRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Detectors []*DetectorOperationRequest `protobuf:"bytes,1,rep,name=detectors,proto3" json:"detectors,omitempty"` + RunType RunType `protobuf:"varint,2,opt,name=runType,proto3,enum=dcs.RunType" json:"runType,omitempty"` // default: NULL_RUNTYPE, should probably never be sent + RunNumber int32 `protobuf:"varint,3,opt,name=runNumber,proto3" json:"runNumber,omitempty"` +} + +func (x *SorRequest) Reset() { + *x = SorRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_dcs_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SorRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SorRequest) ProtoMessage() {} + +func (x *SorRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_dcs_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SorRequest.ProtoReflect.Descriptor instead. +func (*SorRequest) Descriptor() ([]byte, []int) { + return file_protos_dcs_proto_rawDescGZIP(), []int{5} +} + +func (x *SorRequest) GetDetectors() []*DetectorOperationRequest { + if x != nil { + return x.Detectors + } + return nil +} + +func (x *SorRequest) GetRunType() RunType { + if x != nil { + return x.RunType + } + return RunType_NULL_RUNTYPE +} + +func (x *SorRequest) GetRunNumber() int32 { + if x != nil { + return x.RunNumber + } + return 0 +} + +// An EorRequest encapsulates a list of detector-specific request payloads, +// plus a runNumber to identify the run being stopped. +type EorRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Detectors []*DetectorOperationRequest `protobuf:"bytes,1,rep,name=detectors,proto3" json:"detectors,omitempty"` + RunNumber int32 `protobuf:"varint,2,opt,name=runNumber,proto3" json:"runNumber,omitempty"` +} + +func (x *EorRequest) Reset() { + *x = EorRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_dcs_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *EorRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*EorRequest) ProtoMessage() {} + +func (x *EorRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_dcs_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use EorRequest.ProtoReflect.Descriptor instead. +func (*EorRequest) Descriptor() ([]byte, []int) { + return file_protos_dcs_proto_rawDescGZIP(), []int{6} +} + +func (x *EorRequest) GetDetectors() []*DetectorOperationRequest { + if x != nil { + return x.Detectors + } + return nil +} + +func (x *EorRequest) GetRunNumber() int32 { + if x != nil { + return x.RunNumber + } + return 0 +} + +// A stream of Event objects is returned by PFR, SOR and EOR calls. +// Most fields are optional and filled as needed, depending on the type of request. +type RunEvent struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Eventtype EventType `protobuf:"varint,1,opt,name=eventtype,proto3,enum=dcs.EventType" json:"eventtype,omitempty"` // type of event being published by the DCS service + Detector Detector `protobuf:"varint,2,opt,name=detector,proto3,enum=dcs.Detector" json:"detector,omitempty"` // detector that produced the event + State DetectorState `protobuf:"varint,3,opt,name=state,proto3,enum=dcs.DetectorState" json:"state,omitempty"` // aggregate state for all detectors involved in current + // PFR/SOR/EOR operation + ExtraParameters map[string]string `protobuf:"bytes,4,rep,name=extraParameters,proto3" json:"extraParameters,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Timestamp string `protobuf:"bytes,5,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + Message string `protobuf:"bytes,6,opt,name=message,proto3" json:"message,omitempty"` // optional user-facing error message, if the DCS wants to +} + +func (x *RunEvent) Reset() { + *x = RunEvent{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_dcs_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RunEvent) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RunEvent) ProtoMessage() {} + +func (x *RunEvent) ProtoReflect() protoreflect.Message { + mi := &file_protos_dcs_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RunEvent.ProtoReflect.Descriptor instead. +func (*RunEvent) Descriptor() ([]byte, []int) { + return file_protos_dcs_proto_rawDescGZIP(), []int{7} +} + +func (x *RunEvent) GetEventtype() EventType { + if x != nil { + return x.Eventtype + } + return EventType_NULL_EVENT +} + +func (x *RunEvent) GetDetector() Detector { + if x != nil { + return x.Detector + } + return Detector_NULL_DETECTOR +} + +func (x *RunEvent) GetState() DetectorState { + if x != nil { + return x.State + } + return DetectorState_NULL_STATE +} + +func (x *RunEvent) GetExtraParameters() map[string]string { + if x != nil { + return x.ExtraParameters + } + return nil +} + +func (x *RunEvent) GetTimestamp() string { + if x != nil { + return x.Timestamp + } + return "" +} + +func (x *RunEvent) GetMessage() string { + if x != nil { + return x.Message + } + return "" +} + +// Payload for the GetStatus call, just a list of detectors to query. +type StatusRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Detector []Detector `protobuf:"varint,1,rep,packed,name=detector,proto3,enum=dcs.Detector" json:"detector,omitempty"` +} + +func (x *StatusRequest) Reset() { + *x = StatusRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_dcs_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StatusRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StatusRequest) ProtoMessage() {} + +func (x *StatusRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_dcs_proto_msgTypes[8] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StatusRequest.ProtoReflect.Descriptor instead. +func (*StatusRequest) Descriptor() ([]byte, []int) { + return file_protos_dcs_proto_rawDescGZIP(), []int{8} +} + +func (x *StatusRequest) GetDetector() []Detector { + if x != nil { + return x.Detector + } + return nil +} + +// Aggregate response to a GetStatus call, wraps around a list of DetectorInfo +// payloads. +type StatusReply struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + DetectorMatrix []*DetectorInfo `protobuf:"bytes,1,rep,name=detectorMatrix,proto3" json:"detectorMatrix,omitempty"` // an exhaustive list of DetectorInfos +} + +func (x *StatusReply) Reset() { + *x = StatusReply{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_dcs_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StatusReply) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StatusReply) ProtoMessage() {} + +func (x *StatusReply) ProtoReflect() protoreflect.Message { + mi := &file_protos_dcs_proto_msgTypes[9] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StatusReply.ProtoReflect.Descriptor instead. +func (*StatusReply) Descriptor() ([]byte, []int) { + return file_protos_dcs_proto_rawDescGZIP(), []int{9} +} + +func (x *StatusReply) GetDetectorMatrix() []*DetectorInfo { + if x != nil { + return x.DetectorMatrix + } + return nil +} + +var File_protos_dcs_proto protoreflect.FileDescriptor + +var file_protos_dcs_proto_rawDesc = []byte{ + 0x0a, 0x10, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x64, 0x63, 0x73, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x12, 0x03, 0x64, 0x63, 0x73, 0x22, 0xb5, 0x02, 0x0a, 0x0c, 0x44, 0x65, 0x74, 0x65, + 0x63, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x29, 0x0a, 0x08, 0x64, 0x65, 0x74, 0x65, + 0x63, 0x74, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0d, 0x2e, 0x64, 0x63, 0x73, + 0x2e, 0x44, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, 0x08, 0x64, 0x65, 0x74, 0x65, 0x63, + 0x74, 0x6f, 0x72, 0x12, 0x28, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0e, 0x32, 0x12, 0x2e, 0x64, 0x63, 0x73, 0x2e, 0x44, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, + 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1c, 0x0a, + 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x36, 0x0a, 0x0f, 0x61, + 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x52, 0x75, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x73, 0x18, 0x04, + 0x20, 0x03, 0x28, 0x0e, 0x32, 0x0c, 0x2e, 0x64, 0x63, 0x73, 0x2e, 0x52, 0x75, 0x6e, 0x54, 0x79, + 0x70, 0x65, 0x52, 0x0f, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x52, 0x75, 0x6e, 0x54, 0x79, + 0x70, 0x65, 0x73, 0x12, 0x3c, 0x0a, 0x0f, 0x70, 0x66, 0x72, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, + 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x12, 0x2e, 0x64, + 0x63, 0x73, 0x2e, 0x44, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, + 0x52, 0x0f, 0x70, 0x66, 0x72, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, + 0x79, 0x12, 0x3c, 0x0a, 0x0f, 0x73, 0x6f, 0x72, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x69, + 0x6c, 0x69, 0x74, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x12, 0x2e, 0x64, 0x63, 0x73, + 0x2e, 0x44, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x0f, + 0x73, 0x6f, 0x72, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x22, + 0x35, 0x0a, 0x13, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, + 0x63, 0x65, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x69, 0x6e, 0x73, 0x74, + 0x61, 0x6e, 0x63, 0x65, 0x49, 0x64, 0x22, 0xb7, 0x02, 0x0a, 0x05, 0x45, 0x76, 0x65, 0x6e, 0x74, + 0x12, 0x2c, 0x0a, 0x09, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0e, 0x32, 0x0e, 0x2e, 0x64, 0x63, 0x73, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, + 0x79, 0x70, 0x65, 0x52, 0x09, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x74, 0x79, 0x70, 0x65, 0x12, 0x39, + 0x0a, 0x0e, 0x64, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x4d, 0x61, 0x74, 0x72, 0x69, 0x78, + 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x64, 0x63, 0x73, 0x2e, 0x44, 0x65, 0x74, + 0x65, 0x63, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0e, 0x64, 0x65, 0x74, 0x65, 0x63, + 0x74, 0x6f, 0x72, 0x4d, 0x61, 0x74, 0x72, 0x69, 0x78, 0x12, 0x49, 0x0a, 0x0f, 0x65, 0x78, 0x74, + 0x72, 0x61, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x18, 0x03, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x64, 0x63, 0x73, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x45, + 0x78, 0x74, 0x72, 0x61, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x45, 0x6e, + 0x74, 0x72, 0x79, 0x52, 0x0f, 0x65, 0x78, 0x74, 0x72, 0x61, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, + 0x74, 0x65, 0x72, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, + 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, + 0x6d, 0x70, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x42, 0x0a, 0x14, + 0x45, 0x78, 0x74, 0x72, 0x61, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x45, + 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, + 0x22, 0xe7, 0x01, 0x0a, 0x18, 0x44, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x4f, 0x70, 0x65, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x29, 0x0a, + 0x08, 0x64, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, + 0x0d, 0x2e, 0x64, 0x63, 0x73, 0x2e, 0x44, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, 0x08, + 0x64, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x5c, 0x0a, 0x0f, 0x65, 0x78, 0x74, 0x72, + 0x61, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x32, 0x2e, 0x64, 0x63, 0x73, 0x2e, 0x44, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, + 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x2e, 0x45, 0x78, 0x74, 0x72, 0x61, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0f, 0x65, 0x78, 0x74, 0x72, 0x61, 0x50, 0x61, 0x72, 0x61, + 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x1a, 0x42, 0x0a, 0x14, 0x45, 0x78, 0x74, 0x72, 0x61, 0x50, + 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, + 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, + 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x93, 0x01, 0x0a, 0x0a, 0x50, + 0x66, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3b, 0x0a, 0x09, 0x64, 0x65, 0x74, + 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x64, + 0x63, 0x73, 0x2e, 0x44, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x4f, 0x70, 0x65, 0x72, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x09, 0x64, 0x65, 0x74, + 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x12, 0x26, 0x0a, 0x07, 0x72, 0x75, 0x6e, 0x54, 0x79, 0x70, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0c, 0x2e, 0x64, 0x63, 0x73, 0x2e, 0x52, 0x75, + 0x6e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x07, 0x72, 0x75, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x20, + 0x0a, 0x0b, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, + 0x22, 0x8f, 0x01, 0x0a, 0x0a, 0x53, 0x6f, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x3b, 0x0a, 0x09, 0x64, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x64, 0x63, 0x73, 0x2e, 0x44, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, + 0x72, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x52, 0x09, 0x64, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x12, 0x26, 0x0a, 0x07, + 0x72, 0x75, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0c, 0x2e, + 0x64, 0x63, 0x73, 0x2e, 0x52, 0x75, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x07, 0x72, 0x75, 0x6e, + 0x54, 0x79, 0x70, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x72, 0x75, 0x6e, 0x4e, 0x75, 0x6d, 0x62, 0x65, + 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x72, 0x75, 0x6e, 0x4e, 0x75, 0x6d, 0x62, + 0x65, 0x72, 0x22, 0x67, 0x0a, 0x0a, 0x45, 0x6f, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x3b, 0x0a, 0x09, 0x64, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x64, 0x63, 0x73, 0x2e, 0x44, 0x65, 0x74, 0x65, 0x63, 0x74, + 0x6f, 0x72, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x52, 0x09, 0x64, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x12, 0x1c, 0x0a, + 0x09, 0x72, 0x75, 0x6e, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, + 0x52, 0x09, 0x72, 0x75, 0x6e, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x22, 0xd7, 0x02, 0x0a, 0x08, + 0x52, 0x75, 0x6e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x2c, 0x0a, 0x09, 0x65, 0x76, 0x65, 0x6e, + 0x74, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0e, 0x2e, 0x64, 0x63, + 0x73, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x09, 0x65, 0x76, 0x65, + 0x6e, 0x74, 0x74, 0x79, 0x70, 0x65, 0x12, 0x29, 0x0a, 0x08, 0x64, 0x65, 0x74, 0x65, 0x63, 0x74, + 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0d, 0x2e, 0x64, 0x63, 0x73, 0x2e, 0x44, + 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, 0x08, 0x64, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, + 0x72, 0x12, 0x28, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, + 0x32, 0x12, 0x2e, 0x64, 0x63, 0x73, 0x2e, 0x44, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x53, + 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x4c, 0x0a, 0x0f, 0x65, + 0x78, 0x74, 0x72, 0x61, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x18, 0x04, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x64, 0x63, 0x73, 0x2e, 0x52, 0x75, 0x6e, 0x45, 0x76, + 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x78, 0x74, 0x72, 0x61, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, + 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0f, 0x65, 0x78, 0x74, 0x72, 0x61, 0x50, + 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x74, 0x69, + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x1a, 0x42, 0x0a, 0x14, 0x45, 0x78, 0x74, 0x72, 0x61, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, + 0x74, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x3a, 0x0a, 0x0d, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x29, 0x0a, 0x08, 0x64, 0x65, 0x74, 0x65, 0x63, 0x74, + 0x6f, 0x72, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x0d, 0x2e, 0x64, 0x63, 0x73, 0x2e, 0x44, + 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, 0x08, 0x64, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, + 0x72, 0x22, 0x48, 0x0a, 0x0b, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, + 0x12, 0x39, 0x0a, 0x0e, 0x64, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x4d, 0x61, 0x74, 0x72, + 0x69, 0x78, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x64, 0x63, 0x73, 0x2e, 0x44, + 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0e, 0x64, 0x65, 0x74, + 0x65, 0x63, 0x74, 0x6f, 0x72, 0x4d, 0x61, 0x74, 0x72, 0x69, 0x78, 0x2a, 0xa2, 0x01, 0x0a, 0x09, + 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0e, 0x0a, 0x0a, 0x4e, 0x55, 0x4c, + 0x4c, 0x5f, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x48, 0x45, 0x41, + 0x52, 0x54, 0x42, 0x45, 0x41, 0x54, 0x10, 0x01, 0x12, 0x16, 0x0a, 0x12, 0x53, 0x54, 0x41, 0x54, + 0x45, 0x5f, 0x43, 0x48, 0x41, 0x4e, 0x47, 0x45, 0x5f, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x10, 0x02, + 0x12, 0x0f, 0x0a, 0x0b, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x5f, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x10, + 0x03, 0x12, 0x0d, 0x0a, 0x09, 0x41, 0x43, 0x4b, 0x5f, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x10, 0x04, + 0x12, 0x0d, 0x0a, 0x09, 0x53, 0x4f, 0x52, 0x5f, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x10, 0x14, 0x12, + 0x0d, 0x0a, 0x09, 0x45, 0x4f, 0x52, 0x5f, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x10, 0x1e, 0x12, 0x11, + 0x0a, 0x0d, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x5f, 0x54, 0x49, 0x4d, 0x45, 0x4f, 0x55, 0x54, 0x10, + 0x28, 0x12, 0x0d, 0x0a, 0x09, 0x50, 0x46, 0x52, 0x5f, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x10, 0x32, + 0x2a, 0xb9, 0x01, 0x0a, 0x08, 0x44, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x11, 0x0a, + 0x0d, 0x4e, 0x55, 0x4c, 0x4c, 0x5f, 0x44, 0x45, 0x54, 0x45, 0x43, 0x54, 0x4f, 0x52, 0x10, 0x00, + 0x12, 0x07, 0x0a, 0x03, 0x45, 0x4d, 0x43, 0x10, 0x02, 0x12, 0x07, 0x0a, 0x03, 0x46, 0x44, 0x44, + 0x10, 0x03, 0x12, 0x07, 0x0a, 0x03, 0x46, 0x54, 0x30, 0x10, 0x04, 0x12, 0x07, 0x0a, 0x03, 0x46, + 0x56, 0x30, 0x10, 0x05, 0x12, 0x07, 0x0a, 0x03, 0x49, 0x54, 0x53, 0x10, 0x06, 0x12, 0x07, 0x0a, + 0x03, 0x48, 0x4d, 0x50, 0x10, 0x07, 0x12, 0x07, 0x0a, 0x03, 0x4d, 0x43, 0x48, 0x10, 0x08, 0x12, + 0x07, 0x0a, 0x03, 0x4d, 0x46, 0x54, 0x10, 0x09, 0x12, 0x07, 0x0a, 0x03, 0x4d, 0x49, 0x44, 0x10, + 0x0a, 0x12, 0x07, 0x0a, 0x03, 0x54, 0x4f, 0x46, 0x10, 0x0c, 0x12, 0x07, 0x0a, 0x03, 0x54, 0x50, + 0x43, 0x10, 0x0d, 0x12, 0x07, 0x0a, 0x03, 0x54, 0x52, 0x44, 0x10, 0x0e, 0x12, 0x07, 0x0a, 0x03, + 0x5a, 0x44, 0x43, 0x10, 0x0f, 0x12, 0x07, 0x0a, 0x03, 0x4c, 0x48, 0x43, 0x10, 0x10, 0x12, 0x07, + 0x0a, 0x03, 0x44, 0x43, 0x53, 0x10, 0x11, 0x12, 0x07, 0x0a, 0x03, 0x41, 0x47, 0x44, 0x10, 0x12, + 0x22, 0x04, 0x08, 0x01, 0x10, 0x01, 0x22, 0x04, 0x08, 0x0b, 0x10, 0x0b, 0x2a, 0xcd, 0x02, 0x0a, + 0x0d, 0x44, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x0e, + 0x0a, 0x0a, 0x4e, 0x55, 0x4c, 0x4c, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x10, 0x00, 0x12, 0x09, + 0x0a, 0x05, 0x52, 0x45, 0x41, 0x44, 0x59, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x52, 0x55, 0x4e, + 0x5f, 0x4f, 0x4b, 0x10, 0x02, 0x12, 0x0f, 0x0a, 0x0b, 0x52, 0x55, 0x4e, 0x5f, 0x46, 0x41, 0x49, + 0x4c, 0x55, 0x52, 0x45, 0x10, 0x03, 0x12, 0x0f, 0x0a, 0x0b, 0x52, 0x55, 0x4e, 0x5f, 0x49, 0x4e, + 0x48, 0x49, 0x42, 0x49, 0x54, 0x10, 0x04, 0x12, 0x13, 0x0a, 0x0f, 0x53, 0x4f, 0x52, 0x5f, 0x50, + 0x52, 0x4f, 0x47, 0x52, 0x45, 0x53, 0x53, 0x49, 0x4e, 0x47, 0x10, 0x05, 0x12, 0x13, 0x0a, 0x0f, + 0x45, 0x4f, 0x52, 0x5f, 0x50, 0x52, 0x4f, 0x47, 0x52, 0x45, 0x53, 0x53, 0x49, 0x4e, 0x47, 0x10, + 0x06, 0x12, 0x0f, 0x0a, 0x0b, 0x53, 0x4f, 0x52, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, + 0x10, 0x07, 0x12, 0x0f, 0x0a, 0x0b, 0x45, 0x4f, 0x52, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, + 0x45, 0x10, 0x08, 0x12, 0x09, 0x0a, 0x05, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x09, 0x12, 0x08, + 0x0a, 0x04, 0x44, 0x45, 0x41, 0x44, 0x10, 0x0a, 0x12, 0x13, 0x0a, 0x0f, 0x53, 0x4f, 0x52, 0x5f, + 0x55, 0x4e, 0x41, 0x56, 0x41, 0x49, 0x4c, 0x41, 0x42, 0x4c, 0x45, 0x10, 0x0b, 0x12, 0x13, 0x0a, + 0x0f, 0x45, 0x4f, 0x52, 0x5f, 0x55, 0x4e, 0x41, 0x56, 0x41, 0x49, 0x4c, 0x41, 0x42, 0x4c, 0x45, + 0x10, 0x0c, 0x12, 0x0d, 0x0a, 0x09, 0x50, 0x52, 0x45, 0x50, 0x41, 0x52, 0x49, 0x4e, 0x47, 0x10, + 0x0d, 0x12, 0x11, 0x0a, 0x0d, 0x53, 0x4f, 0x52, 0x5f, 0x41, 0x56, 0x41, 0x49, 0x4c, 0x41, 0x42, + 0x4c, 0x45, 0x10, 0x0e, 0x12, 0x11, 0x0a, 0x0d, 0x45, 0x4f, 0x52, 0x5f, 0x41, 0x56, 0x41, 0x49, + 0x4c, 0x41, 0x42, 0x4c, 0x45, 0x10, 0x0f, 0x12, 0x11, 0x0a, 0x0d, 0x50, 0x46, 0x52, 0x5f, 0x41, + 0x56, 0x41, 0x49, 0x4c, 0x41, 0x42, 0x4c, 0x45, 0x10, 0x10, 0x12, 0x13, 0x0a, 0x0f, 0x50, 0x46, + 0x52, 0x5f, 0x55, 0x4e, 0x41, 0x56, 0x41, 0x49, 0x4c, 0x41, 0x42, 0x4c, 0x45, 0x10, 0x11, 0x12, + 0x0b, 0x0a, 0x07, 0x54, 0x49, 0x4d, 0x45, 0x4f, 0x55, 0x54, 0x10, 0x12, 0x2a, 0xf8, 0x02, 0x0a, + 0x07, 0x52, 0x75, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x10, 0x0a, 0x0c, 0x4e, 0x55, 0x4c, 0x4c, + 0x5f, 0x52, 0x55, 0x4e, 0x54, 0x59, 0x50, 0x45, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x50, 0x48, + 0x59, 0x53, 0x49, 0x43, 0x53, 0x10, 0x01, 0x12, 0x0d, 0x0a, 0x09, 0x54, 0x45, 0x43, 0x48, 0x4e, + 0x49, 0x43, 0x41, 0x4c, 0x10, 0x02, 0x12, 0x0d, 0x0a, 0x09, 0x50, 0x45, 0x44, 0x45, 0x53, 0x54, + 0x41, 0x4c, 0x53, 0x10, 0x03, 0x12, 0x0a, 0x0a, 0x06, 0x50, 0x55, 0x4c, 0x53, 0x45, 0x52, 0x10, + 0x04, 0x12, 0x09, 0x0a, 0x05, 0x4c, 0x41, 0x53, 0x45, 0x52, 0x10, 0x05, 0x12, 0x1b, 0x0a, 0x17, + 0x43, 0x41, 0x4c, 0x49, 0x42, 0x52, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x49, 0x54, 0x48, 0x52, + 0x5f, 0x54, 0x55, 0x4e, 0x49, 0x4e, 0x47, 0x10, 0x06, 0x12, 0x1c, 0x0a, 0x18, 0x43, 0x41, 0x4c, + 0x49, 0x42, 0x52, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x56, 0x43, 0x41, 0x53, 0x4e, 0x5f, 0x54, + 0x55, 0x4e, 0x49, 0x4e, 0x47, 0x10, 0x07, 0x12, 0x18, 0x0a, 0x14, 0x43, 0x41, 0x4c, 0x49, 0x42, + 0x52, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x54, 0x48, 0x52, 0x5f, 0x53, 0x43, 0x41, 0x4e, 0x10, + 0x08, 0x12, 0x1c, 0x0a, 0x18, 0x43, 0x41, 0x4c, 0x49, 0x42, 0x52, 0x41, 0x54, 0x49, 0x4f, 0x4e, + 0x5f, 0x44, 0x49, 0x47, 0x49, 0x54, 0x41, 0x4c, 0x5f, 0x53, 0x43, 0x41, 0x4e, 0x10, 0x09, 0x12, + 0x1b, 0x0a, 0x17, 0x43, 0x41, 0x4c, 0x49, 0x42, 0x52, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x41, + 0x4e, 0x41, 0x4c, 0x4f, 0x47, 0x5f, 0x53, 0x43, 0x41, 0x4e, 0x10, 0x0a, 0x12, 0x13, 0x0a, 0x0f, + 0x43, 0x41, 0x4c, 0x49, 0x42, 0x52, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x46, 0x48, 0x52, 0x10, + 0x0b, 0x12, 0x1b, 0x0a, 0x17, 0x43, 0x41, 0x4c, 0x49, 0x42, 0x52, 0x41, 0x54, 0x49, 0x4f, 0x4e, + 0x5f, 0x41, 0x4c, 0x50, 0x49, 0x44, 0x45, 0x5f, 0x53, 0x43, 0x41, 0x4e, 0x10, 0x0c, 0x12, 0x09, + 0x0a, 0x05, 0x4e, 0x4f, 0x49, 0x53, 0x45, 0x10, 0x10, 0x12, 0x1c, 0x0a, 0x18, 0x43, 0x41, 0x4c, + 0x49, 0x42, 0x52, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x50, 0x55, 0x4c, 0x53, 0x45, 0x5f, 0x4c, + 0x45, 0x4e, 0x47, 0x54, 0x48, 0x10, 0x11, 0x12, 0x17, 0x0a, 0x13, 0x43, 0x41, 0x4c, 0x49, 0x42, + 0x52, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x56, 0x52, 0x45, 0x53, 0x45, 0x54, 0x44, 0x10, 0x12, + 0x12, 0x08, 0x0a, 0x03, 0x41, 0x4e, 0x59, 0x10, 0xac, 0x02, 0x22, 0x04, 0x08, 0x0d, 0x10, 0x0f, + 0x22, 0x05, 0x08, 0x13, 0x10, 0xab, 0x02, 0x2a, 0x2c, 0x0a, 0x0b, 0x54, 0x72, 0x69, 0x67, 0x67, + 0x65, 0x72, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x0e, 0x0a, 0x0a, 0x43, 0x4f, 0x4e, 0x54, 0x49, 0x4e, + 0x55, 0x4f, 0x55, 0x53, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x54, 0x52, 0x49, 0x47, 0x47, 0x45, + 0x52, 0x45, 0x44, 0x10, 0x01, 0x2a, 0x1d, 0x0a, 0x07, 0x53, 0x6f, 0x72, 0x4d, 0x6f, 0x64, 0x65, + 0x12, 0x08, 0x0a, 0x04, 0x46, 0x55, 0x4c, 0x4c, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x46, 0x41, + 0x53, 0x54, 0x10, 0x01, 0x32, 0x91, 0x02, 0x0a, 0x0c, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, + 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x35, 0x0a, 0x09, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, + 0x62, 0x65, 0x12, 0x18, 0x2e, 0x64, 0x63, 0x73, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0a, 0x2e, 0x64, + 0x63, 0x73, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x22, 0x00, 0x30, 0x01, 0x12, 0x33, 0x0a, 0x0d, + 0x50, 0x72, 0x65, 0x70, 0x61, 0x72, 0x65, 0x46, 0x6f, 0x72, 0x52, 0x75, 0x6e, 0x12, 0x0f, 0x2e, + 0x64, 0x63, 0x73, 0x2e, 0x50, 0x66, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0d, + 0x2e, 0x64, 0x63, 0x73, 0x2e, 0x52, 0x75, 0x6e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x22, 0x00, 0x30, + 0x01, 0x12, 0x30, 0x0a, 0x0a, 0x53, 0x74, 0x61, 0x72, 0x74, 0x4f, 0x66, 0x52, 0x75, 0x6e, 0x12, + 0x0f, 0x2e, 0x64, 0x63, 0x73, 0x2e, 0x53, 0x6f, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x0d, 0x2e, 0x64, 0x63, 0x73, 0x2e, 0x52, 0x75, 0x6e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x22, + 0x00, 0x30, 0x01, 0x12, 0x2e, 0x0a, 0x08, 0x45, 0x6e, 0x64, 0x4f, 0x66, 0x52, 0x75, 0x6e, 0x12, + 0x0f, 0x2e, 0x64, 0x63, 0x73, 0x2e, 0x45, 0x6f, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x0d, 0x2e, 0x64, 0x63, 0x73, 0x2e, 0x52, 0x75, 0x6e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x22, + 0x00, 0x30, 0x01, 0x12, 0x33, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x12, 0x12, 0x2e, 0x64, 0x63, 0x73, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x10, 0x2e, 0x64, 0x63, 0x73, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x42, 0x56, 0x0a, 0x11, 0x63, 0x68, 0x2e, 0x63, + 0x65, 0x72, 0x6e, 0x2e, 0x61, 0x6c, 0x69, 0x63, 0x65, 0x2e, 0x64, 0x63, 0x73, 0x5a, 0x41, 0x67, + 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x41, 0x6c, 0x69, 0x63, 0x65, 0x4f, + 0x32, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x2f, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2f, 0x63, + 0x6f, 0x72, 0x65, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, + 0x64, 0x63, 0x73, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x3b, 0x64, 0x63, 0x73, 0x70, 0x62, + 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_protos_dcs_proto_rawDescOnce sync.Once + file_protos_dcs_proto_rawDescData = file_protos_dcs_proto_rawDesc +) + +func file_protos_dcs_proto_rawDescGZIP() []byte { + file_protos_dcs_proto_rawDescOnce.Do(func() { + file_protos_dcs_proto_rawDescData = protoimpl.X.CompressGZIP(file_protos_dcs_proto_rawDescData) + }) + return file_protos_dcs_proto_rawDescData +} + +var file_protos_dcs_proto_enumTypes = make([]protoimpl.EnumInfo, 6) +var file_protos_dcs_proto_msgTypes = make([]protoimpl.MessageInfo, 13) +var file_protos_dcs_proto_goTypes = []interface{}{ + (EventType)(0), // 0: dcs.EventType + (Detector)(0), // 1: dcs.Detector + (DetectorState)(0), // 2: dcs.DetectorState + (RunType)(0), // 3: dcs.RunType + (TriggerMode)(0), // 4: dcs.TriggerMode + (SorMode)(0), // 5: dcs.SorMode + (*DetectorInfo)(nil), // 6: dcs.DetectorInfo + (*SubscriptionRequest)(nil), // 7: dcs.SubscriptionRequest + (*Event)(nil), // 8: dcs.Event + (*DetectorOperationRequest)(nil), // 9: dcs.DetectorOperationRequest + (*PfrRequest)(nil), // 10: dcs.PfrRequest + (*SorRequest)(nil), // 11: dcs.SorRequest + (*EorRequest)(nil), // 12: dcs.EorRequest + (*RunEvent)(nil), // 13: dcs.RunEvent + (*StatusRequest)(nil), // 14: dcs.StatusRequest + (*StatusReply)(nil), // 15: dcs.StatusReply + nil, // 16: dcs.Event.ExtraParametersEntry + nil, // 17: dcs.DetectorOperationRequest.ExtraParametersEntry + nil, // 18: dcs.RunEvent.ExtraParametersEntry +} +var file_protos_dcs_proto_depIdxs = []int32{ + 1, // 0: dcs.DetectorInfo.detector:type_name -> dcs.Detector + 2, // 1: dcs.DetectorInfo.state:type_name -> dcs.DetectorState + 3, // 2: dcs.DetectorInfo.allowedRunTypes:type_name -> dcs.RunType + 2, // 3: dcs.DetectorInfo.pfrAvailability:type_name -> dcs.DetectorState + 2, // 4: dcs.DetectorInfo.sorAvailability:type_name -> dcs.DetectorState + 0, // 5: dcs.Event.eventtype:type_name -> dcs.EventType + 6, // 6: dcs.Event.detectorMatrix:type_name -> dcs.DetectorInfo + 16, // 7: dcs.Event.extraParameters:type_name -> dcs.Event.ExtraParametersEntry + 1, // 8: dcs.DetectorOperationRequest.detector:type_name -> dcs.Detector + 17, // 9: dcs.DetectorOperationRequest.extraParameters:type_name -> dcs.DetectorOperationRequest.ExtraParametersEntry + 9, // 10: dcs.PfrRequest.detectors:type_name -> dcs.DetectorOperationRequest + 3, // 11: dcs.PfrRequest.runType:type_name -> dcs.RunType + 9, // 12: dcs.SorRequest.detectors:type_name -> dcs.DetectorOperationRequest + 3, // 13: dcs.SorRequest.runType:type_name -> dcs.RunType + 9, // 14: dcs.EorRequest.detectors:type_name -> dcs.DetectorOperationRequest + 0, // 15: dcs.RunEvent.eventtype:type_name -> dcs.EventType + 1, // 16: dcs.RunEvent.detector:type_name -> dcs.Detector + 2, // 17: dcs.RunEvent.state:type_name -> dcs.DetectorState + 18, // 18: dcs.RunEvent.extraParameters:type_name -> dcs.RunEvent.ExtraParametersEntry + 1, // 19: dcs.StatusRequest.detector:type_name -> dcs.Detector + 6, // 20: dcs.StatusReply.detectorMatrix:type_name -> dcs.DetectorInfo + 7, // 21: dcs.Configurator.Subscribe:input_type -> dcs.SubscriptionRequest + 10, // 22: dcs.Configurator.PrepareForRun:input_type -> dcs.PfrRequest + 11, // 23: dcs.Configurator.StartOfRun:input_type -> dcs.SorRequest + 12, // 24: dcs.Configurator.EndOfRun:input_type -> dcs.EorRequest + 14, // 25: dcs.Configurator.GetStatus:input_type -> dcs.StatusRequest + 8, // 26: dcs.Configurator.Subscribe:output_type -> dcs.Event + 13, // 27: dcs.Configurator.PrepareForRun:output_type -> dcs.RunEvent + 13, // 28: dcs.Configurator.StartOfRun:output_type -> dcs.RunEvent + 13, // 29: dcs.Configurator.EndOfRun:output_type -> dcs.RunEvent + 15, // 30: dcs.Configurator.GetStatus:output_type -> dcs.StatusReply + 26, // [26:31] is the sub-list for method output_type + 21, // [21:26] is the sub-list for method input_type + 21, // [21:21] is the sub-list for extension type_name + 21, // [21:21] is the sub-list for extension extendee + 0, // [0:21] is the sub-list for field type_name +} + +func init() { file_protos_dcs_proto_init() } +func file_protos_dcs_proto_init() { + if File_protos_dcs_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_protos_dcs_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DetectorInfo); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_dcs_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SubscriptionRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_dcs_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Event); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_dcs_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DetectorOperationRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_dcs_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PfrRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_dcs_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SorRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_dcs_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*EorRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_dcs_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RunEvent); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_dcs_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StatusRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_dcs_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StatusReply); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_protos_dcs_proto_rawDesc, + NumEnums: 6, + NumMessages: 13, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_protos_dcs_proto_goTypes, + DependencyIndexes: file_protos_dcs_proto_depIdxs, + EnumInfos: file_protos_dcs_proto_enumTypes, + MessageInfos: file_protos_dcs_proto_msgTypes, + }.Build() + File_protos_dcs_proto = out.File + file_protos_dcs_proto_rawDesc = nil + file_protos_dcs_proto_goTypes = nil + file_protos_dcs_proto_depIdxs = nil +} diff --git a/core/integration/dcs/protos/dcs.proto b/core/integration/dcs/protos/dcs.proto new file mode 100644 index 000000000..053f33cd6 --- /dev/null +++ b/core/integration/dcs/protos/dcs.proto @@ -0,0 +1,285 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2020-2021 CERN and copyright holders of ALICE O². + * Authors: Teo Mrnjavac + * Ombretta Pinazza + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ +syntax = "proto3"; + +package dcs; +option java_package = "ch.cern.alice.dcs"; +option go_package = "github.com/AliceO2Group/Control/core/integration/dcs/protos;dcspb"; + +////////////////////////////////////////////////////// + +service Configurator { + // Optional call, legal at any time, to subscribe to all future events from + // the DCS service. The server stops serving the stream when the client closes + // it. Multiple concurrent stream subscriptions are allowed. + rpc Subscribe (SubscriptionRequest) returns (stream Event) {} + + // Single PFR request for a data taking session, with per-detector parameters. + // Returns an event stream which returns subsequent intermediate states within + // the PFR operation. Upon PFR completion (DetectorState.RUN_OK), the server + // closes the stream. + rpc PrepareForRun (PfrRequest) returns (stream RunEvent) {} + + // Single SOR request for a data taking session, with run number. + // Returns an event stream which returns subsequent intermediate states within + // the SOR operation. Upon SOR completion (DetectorState.RUN_OK), the server + // closes the stream. + // If the DetectorOperationRequest or run type parameters are the same as the + // last PrepareForRun request (no matter how far back in time) and the DCS + // state hasn't changed, the SOR operations should be fast. Otherwise, the SOR + // also implicitly includes any number of PrepareForRun operations as needed, + // this can take a while but should not change the overall behavior reported + // to ECS. + rpc StartOfRun (SorRequest) returns (stream RunEvent) {} + + // Single EOR request for a data taking session, with per-detector parameters. + // Returns an event stream which returns subsequent intermediate states within + // the EOR operation. Upon EOR completion (DetectorState.RUN_OK), the server + // closes the stream. + rpc EndOfRun (EorRequest) returns (stream RunEvent) {} + + // Optional call, legal at any time, to query the status of the DCS service + // and either some or all of its constituent detectors. This call returns a + // single value (not a stream), reflecting the service state at that + // specific moment. + rpc GetStatus (StatusRequest) returns (StatusReply) {} +} + +////////////////////////////////////////////////////// +// Message definitions +////////////////////////////////////////////////////// + +// Note that all fields are optional in Proto3, so some might arrive empty from the +// client. See the gRPC documentation for language-specific default values. + +////////////////////////////////////////////////////// +// Common structures +////////////////////////////////////////////////////// + +// A DetectorInfo is a full status report for a single detector, multiple such +// payloads are then aggregated in a StatusReply or Subscribe Event. +message DetectorInfo { + Detector detector = 1; // 1 detector per DetectorInfo + DetectorState state = 2;// state of this detector at the time of response generation + string timestamp = 3; // time when the detector state is as declared + repeated RunType allowedRunTypes = 4; // a list of run types that are currently + // possible with the given detector, taking into + // account all low-level information; + // for example, at a given moment TECHNICAL + // runs might be possible but not PHYSICS + DetectorState pfrAvailability = 5; // availability of this detector for PFR at the time of response generation + DetectorState sorAvailability = 6; // availability of this detector for SOR at the time of response generation +} + +////////////////////////////////////////////////////// +// Subscribe call +////////////////////////////////////////////////////// + +message SubscriptionRequest { + string instanceId = 1; +} + +// The Subscribe response stream returns Event payloads: +// 1) as needed, Events of type ERROR_EVENT, TIMEOUT, etc. when something unexpected +// happens, especially outside of a SOR or EOR operation (which are already served +// by operation-specific streams) +// 2) periodically, Events of type HEARTBEAT with a full detectorMatrix to republish +// the full status of all detectors in the system +// 3) as needed, Events of type STATE_CHANGE_EVENT and similar, with a partial +// detectorMatrix that only reflects the changes in some detectors, usually when +// the internal state of a detector has changed on the DCS side in some way that +// makes a new RunType possible, or that inhibits a RunType that was previously +// possible and now isn't any more. +message Event { + EventType eventtype = 1; // type of event being published by the DCS service + repeated DetectorInfo detectorMatrix = 2; // detector-specific status payloads (optional depending + // on event type) + map extraParameters = 3; // general key-value map just in case + string timestamp = 4; // this corresponds to the moment where this Event-answer + // was valid from the source + string message = 5; // optional user-facing error message, if the DCS wants to + // report an ECS-related error status (e.g. the reason for + // a failed SOR) to the user +} + +////////////////////////////////////////////////////// +// PFR/SOR/EOR calls +////////////////////////////////////////////////////// + +// Each PfrRequest/SorRequest/EorRequest contains multiple DetectorOperationRequests, one per detector. +// This is needed in order to pass per-detector parameters within a single PFR/SOR/EOR operation. +message DetectorOperationRequest { + Detector detector = 1; + map extraParameters = 2; // detector-specific parameters + // may include ddl_list or cluster_sum (the latter only for MCH) +} + +// A PfrRequest encapsulates a list of detector-specific request payloads, +// plus a common runType any future runs. +message PfrRequest { + repeated DetectorOperationRequest detectors = 1; + RunType runType = 2; // default: NULL_RUNTYPE, should probably never be sent + string partitionId = 3; // unique id for a pfr request +} + +// A SorRequest encapsulates a list of detector-specific request payloads, +// plus a common runType and a runNumber for the run about to start. +message SorRequest { + repeated DetectorOperationRequest detectors = 1; + RunType runType = 2; // default: NULL_RUNTYPE, should probably never be sent + int32 runNumber = 3; +} + +// An EorRequest encapsulates a list of detector-specific request payloads, +// plus a runNumber to identify the run being stopped. +message EorRequest { + repeated DetectorOperationRequest detectors = 1; + int32 runNumber = 2; +} + +// A stream of Event objects is returned by PFR, SOR and EOR calls. +// Most fields are optional and filled as needed, depending on the type of request. +message RunEvent { + EventType eventtype = 1; // type of event being published by the DCS service + Detector detector = 2; // detector that produced the event + DetectorState state = 3; // aggregate state for all detectors involved in current + // PFR/SOR/EOR operation + map extraParameters = 4; + string timestamp = 5; + string message = 6; // optional user-facing error message, if the DCS wants to + // report an ECS-related error status (e.g. the reason for + // a failed SOR) to the user +} + +////////////////////////////////////////////////////// +// GetStatus call +////////////////////////////////////////////////////// + +// Payload for the GetStatus call, just a list of detectors to query. +message StatusRequest { + repeated Detector detector = 1; + // If the client sends this request with empty detector list, the server should return + // the status of all detectors. +} + +// Aggregate response to a GetStatus call, wraps around a list of DetectorInfo +// payloads. +message StatusReply { + repeated DetectorInfo detectorMatrix = 1; // an exhaustive list of DetectorInfos + // one for each detector +} + +////////////////////////////////////////////////////// +// Enum definitions +////////////////////////////////////////////////////// + +enum EventType { + NULL_EVENT = 0; + HEARTBEAT = 1; + STATE_CHANGE_EVENT = 2; + ERROR_EVENT = 3; + ACK_EVENT = 4; + SOR_EVENT = 20; + EOR_EVENT = 30; + EVENT_TIMEOUT = 40; + PFR_EVENT = 50; +} + +enum Detector { + NULL_DETECTOR = 0; + reserved 1; // CPV was removed during YETS 2024-2025 + EMC = 2; + FDD = 3; + FT0 = 4; + FV0 = 5; + ITS = 6; + HMP = 7; + MCH = 8; + MFT = 9; + MID = 10; + reserved 11; // PHS was removed during YETS 2024-2025 + TOF = 12; + TPC = 13; + TRD = 14; + ZDC = 15; + LHC = 16; + DCS = 17; + AGD = 18; +} + +enum DetectorState { + NULL_STATE = 0; + READY = 1; + RUN_OK = 2; + RUN_FAILURE = 3; + RUN_INHIBIT = 4; + SOR_PROGRESSING = 5; + EOR_PROGRESSING = 6; + SOR_FAILURE = 7; + EOR_FAILURE = 8; + ERROR = 9; + DEAD = 10; + SOR_UNAVAILABLE = 11; + EOR_UNAVAILABLE = 12; + PREPARING = 13; + SOR_AVAILABLE = 14; + EOR_AVAILABLE = 15; + PFR_AVAILABLE = 16; + PFR_UNAVAILABLE = 17; + TIMEOUT = 18; +} + +enum RunType { + NULL_RUNTYPE = 0; + PHYSICS = 1; + TECHNICAL = 2; + PEDESTALS = 3; + PULSER = 4; + LASER = 5; + CALIBRATION_ITHR_TUNING = 6; + CALIBRATION_VCASN_TUNING = 7; + CALIBRATION_THR_SCAN = 8; + CALIBRATION_DIGITAL_SCAN = 9; + CALIBRATION_ANALOG_SCAN = 10; + CALIBRATION_FHR = 11; + CALIBRATION_ALPIDE_SCAN = 12; + reserved 13 to 15; // CALIBRATION, COSMICS, SYNTHETIC + NOISE = 16; + CALIBRATION_PULSE_LENGTH = 17; + CALIBRATION_VRESETD = 18; + reserved 19 to 299; + + ANY = 300; + +} + +enum TriggerMode { // FIXME: unused + CONTINUOUS = 0; + TRIGGERED = 1; +} +enum SorMode { // FIXME: unused + FULL = 0; + FAST = 1; +} diff --git a/core/integration/dcs/protos/dcs_grpc.pb.go b/core/integration/dcs/protos/dcs_grpc.pb.go new file mode 100644 index 000000000..35ee86566 --- /dev/null +++ b/core/integration/dcs/protos/dcs_grpc.pb.go @@ -0,0 +1,438 @@ +// +// === This file is part of ALICE O² === +// +// Copyright 2020-2021 CERN and copyright holders of ALICE O². +// Authors: Teo Mrnjavac +// Ombretta Pinazza +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// In applying this license CERN does not waive the privileges and +// immunities granted to it by virtue of its status as an +// Intergovernmental Organization or submit itself to any jurisdiction. + +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.3.0 +// - protoc v5.29.3 +// source: protos/dcs.proto + +package dcspb + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +const ( + Configurator_Subscribe_FullMethodName = "/dcs.Configurator/Subscribe" + Configurator_PrepareForRun_FullMethodName = "/dcs.Configurator/PrepareForRun" + Configurator_StartOfRun_FullMethodName = "/dcs.Configurator/StartOfRun" + Configurator_EndOfRun_FullMethodName = "/dcs.Configurator/EndOfRun" + Configurator_GetStatus_FullMethodName = "/dcs.Configurator/GetStatus" +) + +// ConfiguratorClient is the client API for Configurator service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type ConfiguratorClient interface { + // Optional call, legal at any time, to subscribe to all future events from + // the DCS service. The server stops serving the stream when the client closes + // it. Multiple concurrent stream subscriptions are allowed. + Subscribe(ctx context.Context, in *SubscriptionRequest, opts ...grpc.CallOption) (Configurator_SubscribeClient, error) + // Single PFR request for a data taking session, with per-detector parameters. + // Returns an event stream which returns subsequent intermediate states within + // the PFR operation. Upon PFR completion (DetectorState.RUN_OK), the server + // closes the stream. + PrepareForRun(ctx context.Context, in *PfrRequest, opts ...grpc.CallOption) (Configurator_PrepareForRunClient, error) + // Single SOR request for a data taking session, with run number. + // Returns an event stream which returns subsequent intermediate states within + // the SOR operation. Upon SOR completion (DetectorState.RUN_OK), the server + // closes the stream. + // If the DetectorOperationRequest or run type parameters are the same as the + // last PrepareForRun request (no matter how far back in time) and the DCS + // state hasn't changed, the SOR operations should be fast. Otherwise, the SOR + // also implicitly includes any number of PrepareForRun operations as needed, + // this can take a while but should not change the overall behavior reported + // to ECS. + StartOfRun(ctx context.Context, in *SorRequest, opts ...grpc.CallOption) (Configurator_StartOfRunClient, error) + // Single EOR request for a data taking session, with per-detector parameters. + // Returns an event stream which returns subsequent intermediate states within + // the EOR operation. Upon EOR completion (DetectorState.RUN_OK), the server + // closes the stream. + EndOfRun(ctx context.Context, in *EorRequest, opts ...grpc.CallOption) (Configurator_EndOfRunClient, error) + // Optional call, legal at any time, to query the status of the DCS service + // and either some or all of its constituent detectors. This call returns a + // single value (not a stream), reflecting the service state at that + // specific moment. + GetStatus(ctx context.Context, in *StatusRequest, opts ...grpc.CallOption) (*StatusReply, error) +} + +type configuratorClient struct { + cc grpc.ClientConnInterface +} + +func NewConfiguratorClient(cc grpc.ClientConnInterface) ConfiguratorClient { + return &configuratorClient{cc} +} + +func (c *configuratorClient) Subscribe(ctx context.Context, in *SubscriptionRequest, opts ...grpc.CallOption) (Configurator_SubscribeClient, error) { + stream, err := c.cc.NewStream(ctx, &Configurator_ServiceDesc.Streams[0], Configurator_Subscribe_FullMethodName, opts...) + if err != nil { + return nil, err + } + x := &configuratorSubscribeClient{stream} + if err := x.ClientStream.SendMsg(in); err != nil { + return nil, err + } + if err := x.ClientStream.CloseSend(); err != nil { + return nil, err + } + return x, nil +} + +type Configurator_SubscribeClient interface { + Recv() (*Event, error) + grpc.ClientStream +} + +type configuratorSubscribeClient struct { + grpc.ClientStream +} + +func (x *configuratorSubscribeClient) Recv() (*Event, error) { + m := new(Event) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +func (c *configuratorClient) PrepareForRun(ctx context.Context, in *PfrRequest, opts ...grpc.CallOption) (Configurator_PrepareForRunClient, error) { + stream, err := c.cc.NewStream(ctx, &Configurator_ServiceDesc.Streams[1], Configurator_PrepareForRun_FullMethodName, opts...) + if err != nil { + return nil, err + } + x := &configuratorPrepareForRunClient{stream} + if err := x.ClientStream.SendMsg(in); err != nil { + return nil, err + } + if err := x.ClientStream.CloseSend(); err != nil { + return nil, err + } + return x, nil +} + +type Configurator_PrepareForRunClient interface { + Recv() (*RunEvent, error) + grpc.ClientStream +} + +type configuratorPrepareForRunClient struct { + grpc.ClientStream +} + +func (x *configuratorPrepareForRunClient) Recv() (*RunEvent, error) { + m := new(RunEvent) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +func (c *configuratorClient) StartOfRun(ctx context.Context, in *SorRequest, opts ...grpc.CallOption) (Configurator_StartOfRunClient, error) { + stream, err := c.cc.NewStream(ctx, &Configurator_ServiceDesc.Streams[2], Configurator_StartOfRun_FullMethodName, opts...) + if err != nil { + return nil, err + } + x := &configuratorStartOfRunClient{stream} + if err := x.ClientStream.SendMsg(in); err != nil { + return nil, err + } + if err := x.ClientStream.CloseSend(); err != nil { + return nil, err + } + return x, nil +} + +type Configurator_StartOfRunClient interface { + Recv() (*RunEvent, error) + grpc.ClientStream +} + +type configuratorStartOfRunClient struct { + grpc.ClientStream +} + +func (x *configuratorStartOfRunClient) Recv() (*RunEvent, error) { + m := new(RunEvent) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +func (c *configuratorClient) EndOfRun(ctx context.Context, in *EorRequest, opts ...grpc.CallOption) (Configurator_EndOfRunClient, error) { + stream, err := c.cc.NewStream(ctx, &Configurator_ServiceDesc.Streams[3], Configurator_EndOfRun_FullMethodName, opts...) + if err != nil { + return nil, err + } + x := &configuratorEndOfRunClient{stream} + if err := x.ClientStream.SendMsg(in); err != nil { + return nil, err + } + if err := x.ClientStream.CloseSend(); err != nil { + return nil, err + } + return x, nil +} + +type Configurator_EndOfRunClient interface { + Recv() (*RunEvent, error) + grpc.ClientStream +} + +type configuratorEndOfRunClient struct { + grpc.ClientStream +} + +func (x *configuratorEndOfRunClient) Recv() (*RunEvent, error) { + m := new(RunEvent) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +func (c *configuratorClient) GetStatus(ctx context.Context, in *StatusRequest, opts ...grpc.CallOption) (*StatusReply, error) { + out := new(StatusReply) + err := c.cc.Invoke(ctx, Configurator_GetStatus_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// ConfiguratorServer is the server API for Configurator service. +// All implementations should embed UnimplementedConfiguratorServer +// for forward compatibility +type ConfiguratorServer interface { + // Optional call, legal at any time, to subscribe to all future events from + // the DCS service. The server stops serving the stream when the client closes + // it. Multiple concurrent stream subscriptions are allowed. + Subscribe(*SubscriptionRequest, Configurator_SubscribeServer) error + // Single PFR request for a data taking session, with per-detector parameters. + // Returns an event stream which returns subsequent intermediate states within + // the PFR operation. Upon PFR completion (DetectorState.RUN_OK), the server + // closes the stream. + PrepareForRun(*PfrRequest, Configurator_PrepareForRunServer) error + // Single SOR request for a data taking session, with run number. + // Returns an event stream which returns subsequent intermediate states within + // the SOR operation. Upon SOR completion (DetectorState.RUN_OK), the server + // closes the stream. + // If the DetectorOperationRequest or run type parameters are the same as the + // last PrepareForRun request (no matter how far back in time) and the DCS + // state hasn't changed, the SOR operations should be fast. Otherwise, the SOR + // also implicitly includes any number of PrepareForRun operations as needed, + // this can take a while but should not change the overall behavior reported + // to ECS. + StartOfRun(*SorRequest, Configurator_StartOfRunServer) error + // Single EOR request for a data taking session, with per-detector parameters. + // Returns an event stream which returns subsequent intermediate states within + // the EOR operation. Upon EOR completion (DetectorState.RUN_OK), the server + // closes the stream. + EndOfRun(*EorRequest, Configurator_EndOfRunServer) error + // Optional call, legal at any time, to query the status of the DCS service + // and either some or all of its constituent detectors. This call returns a + // single value (not a stream), reflecting the service state at that + // specific moment. + GetStatus(context.Context, *StatusRequest) (*StatusReply, error) +} + +// UnimplementedConfiguratorServer should be embedded to have forward compatible implementations. +type UnimplementedConfiguratorServer struct { +} + +func (UnimplementedConfiguratorServer) Subscribe(*SubscriptionRequest, Configurator_SubscribeServer) error { + return status.Errorf(codes.Unimplemented, "method Subscribe not implemented") +} +func (UnimplementedConfiguratorServer) PrepareForRun(*PfrRequest, Configurator_PrepareForRunServer) error { + return status.Errorf(codes.Unimplemented, "method PrepareForRun not implemented") +} +func (UnimplementedConfiguratorServer) StartOfRun(*SorRequest, Configurator_StartOfRunServer) error { + return status.Errorf(codes.Unimplemented, "method StartOfRun not implemented") +} +func (UnimplementedConfiguratorServer) EndOfRun(*EorRequest, Configurator_EndOfRunServer) error { + return status.Errorf(codes.Unimplemented, "method EndOfRun not implemented") +} +func (UnimplementedConfiguratorServer) GetStatus(context.Context, *StatusRequest) (*StatusReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetStatus not implemented") +} + +// UnsafeConfiguratorServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to ConfiguratorServer will +// result in compilation errors. +type UnsafeConfiguratorServer interface { + mustEmbedUnimplementedConfiguratorServer() +} + +func RegisterConfiguratorServer(s grpc.ServiceRegistrar, srv ConfiguratorServer) { + s.RegisterService(&Configurator_ServiceDesc, srv) +} + +func _Configurator_Subscribe_Handler(srv interface{}, stream grpc.ServerStream) error { + m := new(SubscriptionRequest) + if err := stream.RecvMsg(m); err != nil { + return err + } + return srv.(ConfiguratorServer).Subscribe(m, &configuratorSubscribeServer{stream}) +} + +type Configurator_SubscribeServer interface { + Send(*Event) error + grpc.ServerStream +} + +type configuratorSubscribeServer struct { + grpc.ServerStream +} + +func (x *configuratorSubscribeServer) Send(m *Event) error { + return x.ServerStream.SendMsg(m) +} + +func _Configurator_PrepareForRun_Handler(srv interface{}, stream grpc.ServerStream) error { + m := new(PfrRequest) + if err := stream.RecvMsg(m); err != nil { + return err + } + return srv.(ConfiguratorServer).PrepareForRun(m, &configuratorPrepareForRunServer{stream}) +} + +type Configurator_PrepareForRunServer interface { + Send(*RunEvent) error + grpc.ServerStream +} + +type configuratorPrepareForRunServer struct { + grpc.ServerStream +} + +func (x *configuratorPrepareForRunServer) Send(m *RunEvent) error { + return x.ServerStream.SendMsg(m) +} + +func _Configurator_StartOfRun_Handler(srv interface{}, stream grpc.ServerStream) error { + m := new(SorRequest) + if err := stream.RecvMsg(m); err != nil { + return err + } + return srv.(ConfiguratorServer).StartOfRun(m, &configuratorStartOfRunServer{stream}) +} + +type Configurator_StartOfRunServer interface { + Send(*RunEvent) error + grpc.ServerStream +} + +type configuratorStartOfRunServer struct { + grpc.ServerStream +} + +func (x *configuratorStartOfRunServer) Send(m *RunEvent) error { + return x.ServerStream.SendMsg(m) +} + +func _Configurator_EndOfRun_Handler(srv interface{}, stream grpc.ServerStream) error { + m := new(EorRequest) + if err := stream.RecvMsg(m); err != nil { + return err + } + return srv.(ConfiguratorServer).EndOfRun(m, &configuratorEndOfRunServer{stream}) +} + +type Configurator_EndOfRunServer interface { + Send(*RunEvent) error + grpc.ServerStream +} + +type configuratorEndOfRunServer struct { + grpc.ServerStream +} + +func (x *configuratorEndOfRunServer) Send(m *RunEvent) error { + return x.ServerStream.SendMsg(m) +} + +func _Configurator_GetStatus_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(StatusRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ConfiguratorServer).GetStatus(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Configurator_GetStatus_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ConfiguratorServer).GetStatus(ctx, req.(*StatusRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// Configurator_ServiceDesc is the grpc.ServiceDesc for Configurator service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var Configurator_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "dcs.Configurator", + HandlerType: (*ConfiguratorServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "GetStatus", + Handler: _Configurator_GetStatus_Handler, + }, + }, + Streams: []grpc.StreamDesc{ + { + StreamName: "Subscribe", + Handler: _Configurator_Subscribe_Handler, + ServerStreams: true, + }, + { + StreamName: "PrepareForRun", + Handler: _Configurator_PrepareForRun_Handler, + ServerStreams: true, + }, + { + StreamName: "StartOfRun", + Handler: _Configurator_StartOfRun_Handler, + ServerStreams: true, + }, + { + StreamName: "EndOfRun", + Handler: _Configurator_EndOfRun_Handler, + ServerStreams: true, + }, + }, + Metadata: "protos/dcs.proto", +} diff --git a/core/integration/dcs/structs.go b/core/integration/dcs/structs.go new file mode 100644 index 000000000..6455168f8 --- /dev/null +++ b/core/integration/dcs/structs.go @@ -0,0 +1,179 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2021-2025 CERN and copyright holders of ALICE O². + * Author: Teo Mrnjavac + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package dcs + +import ( + "fmt" + "strings" + + dcspb "github.com/AliceO2Group/Control/core/integration/dcs/protos" +) + +type PartitionInfo struct { + SorSuccessful bool + Detectors ECSDetectorInfoMap +} + +type ECSDetectorInfo struct { + State string + Timestamp string + AllowedRunTypes []string + PfrAvailability string + SorAvailability string +} + +func (d DCSDetectors) ToStringSlice() (sslice []string) { + if d == nil { + return + } + sslice = make([]string, len(d)) + if len(d) == 0 { + return + } + + for i, det := range d { + sslice[i] = det.String() + } + return +} + +func (d DCSDetectors) EcsDetectorsSlice() (sslice []string) { + if d == nil { + return + } + sslice = make([]string, len(d)) + if len(d) == 0 { + return + } + + for i, det := range d { + sslice[i] = dcsToEcsDetector(det) + } + return +} + +func (dsm DCSDetectorOpAvailabilityMap) ToStringMap() (sm map[string]string) { + sm = make(map[string]string) + for det, detState := range dsm { + sm[det.String()] = detState.String() + } + return +} + +func (dsm DCSDetectorOpAvailabilityMap) EcsDetectorsMap() (sm map[string]string) { + sm = make(map[string]string) + for det, detState := range dsm { + sm[dcsToEcsDetector(det)] = detState.String() + } + return +} + +func (dsm DCSDetectorOpAvailabilityMap) makeDetectorsByStateMap() map[dcspb.DetectorState]DCSDetectors { + detectorsByState := make(map[dcspb.DetectorState]DCSDetectors) + for det, detState := range dsm { + if _, ok := detectorsByState[detState]; !ok { + detectorsByState[detState] = make([]dcspb.Detector, 0) + } + detectorsByState[detState] = append(detectorsByState[detState], det) + } + return detectorsByState +} + +// Returns true if the provided detectors are either all in conditionState or in NULL_STATE +func (dsm DCSDetectorOpAvailabilityMap) compatibleWithDCSOperation(conditionState dcspb.DetectorState) (isCompatible bool, detectorsCompatible DCSDetectors, detectorsIncompatible DCSDetectors, err error) { + detectorsByState := dsm.makeDetectorsByStateMap() + + if len(detectorsByState) == 0 { + return true, make(DCSDetectors, 0), make(DCSDetectors, 0), fmt.Errorf("no detectors provided") + } + + detectorsInConditionState, thereAreDetectorsInConditionState := detectorsByState[conditionState] + detectorsInNullState, thereAreDetectorsInNullState := detectorsByState[dcspb.DetectorState_NULL_STATE] + + if thereAreDetectorsInConditionState && (len(detectorsInConditionState) == len(dsm)) { + // all detectors are in conditionState + return true, detectorsInConditionState, make(DCSDetectors, 0), nil + } else if thereAreDetectorsInConditionState && thereAreDetectorsInNullState && (len(detectorsInConditionState)+len(detectorsInNullState) == len(dsm)) { + // all detectors are either in conditionState or in NULL_STATE + detectorsCompatible = append(detectorsInConditionState, detectorsInNullState...) + return true, detectorsCompatible, make(DCSDetectors, 0), fmt.Errorf("detectors %s are in NULL_STATE", strings.Join(detectorsByState[dcspb.DetectorState_NULL_STATE].ToStringSlice(), ", ")) + } else if thereAreDetectorsInNullState && (len(detectorsInNullState) == len(dsm)) { + // all detectors are in NULL_STATE + return true, detectorsInNullState, make(DCSDetectors, 0), fmt.Errorf("all detectors are in NULL_STATE") + } else { + // there are detectors in other states incompatible with conditionState + reportByState := make([]string, 0) + detectorsCompatible = make(DCSDetectors, 0) + detectorsIncompatible = make(DCSDetectors, 0) + for state, detectors := range detectorsByState { + if state == conditionState { + detectorsCompatible = append(detectorsCompatible, detectors...) + continue + } + if state == dcspb.DetectorState_NULL_STATE { + detectorsCompatible = append(detectorsCompatible, detectors...) + } else { + detectorsIncompatible = append(detectorsIncompatible, detectors...) + } + reportByState = append(reportByState, + fmt.Sprintf("%s in %s", strings.Join(detectors.ToStringSlice(), ", "), state.String())) + } + return false, detectorsCompatible, detectorsIncompatible, fmt.Errorf("detectors are in incompatible states: %v", strings.Join(reportByState, "; ")) + } +} + +func fromDcsDetectorInfo(d *dcspb.DetectorInfo) ECSDetectorInfo { + return ECSDetectorInfo{ + State: d.GetState().String(), + Timestamp: d.GetTimestamp(), + AllowedRunTypes: func(rts []dcspb.RunType) []string { + out := make([]string, len(rts)) + for i, rt := range rts { + out[i] = rt.String() + } + return out + }(d.GetAllowedRunTypes()), + PfrAvailability: d.GetPfrAvailability().String(), + SorAvailability: d.GetSorAvailability().String(), + } +} + +func (m DCSDetectorInfoMap) ToEcsDetectors() ECSDetectorInfoMap { + out := make(map[string]ECSDetectorInfo) + for k, v := range m { + out[dcsToEcsDetector(k)] = fromDcsDetectorInfo(v) + } + return out +} + +func (m ECSDetectorInfoMap) Filtered(detectorList []string) ECSDetectorInfoMap { + out := make(ECSDetectorInfoMap) + for _, det := range detectorList { + if _, ok := m[det]; ok { + out[det] = m[det] + } + } + return out +} diff --git a/core/integration/dcs/structs_test.go b/core/integration/dcs/structs_test.go new file mode 100644 index 000000000..d01689572 --- /dev/null +++ b/core/integration/dcs/structs_test.go @@ -0,0 +1,195 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2024 CERN and copyright holders of ALICE O². + * Author: Teo Mrnjavac + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package dcs + +import ( + "testing" + + dcspb "github.com/AliceO2Group/Control/core/integration/dcs/protos" +) + +func TestDCSDetectorOpAvailabilityMap_compatibleWithDCSOperation(t *testing.T) { + type args struct { + conditionState dcspb.DetectorState + } + tests := []struct { + name string + dsm DCSDetectorOpAvailabilityMap + args args + want bool + wantErr bool + }{ + { + name: "condition=PFR_AVAILABLE/states=[]", + dsm: map[dcspb.Detector]dcspb.DetectorState{}, + args: args{conditionState: dcspb.DetectorState_PFR_AVAILABLE}, + want: true, + wantErr: true, + }, + { + name: "condition=PFR_AVAILABLE/states=NULL", + dsm: map[dcspb.Detector]dcspb.DetectorState{ + dcspb.Detector_ZDC: dcspb.DetectorState_NULL_STATE, + dcspb.Detector_FT0: dcspb.DetectorState_NULL_STATE, + dcspb.Detector_MID: dcspb.DetectorState_NULL_STATE, + }, + args: args{conditionState: dcspb.DetectorState_PFR_AVAILABLE}, + want: true, + wantErr: true, + }, + { + name: "condition=SOR_AVAILABLE/states=NULL", + dsm: map[dcspb.Detector]dcspb.DetectorState{ + dcspb.Detector_ZDC: dcspb.DetectorState_NULL_STATE, + dcspb.Detector_FT0: dcspb.DetectorState_NULL_STATE, + dcspb.Detector_MID: dcspb.DetectorState_NULL_STATE, + }, + args: args{conditionState: dcspb.DetectorState_SOR_AVAILABLE}, + want: true, + wantErr: true, + }, + { + name: "condition=PFR_AVAILABLE/states=NULL,PFR_UNAVAILABLE", + dsm: map[dcspb.Detector]dcspb.DetectorState{ + dcspb.Detector_ZDC: dcspb.DetectorState_NULL_STATE, + dcspb.Detector_FT0: dcspb.DetectorState_NULL_STATE, + dcspb.Detector_MID: dcspb.DetectorState_NULL_STATE, + dcspb.Detector_EMC: dcspb.DetectorState_PFR_UNAVAILABLE, + }, + args: args{conditionState: dcspb.DetectorState_PFR_AVAILABLE}, + want: false, + wantErr: true, + }, + { + name: "condition=PFR_AVAILABLE/states=NULL,PFR_AVAILABLE", + dsm: map[dcspb.Detector]dcspb.DetectorState{ + dcspb.Detector_ZDC: dcspb.DetectorState_NULL_STATE, + dcspb.Detector_FT0: dcspb.DetectorState_NULL_STATE, + dcspb.Detector_MID: dcspb.DetectorState_NULL_STATE, + dcspb.Detector_EMC: dcspb.DetectorState_PFR_AVAILABLE, + }, + args: args{conditionState: dcspb.DetectorState_PFR_AVAILABLE}, + want: true, + wantErr: true, + }, + { + name: "condition=PFR_AVAILABLE/states=NULL,PFR_AVAILABLE,PFR_UNAVAILABLE", + dsm: map[dcspb.Detector]dcspb.DetectorState{ + dcspb.Detector_ZDC: dcspb.DetectorState_NULL_STATE, + dcspb.Detector_FT0: dcspb.DetectorState_NULL_STATE, + dcspb.Detector_MID: dcspb.DetectorState_NULL_STATE, + dcspb.Detector_EMC: dcspb.DetectorState_PFR_AVAILABLE, + dcspb.Detector_HMP: dcspb.DetectorState_PFR_UNAVAILABLE, + }, + args: args{conditionState: dcspb.DetectorState_PFR_AVAILABLE}, + want: false, + wantErr: true, + }, + { + name: "condition=PFR_AVAILABLE/states=PFR_AVAILABLE,PFR_UNAVAILABLE", + dsm: map[dcspb.Detector]dcspb.DetectorState{ + dcspb.Detector_ZDC: dcspb.DetectorState_PFR_AVAILABLE, + dcspb.Detector_FT0: dcspb.DetectorState_PFR_AVAILABLE, + dcspb.Detector_MID: dcspb.DetectorState_PFR_AVAILABLE, + dcspb.Detector_EMC: dcspb.DetectorState_PFR_AVAILABLE, + dcspb.Detector_HMP: dcspb.DetectorState_PFR_UNAVAILABLE, + }, + args: args{conditionState: dcspb.DetectorState_PFR_AVAILABLE}, + want: false, + wantErr: true, + }, + { + name: "condition=SOR_AVAILABLE/states=PFR_AVAILABLE,PFR_UNAVAILABLE", + dsm: map[dcspb.Detector]dcspb.DetectorState{ + dcspb.Detector_ZDC: dcspb.DetectorState_PFR_AVAILABLE, + dcspb.Detector_FT0: dcspb.DetectorState_PFR_AVAILABLE, + dcspb.Detector_MID: dcspb.DetectorState_PFR_AVAILABLE, + dcspb.Detector_EMC: dcspb.DetectorState_PFR_AVAILABLE, + dcspb.Detector_HMP: dcspb.DetectorState_PFR_UNAVAILABLE, + }, + args: args{conditionState: dcspb.DetectorState_SOR_AVAILABLE}, + want: false, + wantErr: true, + }, + { + name: "condition=PFR_AVAILABLE/states=PFR_AVAILABLE,PFR_UNAVAILABLE,DEAD,ERROR,RUN_INHIBIT", + dsm: map[dcspb.Detector]dcspb.DetectorState{ + dcspb.Detector_ZDC: dcspb.DetectorState_DEAD, + dcspb.Detector_FT0: dcspb.DetectorState_ERROR, + dcspb.Detector_MID: dcspb.DetectorState_RUN_INHIBIT, + dcspb.Detector_EMC: dcspb.DetectorState_PFR_AVAILABLE, + dcspb.Detector_HMP: dcspb.DetectorState_PFR_UNAVAILABLE, + }, + args: args{conditionState: dcspb.DetectorState_PFR_AVAILABLE}, + want: false, + wantErr: true, + }, + { + name: "condition=PFR_AVAILABLE/states=PFR_AVAILABLE", + dsm: map[dcspb.Detector]dcspb.DetectorState{ + dcspb.Detector_ZDC: dcspb.DetectorState_PFR_AVAILABLE, + dcspb.Detector_FT0: dcspb.DetectorState_PFR_AVAILABLE, + dcspb.Detector_MID: dcspb.DetectorState_PFR_AVAILABLE, + dcspb.Detector_EMC: dcspb.DetectorState_PFR_AVAILABLE, + dcspb.Detector_HMP: dcspb.DetectorState_PFR_AVAILABLE, + }, + args: args{conditionState: dcspb.DetectorState_PFR_AVAILABLE}, + want: true, + wantErr: false, + }, + { + name: "condition=SOR_AVAILABLE/states=PFR_AVAILABLE", + dsm: map[dcspb.Detector]dcspb.DetectorState{ + dcspb.Detector_ZDC: dcspb.DetectorState_PFR_AVAILABLE, + dcspb.Detector_FT0: dcspb.DetectorState_PFR_AVAILABLE, + dcspb.Detector_MID: dcspb.DetectorState_PFR_AVAILABLE, + dcspb.Detector_EMC: dcspb.DetectorState_PFR_AVAILABLE, + dcspb.Detector_HMP: dcspb.DetectorState_PFR_AVAILABLE, + }, + args: args{conditionState: dcspb.DetectorState_SOR_AVAILABLE}, + want: false, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, _, _, err := tt.dsm.compatibleWithDCSOperation(tt.args.conditionState) + if (err != nil) != tt.wantErr { + t.Errorf("compatibleWithDCSOperation() error = %v, wantErr %v", err, tt.wantErr) + return + } else if (err != nil) == tt.wantErr { + t.Logf("compatibleWithDCSOperation() error = %v, wantErr %v", err, tt.wantErr) + } + + if got != tt.want { + if err != nil { + t.Errorf("compatibleWithDCSOperation() got = %v, want %v, error = %s", got, tt.want, err.Error()) + } else { + t.Errorf("compatibleWithDCSOperation() got = %v, want %v", got, tt.want) + } + } + }) + } +} diff --git a/core/integration/ddsched/client.go b/core/integration/ddsched/client.go new file mode 100644 index 000000000..a24b37365 --- /dev/null +++ b/core/integration/ddsched/client.go @@ -0,0 +1,149 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2021 CERN and copyright holders of ALICE O². + * Author: Teo Mrnjavac + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package ddsched + +import ( + "context" + "time" + + "github.com/AliceO2Group/Control/common/logger" + "github.com/AliceO2Group/Control/common/monitoring" + ddpb "github.com/AliceO2Group/Control/core/integration/ddsched/protos" + "github.com/sirupsen/logrus" + "github.com/spf13/viper" + "google.golang.org/grpc" + "google.golang.org/grpc/backoff" + "google.golang.org/grpc/connectivity" + "google.golang.org/grpc/keepalive" +) + +var log = logger.New(logrus.StandardLogger(), "ddschedclient") + +func convertMethodName(method string) (converted string) { + switch method { + case ddpb.DataDistributionControl_PartitionInitialize_FullMethodName: + converted = "PartInit" + case ddpb.DataDistributionControl_PartitionTerminate_FullMethodName: + converted = "PartTerm" + case ddpb.DataDistributionControl_PartitionStatus_FullMethodName: + converted = "PartStatus" + default: + converted = "Unknown" + } + return +} + +type RpcClient struct { + ddpb.DataDistributionControlClient + conn *grpc.ClientConn + cancel context.CancelFunc +} + +func NewClient(cxt context.Context, cancel context.CancelFunc, endpoint string) *RpcClient { + log.WithFields(logrus.Fields{ + "endpoint": endpoint, + }).Debug("dialing DD scheduler endpoint") + + dialOptions := []grpc.DialOption{ + grpc.WithInsecure(), + grpc.WithConnectParams(grpc.ConnectParams{ + Backoff: backoff.Config{ + BaseDelay: backoff.DefaultConfig.BaseDelay, + Multiplier: backoff.DefaultConfig.Multiplier, + Jitter: backoff.DefaultConfig.Jitter, + MaxDelay: 20 * time.Second, + }, + MinConnectTimeout: 10 * time.Second, + }), + grpc.WithKeepaliveParams(keepalive.ClientParameters{ + Time: 5 * time.Second, + Timeout: time.Second, + PermitWithoutStream: true, + }), + grpc.WithUnaryInterceptor(monitoring.SetupUnaryClientInterceptor("ddsched", convertMethodName)), + } + if !viper.GetBool("ddSchedulerUseSystemProxy") { + dialOptions = append(dialOptions, grpc.WithNoProxy()) + } + conn, err := grpc.DialContext(cxt, + endpoint, + dialOptions..., + ) + if err != nil { + log.WithField("error", err.Error()). + WithField("endpoint", endpoint). + Errorf("cannot dial RPC endpoint") + cancel() + return nil + } + + go func() { + connState := connectivity.Idle + stateChangedNotify := make(chan bool) + notifyFunc := func(st connectivity.State) { + ok := conn.WaitForStateChange(cxt, st) + stateChangedNotify <- ok + } + go notifyFunc(connState) + + for { + select { + case ok := <-stateChangedNotify: + if !ok { + return + } + connState = conn.GetState() + log.Debugf("DD scheduler client %s", connState.String()) + go notifyFunc(connState) + case <-time.After(2 * time.Minute): + if conn.GetState() != connectivity.Ready { + conn.ResetConnectBackoff() + } + case <-cxt.Done(): + return + } + } + }() + + client := &RpcClient{ + DataDistributionControlClient: ddpb.NewDataDistributionControlClient(conn), + conn: conn, + cancel: cancel, + } + + return client +} + +func (m *RpcClient) GetConnState() connectivity.State { + if m.conn == nil { + return connectivity.Idle + } + return m.conn.GetState() +} + +func (m *RpcClient) Close() error { + m.cancel() + return m.conn.Close() +} diff --git a/core/integration/ddsched/plugin.go b/core/integration/ddsched/plugin.go new file mode 100644 index 000000000..c6d3e1dd6 --- /dev/null +++ b/core/integration/ddsched/plugin.go @@ -0,0 +1,1149 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2021-2024 CERN and copyright holders of ALICE O². + * Author: Teo Mrnjavac + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +//go:generate protoc --go_out=. --go_opt=paths=source_relative --go-grpc_opt=paths=source_relative --go-grpc_out=require_unimplemented_servers=false:. protos/ddsched.proto + +// Package ddsched provides integration with the Data Distribution (DD) scheduler +// for managing the pool of FLPs participating in data taking operations. +package ddsched + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "net/url" + "strconv" + "strings" + "time" + + "github.com/AliceO2Group/Control/common/event/topic" + "github.com/AliceO2Group/Control/common/logger/infologger" + "github.com/AliceO2Group/Control/common/monitoring" + pb "github.com/AliceO2Group/Control/common/protos" + "github.com/AliceO2Group/Control/common/utils/uid" + "github.com/AliceO2Group/Control/core/environment" + "github.com/AliceO2Group/Control/core/integration" + ddpb "github.com/AliceO2Group/Control/core/integration/ddsched/protos" + "github.com/AliceO2Group/Control/core/the" + "github.com/AliceO2Group/Control/core/workflow" + "github.com/AliceO2Group/Control/core/workflow/callable" + "github.com/spf13/viper" + "google.golang.org/grpc" + "google.golang.org/grpc/connectivity" +) + +const ( + DDSCHED_DIAL_TIMEOUT = 2 * time.Second + DDSCHED_INITIALIZE_TIMEOUT = 30 * time.Second + DDSCHED_TERMINATE_TIMEOUT = 30 * time.Second + TOPIC = topic.IntegratedService + topic.Separator + "ddsched" +) + +type Plugin struct { + ddSchedulerHost string + ddSchedulerPort int + + ddSchedClient *RpcClient +} + +func NewPlugin(endpoint string) integration.Plugin { + u, err := url.Parse(endpoint) + if err != nil { + log.WithField("endpoint", endpoint). + WithError(err). + Error("bad service endpoint") + return nil + } + + portNumber, _ := strconv.Atoi(u.Port()) + + return &Plugin{ + ddSchedulerHost: u.Hostname(), + ddSchedulerPort: portNumber, + ddSchedClient: nil, + } +} + +func (p *Plugin) GetName() string { + return "ddsched" +} + +func (p *Plugin) GetPrettyName() string { + return "DD (EPN DataDistribution TfScheduler)" +} + +func (p *Plugin) GetEndpoint() string { + return viper.GetString("ddSchedulerEndpoint") +} + +func (p *Plugin) GetConnectionState() string { + if p == nil || p.ddSchedClient == nil { + return "UNKNOWN" + } + return p.ddSchedClient.conn.GetState().String() +} + +func (p *Plugin) GetData(_ []any) string { + if p == nil || p.ddSchedClient == nil { + return "" + } + + partitionStates := make(map[string]string) + environmentIds := environment.ManagerInstance().Ids() + + partitionInfos := p.partitionStatesForEnvs(environmentIds) + + partitionStates = make(map[string]string) + for envId, partitionInfo := range partitionInfos { + state, ok := partitionInfo["state"] + if !ok { + continue + } + partitionStates[envId.String()] = state + } + + out, err := json.Marshal(partitionStates) + if err != nil { + return "" + } + return string(out[:]) +} + +func (p *Plugin) GetEnvironmentsData(envIds []uid.ID) map[uid.ID]string { + if p == nil || p.ddSchedClient == nil { + return nil + } + + partitionInfos := p.partitionStatesForEnvs(envIds) + + partitionInfosOut := make(map[uid.ID]string) + + for envId, partitionInfo := range partitionInfos { + partitionInfoOut, err := json.Marshal(partitionInfo) + if err != nil { + continue + } + partitionInfosOut[envId] = string(partitionInfoOut[:]) + } + + return partitionInfosOut +} + +func (p *Plugin) GetEnvironmentsShortData(envIds []uid.ID) map[uid.ID]string { + return p.GetEnvironmentsData(envIds) +} + +func (p *Plugin) partitionStatesForEnvs(envIds []uid.ID) map[uid.ID]map[string]string { + partitionStates := make(map[uid.ID]map[string]string) + + for _, envId := range envIds { + in := ddpb.PartitionInfo{ + PartitionId: envId.String(), + EnvironmentId: envId.String(), + } + ctx, cancel := context.WithTimeout(context.Background(), viper.GetDuration("ddschedStatusTimeout")) + ctx = monitoring.AddEnvAndRunType(ctx, envId.String(), "none") + state, err := p.ddSchedClient.PartitionStatus(ctx, &in, grpc.EmptyCallOption{}) + cancel() + if err != nil { + continue + } + if state == nil { + continue + } + partitionState, ok := ddpb.PartitionState_name[int32(state.GetPartitionState())] + if !ok { + continue + } + partitionInfo := map[string]string{ + "state": partitionState, + "infoMessage": state.GetInfoMessage(), + } + partitionStates[envId] = partitionInfo + } + + return partitionStates +} + +func (p *Plugin) Init(_ string) error { + if p.ddSchedClient == nil { + cxt, cancel := context.WithCancel(context.Background()) + p.ddSchedClient = NewClient(cxt, cancel, viper.GetString("ddSchedulerEndpoint")) + if p.ddSchedClient == nil { + return fmt.Errorf("failed to connect to DD scheduler service on %s", viper.GetString("ddSchedulerEndpoint")) + } + log.Debug("DD scheduler plugin initialized") + } + return nil +} + +func (p *Plugin) ObjectStack(_ map[string]string, _ map[string]string) (stack map[string]interface{}) { + stack = make(map[string]interface{}) + return stack +} + +func (p *Plugin) CallStack(data interface{}) (stack map[string]interface{}) { + call, ok := data.(*callable.Call) + if !ok { + return + } + varStack := call.VarStack + envId, ok := varStack["environment_id"] + if !ok { + log.Error("cannot acquire environment ID") + return + } + + stack = make(map[string]interface{}) + stack["PartitionInitialize"] = func() (out string) { // must formally return string even when we return nothing + log.WithField("partition", envId). + Debug("performing DD scheduler PartitionInitialize") + + var err error + callFailedStr := "DDsched PartitionInitialize call failed" + + parentRoleI := call.GetParentRole() + parentRole, ok := parentRoleI.(workflow.Role) + if !ok { + err = errors.New("internal error: cannot acquire parent role") + + log.WithError(err). + WithField("level", infologger.IL_Support). + WithField("partition", envId). + WithField("call", "PartitionInitialize"). + Error("DDsched error") + + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + + return + } + root := workflow.GetRoot(parentRole) + + stfbHostIdMap := make(map[string]string) + stfsHostIdMap := make(map[string]string) + + workflow.LeafWalk(root, func(role workflow.Role) { + roleVS, err := role.ConsolidatedVarStack() + if err != nil { + log.WithError(err). + WithField("partition", envId). + Error("error processing DD host_id_map") + return + } + var ( + ddDiscoveryIbHostname, ddDiscoveryStfbId, ddDiscoveryStfsId string + ddDiscoveryIbHostnameOk, ddDiscoveryStfbIdOk, ddDiscoveryStfsIdOk bool + ) + ddDiscoveryIbHostname, ddDiscoveryIbHostnameOk = roleVS["dd_discovery_ib_hostname"] + ddDiscoveryStfbId, ddDiscoveryStfbIdOk = roleVS["dd_discovery_stfb_id"] + ddDiscoveryStfsId, ddDiscoveryStfsIdOk = roleVS["dd_discovery_stfs_id"] + if ddDiscoveryIbHostnameOk { + if ddDiscoveryStfsIdOk { + stfsHostIdMap[ddDiscoveryStfsId] = ddDiscoveryIbHostname + } else if ddDiscoveryStfbIdOk { + stfbHostIdMap[ddDiscoveryStfbId] = ddDiscoveryIbHostname + } else { + return + } + } else { + return + } + }) + + partitionParams := make(map[string]string) + + // FIXME: this only copies over vars prefixed with "ddsched_" + // Figure out a better way! + for k, v := range varStack { + if strings.HasPrefix(k, "ddsched_") && k != "ddsched_enabled" { + partitionParams[strings.TrimPrefix(k, "ddsched_")] = v + } + } + + in := ddpb.PartitionInitRequest{ + PartitionInfo: &ddpb.PartitionInfo{ + EnvironmentId: envId, + PartitionId: envId, + }, + StfbHostIdMap: stfbHostIdMap, + StfsHostIdMap: stfsHostIdMap, + PartitionParams: partitionParams, + } + + if p.ddSchedClient == nil { + err = fmt.Errorf("DD scheduler plugin not initialized, PartitionInitialize impossible") + + log.WithError(err). + WithField("level", infologger.IL_Support). + WithField("partition", envId). + WithField("call", "PartitionInitialize"). + Error("DDsched error") + + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + + return + } + + if p.ddSchedClient.GetConnState() == connectivity.Shutdown { + err = fmt.Errorf("DD scheduler client connection not available, PartitionInitialize impossible") + + log.WithError(err). + WithField("level", infologger.IL_Support). + WithField("partition", envId). + WithField("call", "PartitionInitialize"). + Error("DDsched error") + + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + + return + } + + var response *ddpb.PartitionResponse + timeout := callable.AcquireTimeout(DDSCHED_INITIALIZE_TIMEOUT, varStack, "Initialize", envId) + ctx, cancel := integration.NewContext(envId, varStack, timeout) + defer cancel() + + payload := map[string]interface{}{ + "ddschedRequest": &in, + } + payloadJson, _ := json.Marshal(payload) + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_STARTED, + OperationStep: "perform DD scheduler call: PartitionInitialize", + OperationStepStatus: pb.OpStatus_STARTED, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + }) + + response, err = p.ddSchedClient.PartitionInitialize(ctx, &in, grpc.EmptyCallOption{}) + if err != nil { + log.WithError(err). + WithField("level", infologger.IL_Support). + WithField("partition", envId). + WithField("endpoint", viper.GetString("ddSchedulerEndpoint")). + WithField("call", "PartitionInitialize"). + Error("DDsched error") + + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_ERROR, + OperationStep: "perform DD scheduler call: PartitionInitialize", + OperationStepStatus: pb.OpStatus_DONE_ERROR, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + Error: err.Error(), + }) + + return + } + if response.PartitionState != ddpb.PartitionState_PARTITION_CONFIGURING && + response.PartitionState != ddpb.PartitionState_PARTITION_CONFIGURED { + err = fmt.Errorf("PartitionInitialize returned unexpected state %s (expected: PARTITION_CONFIGURING)", response.PartitionState.String()) + + log.WithError(err). + WithField("level", infologger.IL_Support). + WithField("partition", envId). + WithField("endpoint", viper.GetString("ddSchedulerEndpoint")). + WithField("call", "PartitionInitialize"). + Error("DDsched error") + + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + + payload["ddschedResponse"] = &response + payloadJson, _ = json.Marshal(payload) + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_ERROR, + OperationStep: "perform DD scheduler call: PartitionInitialize", + OperationStepStatus: pb.OpStatus_DONE_ERROR, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + Error: err.Error(), + }) + + return + } + + payload["ddschedResponse"] = &response + payloadJson, _ = json.Marshal(payload) + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_ONGOING, + OperationStep: "perform DD scheduler call: PartitionInitialize", + OperationStepStatus: pb.OpStatus_DONE_OK, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + }) + + payload = map[string]interface{}{ + "lastKnownState": response.PartitionState.String(), + } + payloadJson, _ = json.Marshal(payload) + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_ONGOING, + OperationStep: "poll for DD partition state", + OperationStepStatus: pb.OpStatus_STARTED, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + }) + + PARTITION_STATE_POLLING: + for ctx.Err() == nil { + response, err = p.ddSchedClient.PartitionStatus(ctx, in.PartitionInfo, grpc.EmptyCallOption{}) + if err != nil { + log.WithError(err). + WithField("level", infologger.IL_Support). + WithField("partition", envId). + WithField("endpoint", viper.GetString("ddSchedulerEndpoint")). + WithField("call", "PartitionStatus"). + WithField("timeout", timeout.String()). + Error("DDsched error, will keep retying until timeout") + + // The query failed, we'll keep retrying until timeout + time.Sleep(100 * time.Millisecond) + continue + } + + switch lastKnownState := response.PartitionState; lastKnownState { + case ddpb.PartitionState_PARTITION_CONFIGURING: + payload["lastKnownState"] = lastKnownState.String() + payloadJson, _ = json.Marshal(payload) + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_ONGOING, + OperationStep: "poll for DD partition state", + OperationStepStatus: pb.OpStatus_ONGOING, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + }) + + time.Sleep(100 * time.Millisecond) + case ddpb.PartitionState_PARTITION_CONFIGURED: + payload["lastKnownState"] = lastKnownState.String() + payloadJson, _ = json.Marshal(payload) + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_OK, + OperationStep: "poll for DD partition state", + OperationStepStatus: pb.OpStatus_DONE_OK, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + }) + + break PARTITION_STATE_POLLING + default: + err = fmt.Errorf("PartitionInitialize landed on unexpected state %s (expected: PARTITION_CONFIGURED)", response.PartitionState.String()) + + log.WithError(err). + WithField("level", infologger.IL_Support). + WithField("partition", envId). + WithField("endpoint", viper.GetString("ddSchedulerEndpoint")). + WithField("call", "PartitionInitialize"). + Error("DDsched error") + + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + + payload["lastKnownState"] = lastKnownState.String() + payloadJson, _ = json.Marshal(payload) + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_ERROR, + OperationStep: "poll for DD partition state", + OperationStepStatus: pb.OpStatus_DONE_ERROR, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + Error: err.Error(), + }) + + break PARTITION_STATE_POLLING + } + + if ctx.Err() != nil { + err = fmt.Errorf("PartitionInitialize timeout exceeded. Latest state %s (expected: PARTITION_CONFIGURED)", response.PartitionState.String()) + + log.WithError(err). + WithField("level", infologger.IL_Support). + WithField("partition", envId). + WithField("endpoint", viper.GetString("ddSchedulerEndpoint")). + WithField("call", "PartitionInitialize"). + WithField("timeout", timeout). + Error("DDsched error") + + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + + payload["lastKnownState"] = response.PartitionState.String() + payloadJson, _ = json.Marshal(payload) + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_TIMEOUT, + OperationStep: "poll for DD partition state", + OperationStepStatus: pb.OpStatus_DONE_TIMEOUT, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + Error: err.Error(), + }) + + break + } + } + return + } + stack["PartitionTerminate"] = func() (out string) { // must formally return string even when we return nothing + log.WithField("partition", envId). + Debug("performing DD scheduler PartitionTerminate") + + in := ddpb.PartitionTermRequest{ + PartitionInfo: &ddpb.PartitionInfo{ + EnvironmentId: envId, + PartitionId: envId, + }, + } + var err error + callFailedStr := "DDsched PartitionTerminate call failed" + + if p.ddSchedClient == nil { + err = fmt.Errorf("DD scheduler plugin not initialized, PartitionTerminate impossible") + + log.WithError(err). + WithField("level", infologger.IL_Support). + WithField("partition", envId). + WithField("endpoint", viper.GetString("ddSchedulerEndpoint")). + WithField("call", "PartitionTerminate"). + Error("DDsched error") + + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + + return + } + if p.ddSchedClient.GetConnState() == connectivity.Shutdown { + err = fmt.Errorf("DD scheduler client connection not available, PartitionTerminate impossible") + + log.WithError(err). + WithField("level", infologger.IL_Support). + WithField("partition", envId). + WithField("endpoint", viper.GetString("ddSchedulerEndpoint")). + WithField("call", "PartitionTerminate"). + Error("DDsched error") + + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + + return + } + + var response *ddpb.PartitionResponse + timeout := callable.AcquireTimeout(DDSCHED_TERMINATE_TIMEOUT, varStack, "Terminate", envId) + ctx, cancel := integration.NewContext(envId, varStack, timeout) + defer cancel() + + payload := map[string]interface{}{ + "ddschedRequest": &in, + } + payloadJson, _ := json.Marshal(payload) + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_STARTED, + OperationStep: "perform DD scheduler call: PartitionTerminate", + OperationStepStatus: pb.OpStatus_STARTED, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + }) + + response, err = p.ddSchedClient.PartitionTerminate(ctx, &in, grpc.EmptyCallOption{}) + if err != nil { + log.WithError(err). + WithField("level", infologger.IL_Support). + WithField("partition", envId). + WithField("endpoint", viper.GetString("ddSchedulerEndpoint")). + WithField("call", "PartitionTerminate"). + Error("DDsched error") + + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_ERROR, + OperationStep: "perform DD scheduler call: PartitionTerminate", + OperationStepStatus: pb.OpStatus_DONE_ERROR, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + Error: err.Error(), + }) + + return + } + + if response.PartitionState != ddpb.PartitionState_PARTITION_TERMINATING && + response.PartitionState != ddpb.PartitionState_PARTITION_TERMINATED { + err = fmt.Errorf("PartitionTerminate returned unexpected state %s (expected: PARTITION_TERMINATING)", response.PartitionState.String()) + + log.WithError(err). + WithField("level", infologger.IL_Support). + WithField("partition", envId). + WithField("endpoint", viper.GetString("ddSchedulerEndpoint")). + WithField("call", "PartitionTerminate"). + Error("DDsched error") + + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + + payload["ddschedResponse"] = &response + payloadJson, _ = json.Marshal(payload) + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_ERROR, + OperationStep: "perform DD scheduler call: PartitionTerminate", + OperationStepStatus: pb.OpStatus_DONE_ERROR, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + Error: err.Error(), + }) + + return + } + + payload["ddschedResponse"] = &response + payloadJson, _ = json.Marshal(payload) + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_ONGOING, + OperationStep: "perform DD scheduler call: PartitionTerminate", + OperationStepStatus: pb.OpStatus_DONE_OK, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + }) + + payload = map[string]interface{}{ + "lastKnownState": response.PartitionState.String(), + } + payloadJson, _ = json.Marshal(payload) + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_ONGOING, + OperationStep: "poll for DD partition state", + OperationStepStatus: pb.OpStatus_STARTED, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + }) + + PARTITION_STATE_POLLING: + for ctx.Err() == nil { + response, err = p.ddSchedClient.PartitionStatus(ctx, in.PartitionInfo, grpc.EmptyCallOption{}) + if err != nil { + log.WithError(err). + WithField("level", infologger.IL_Support). + WithField("partition", envId). + WithField("endpoint", viper.GetString("ddSchedulerEndpoint")). + WithField("call", "PartitionStatus"). + WithField("timeout", timeout.String()). + Error("DDsched error, will keep retying until timeout") + + // The query failed, we'll keep retrying until timeout + time.Sleep(100 * time.Millisecond) + continue + } + + switch lastKnownState := response.PartitionState; lastKnownState { + case ddpb.PartitionState_PARTITION_TERMINATING: + payload["lastKnownState"] = lastKnownState.String() + payloadJson, _ = json.Marshal(payload) + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_ONGOING, + OperationStep: "poll for DD partition state", + OperationStepStatus: pb.OpStatus_ONGOING, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + }) + + time.Sleep(100 * time.Millisecond) + case ddpb.PartitionState_PARTITION_TERMINATED: + payload["lastKnownState"] = lastKnownState.String() + payloadJson, _ = json.Marshal(payload) + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_OK, + OperationStep: "poll for DD partition state", + OperationStepStatus: pb.OpStatus_DONE_OK, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + }) + + break PARTITION_STATE_POLLING + default: + err = fmt.Errorf("PartitionTerminate landed on unexpected state %s (expected: PARTITION_TERMINATED)", response.PartitionState.String()) + + log.WithError(err). + WithField("level", infologger.IL_Support). + WithField("partition", envId). + WithField("endpoint", viper.GetString("ddSchedulerEndpoint")). + WithField("call", "PartitionTerminate"). + Error("DDsched error") + + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + + payload["lastKnownState"] = lastKnownState.String() + payloadJson, _ = json.Marshal(payload) + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_ERROR, + OperationStep: "poll for DD partition state", + OperationStepStatus: pb.OpStatus_DONE_ERROR, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + Error: err.Error(), + }) + + break PARTITION_STATE_POLLING + } + if ctx.Err() != nil { + err = fmt.Errorf("PartitionTerminate timeout exceeded. Latest state %s (expected: PARTITION_TERMINATED)", response.PartitionState.String()) + + log.WithError(err). + WithField("level", infologger.IL_Support). + WithField("partition", envId). + WithField("endpoint", viper.GetString("ddSchedulerEndpoint")). + WithField("call", "PartitionTerminate"). + WithField("timeout", timeout). + Error("DDsched error") + + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + + payload["lastKnownState"] = response.PartitionState.String() + payloadJson, _ = json.Marshal(payload) + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_TIMEOUT, + OperationStep: "poll for DD partition state", + OperationStepStatus: pb.OpStatus_DONE_TIMEOUT, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + Error: err.Error(), + }) + + break + } + } + return + } + stack["EnsureTermination"] = func() (out string) { + log.WithField("partition", envId). + Debug("performing DD scheduler session cleanup") + + var err error + callFailedStr := "DDsched EnsureTermination call failed" + + if p.ddSchedClient == nil { + err = fmt.Errorf("DD scheduler plugin not initialized, EnsureTermination impossible") + + log.WithError(err). + WithField("level", infologger.IL_Support). + WithField("partition", envId). + WithField("endpoint", viper.GetString("ddSchedulerEndpoint")). + WithField("call", "EnsureTermination"). + Error("DDsched error") + + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + + return + } + if p.ddSchedClient.GetConnState() == connectivity.Shutdown { + err = fmt.Errorf("DD scheduler client connection not available, EnsureTermination impossible") + + log.WithError(err). + WithField("level", infologger.IL_Support). + WithField("partition", envId). + WithField("endpoint", viper.GetString("ddSchedulerEndpoint")). + WithField("call", "EnsureTermination"). + Error("DDsched error") + + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + + return + } + + var response *ddpb.PartitionResponse + + infoReq := ddpb.PartitionInfo{ + EnvironmentId: envId, + PartitionId: envId, + } + timeout := callable.AcquireTimeout(DDSCHED_TERMINATE_TIMEOUT, varStack, "Terminate", envId) + ctx, cancel := integration.NewContext(envId, varStack, timeout) + defer cancel() + + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_STARTED, + OperationStep: "check DD partition status", + OperationStepStatus: pb.OpStatus_STARTED, + EnvironmentId: envId, + }) + + response, err = p.ddSchedClient.PartitionStatus(ctx, &infoReq, grpc.EmptyCallOption{}) + if err != nil { + log.WithError(err). + WithField("level", infologger.IL_Support). + WithField("partition", envId). + WithField("endpoint", viper.GetString("ddSchedulerEndpoint")). + WithField("call", "EnsureTermination"). + Error("DDsched error") + + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_ERROR, + OperationStep: "check DD partition status", + OperationStepStatus: pb.OpStatus_DONE_ERROR, + EnvironmentId: envId, + Error: err.Error(), + }) + + return + } + + if response == nil { + err = errors.New("nil response") + + log.WithError(err). + WithField("level", infologger.IL_Support). + WithField("partition", envId). + WithField("endpoint", viper.GetString("ddSchedulerEndpoint")). + WithField("call", "EnsureTermination"). + Error("DDsched error") + + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_ERROR, + OperationStep: "check DD partition status", + OperationStepStatus: pb.OpStatus_DONE_ERROR, + EnvironmentId: envId, + Error: err.Error(), + }) + + return + } + + // If the partition is anything other than UNKNOWN, TERMINATING or TERMINATED, + // we need to trigger a PartitionTerminate. + // This usually happens when the PartitionTerminate hook call was skipped due + // to a failed deployment or other environment error. + if response.PartitionState == ddpb.PartitionState_PARTITION_UNKNOWN || + response.PartitionState == ddpb.PartitionState_PARTITION_TERMINATING || + response.PartitionState == ddpb.PartitionState_PARTITION_TERMINATED { + // DDsched is in an acceptable state, so we return + log.WithField("endpoint", viper.GetString("ddSchedulerEndpoint")). + WithField("partition", envId). + WithField("partition_state", response.PartitionState). + Trace("DD scheduler session cleanup not needed") + + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_OK, + OperationStep: "check DD partition status", + OperationStepStatus: pb.OpStatus_DONE_OK, + EnvironmentId: envId, + }) + + return + } + + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_ONGOING, + OperationStep: "check DD partition status", + OperationStepStatus: pb.OpStatus_DONE_OK, + EnvironmentId: envId, + Error: "partition still exists, cleanup needed", + }) + + // No guarantee that the DDsched partition is in an obedient state or able + // to perform control commands, but if it's CONFIGURED we should be able + // call PartitionTerminate + in := ddpb.PartitionTermRequest{ + PartitionInfo: &ddpb.PartitionInfo{ + EnvironmentId: envId, + PartitionId: envId, + }, + } + + log.WithField("endpoint", viper.GetString("ddSchedulerEndpoint")). + WithField("partition", envId). + WithField("partition_state", response.PartitionState). + WithField("level", infologger.IL_Support). + Warn("DD scheduler partition still active, performing PartitionTerminate") + + payload := map[string]interface{}{ + "ddschedRequest": &in, + } + payloadJson, _ := json.Marshal(payload) + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_ONGOING, + OperationStep: "perform DD scheduler call: PartitionTerminate", + OperationStepStatus: pb.OpStatus_STARTED, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + }) + + response, err = p.ddSchedClient.PartitionTerminate(ctx, &in, grpc.EmptyCallOption{}) + if err != nil { + log.WithError(err). + WithField("level", infologger.IL_Support). + WithField("partition", envId). + WithField("endpoint", viper.GetString("ddSchedulerEndpoint")). + WithField("call", "EnsureTermination"). + Error("DDsched error") + + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_ERROR, + OperationStep: "perform DD scheduler call: PartitionTerminate", + OperationStepStatus: pb.OpStatus_DONE_ERROR, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + Error: err.Error(), + }) + + return + } + if response.PartitionState != ddpb.PartitionState_PARTITION_TERMINATING && + response.PartitionState != ddpb.PartitionState_PARTITION_TERMINATED { + err = fmt.Errorf("PartitionTerminate returned unexpected state %s (expected: PARTITION_TERMINATING)", response.PartitionState.String()) + + log.WithError(err). + WithField("level", infologger.IL_Support). + WithField("partition", envId). + WithField("endpoint", viper.GetString("ddSchedulerEndpoint")). + WithField("call", "PartitionTerminate"). + Error("DDsched error") + + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + + payload["ddschedResponse"] = &response + payloadJson, _ = json.Marshal(payload) + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_ERROR, + OperationStep: "perform DD scheduler call: PartitionTerminate", + OperationStepStatus: pb.OpStatus_DONE_ERROR, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + Error: err.Error(), + }) + + return + } + + payload["ddschedResponse"] = &response + payloadJson, _ = json.Marshal(payload) + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_ONGOING, + OperationStep: "perform DD scheduler call: PartitionTerminate", + OperationStepStatus: pb.OpStatus_DONE_OK, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + }) + + payload = map[string]interface{}{ + "lastKnownState": response.PartitionState.String(), + } + payloadJson, _ = json.Marshal(payload) + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_ONGOING, + OperationStep: "poll for DD partition state", + OperationStepStatus: pb.OpStatus_STARTED, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + }) + + PARTITION_STATE_POLLING: + for ctx.Err() == nil { + response, err = p.ddSchedClient.PartitionStatus(ctx, in.PartitionInfo, grpc.EmptyCallOption{}) + if err != nil { + log.WithError(err). + WithField("level", infologger.IL_Support). + WithField("partition", envId). + WithField("endpoint", viper.GetString("ddSchedulerEndpoint")). + WithField("call", "PartitionStatus"). + WithField("timeout", timeout.String()). + Error("DDsched error, will keep retying until timeout") + + // The query failed, we'll keep retrying until timeout + time.Sleep(100 * time.Millisecond) + continue + } + + switch lastKnownState := response.PartitionState; lastKnownState { + case ddpb.PartitionState_PARTITION_TERMINATING: + payload["lastKnownState"] = lastKnownState.String() + payloadJson, _ = json.Marshal(payload) + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_ONGOING, + OperationStep: "poll for DD partition state", + OperationStepStatus: pb.OpStatus_ONGOING, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + }) + + time.Sleep(100 * time.Millisecond) + case ddpb.PartitionState_PARTITION_TERMINATED: + payload["lastKnownState"] = lastKnownState.String() + payloadJson, _ = json.Marshal(payload) + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_OK, + OperationStep: "poll for DD partition state", + OperationStepStatus: pb.OpStatus_DONE_OK, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + }) + + break PARTITION_STATE_POLLING + default: + err = fmt.Errorf("PartitionTerminate landed on unexpected state %s (expected: PARTITION_TERMINATED)", response.PartitionState.String()) + + log.WithError(err). + WithField("level", infologger.IL_Support). + WithField("partition", envId). + WithField("endpoint", viper.GetString("ddSchedulerEndpoint")). + WithField("call", "EnsureTermination"). + Error("DDsched error") + + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + + payload["lastKnownState"] = lastKnownState.String() + payloadJson, _ = json.Marshal(payload) + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_ERROR, + OperationStep: "poll for DD partition state", + OperationStepStatus: pb.OpStatus_DONE_ERROR, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + Error: err.Error(), + }) + + break PARTITION_STATE_POLLING + } + if ctx.Err() != nil { + err = fmt.Errorf("PartitionTerminate timeout exceeded. Latest state %s (expected: PARTITION_TERMINATED)", response.PartitionState.String()) + + log.WithError(err). + WithField("level", infologger.IL_Support). + WithField("partition", envId). + WithField("endpoint", viper.GetString("ddSchedulerEndpoint")). + WithField("call", "EnsureTermination"). + WithField("timeout", timeout). + Error("DDsched error") + + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + + payload["lastKnownState"] = response.PartitionState.String() + payloadJson, _ = json.Marshal(payload) + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_TIMEOUT, + OperationStep: "poll for DD partition state", + OperationStepStatus: pb.OpStatus_DONE_TIMEOUT, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + Error: err.Error(), + }) + + break + } + } + return + } + return +} + +func (p *Plugin) Destroy() error { + return p.ddSchedClient.Close() +} diff --git a/core/integration/ddsched/protos/ddsched.pb.go b/core/integration/ddsched/protos/ddsched.pb.go new file mode 100644 index 000000000..fe89342bc --- /dev/null +++ b/core/integration/ddsched/protos/ddsched.pb.go @@ -0,0 +1,573 @@ +// Copyright 2019-2022 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \author Gvozden Nešković, Frankfurt Institute for Advanced Studies and Goethe University Frankfurt + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.34.1 +// protoc v5.29.3 +// source: protos/ddsched.proto + +// Changelog: +// 2021-02-08: - Initial version +// - Support for a single partition/environment: PartitionID = EnvironmentID + +package ddpb + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type PartitionState int32 + +const ( + PartitionState_IGNORE__ PartitionState = 0 + PartitionState_PARTITION_UNKNOWN PartitionState = 1 // Partition not known to DataDistControl. Initialize by calling PartitionInitialize() + PartitionState_PARTITION_ERROR PartitionState = 2 + PartitionState_PARTITION_REQUEST_INVALID PartitionState = 3 // Provided configuration does not meet requirements or the request is not accepted at the current state. + PartitionState_PARTITION_CONFIGURING PartitionState = 4 // Configuration accepted, waiting on FLP and EPN DD components to respond and connect. + PartitionState_PARTITION_CONFIGURED PartitionState = 5 // All components configured, ready for dataflow commands + PartitionState_PARTITION_TERMINATING PartitionState = 6 // Partition is terminating. EPN-FLP connections will be cleanly closed. + PartitionState_PARTITION_TERMINATED PartitionState = 7 // Partition is terminated. TfScheduler does not accept further requests +) + +// Enum value maps for PartitionState. +var ( + PartitionState_name = map[int32]string{ + 0: "IGNORE__", + 1: "PARTITION_UNKNOWN", + 2: "PARTITION_ERROR", + 3: "PARTITION_REQUEST_INVALID", + 4: "PARTITION_CONFIGURING", + 5: "PARTITION_CONFIGURED", + 6: "PARTITION_TERMINATING", + 7: "PARTITION_TERMINATED", + } + PartitionState_value = map[string]int32{ + "IGNORE__": 0, + "PARTITION_UNKNOWN": 1, + "PARTITION_ERROR": 2, + "PARTITION_REQUEST_INVALID": 3, + "PARTITION_CONFIGURING": 4, + "PARTITION_CONFIGURED": 5, + "PARTITION_TERMINATING": 6, + "PARTITION_TERMINATED": 7, + } +) + +func (x PartitionState) Enum() *PartitionState { + p := new(PartitionState) + *p = x + return p +} + +func (x PartitionState) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (PartitionState) Descriptor() protoreflect.EnumDescriptor { + return file_protos_ddsched_proto_enumTypes[0].Descriptor() +} + +func (PartitionState) Type() protoreflect.EnumType { + return &file_protos_ddsched_proto_enumTypes[0] +} + +func (x PartitionState) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use PartitionState.Descriptor instead. +func (PartitionState) EnumDescriptor() ([]byte, []int) { + return file_protos_ddsched_proto_rawDescGZIP(), []int{0} +} + +type PartitionInfo struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + EnvironmentId string `protobuf:"bytes,1,opt,name=environment_id,json=environmentId,proto3" json:"environment_id,omitempty"` + // AliECS environment ID (required) + PartitionId string `protobuf:"bytes,2,opt,name=partition_id,json=partitionId,proto3" json:"partition_id,omitempty"` // Partition ID. (required) +} + +func (x *PartitionInfo) Reset() { + *x = PartitionInfo{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_ddsched_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PartitionInfo) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PartitionInfo) ProtoMessage() {} + +func (x *PartitionInfo) ProtoReflect() protoreflect.Message { + mi := &file_protos_ddsched_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PartitionInfo.ProtoReflect.Descriptor instead. +func (*PartitionInfo) Descriptor() ([]byte, []int) { + return file_protos_ddsched_proto_rawDescGZIP(), []int{0} +} + +func (x *PartitionInfo) GetEnvironmentId() string { + if x != nil { + return x.EnvironmentId + } + return "" +} + +func (x *PartitionInfo) GetPartitionId() string { + if x != nil { + return x.PartitionId + } + return "" +} + +type PartitionResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + PartitionState PartitionState `protobuf:"varint,1,opt,name=partition_state,json=partitionState,proto3,enum=o2.DataDistribution.Control.PartitionState" json:"partition_state,omitempty"` // Current or new state of the partition following reception of the request. + InfoMessage string `protobuf:"bytes,2,opt,name=info_message,json=infoMessage,proto3" json:"info_message,omitempty"` // Optional information message. +} + +func (x *PartitionResponse) Reset() { + *x = PartitionResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_ddsched_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PartitionResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PartitionResponse) ProtoMessage() {} + +func (x *PartitionResponse) ProtoReflect() protoreflect.Message { + mi := &file_protos_ddsched_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PartitionResponse.ProtoReflect.Descriptor instead. +func (*PartitionResponse) Descriptor() ([]byte, []int) { + return file_protos_ddsched_proto_rawDescGZIP(), []int{1} +} + +func (x *PartitionResponse) GetPartitionState() PartitionState { + if x != nil { + return x.PartitionState + } + return PartitionState_IGNORE__ +} + +func (x *PartitionResponse) GetInfoMessage() string { + if x != nil { + return x.InfoMessage + } + return "" +} + +// Request for a new partition. +// AliECS provides the information about processes of DD in order for partition to be configured by TfScheduler. +// Partition request must include all processes. Adding additional processes to existing partition is not supported. +type PartitionInitRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + PartitionInfo *PartitionInfo `protobuf:"bytes,1,opt,name=partition_info,json=partitionInfo,proto3" json:"partition_info,omitempty"` + StfbHostIdMap map[string]string `protobuf:"bytes,2,rep,name=stfb_host_id_map,json=stfbHostIdMap,proto3" json:"stfb_host_id_map,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + // Mapping between StfBuilder::discovery-id parameter and its FLP host name (must resolve to IB IP), for all + // StfBuilder processes in the partition. + StfsHostIdMap map[string]string `protobuf:"bytes,3,rep,name=stfs_host_id_map,json=stfsHostIdMap,proto3" json:"stfs_host_id_map,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + // [optional] parameters passed from AliECS to TfScheduler + PartitionParams map[string]string `protobuf:"bytes,4,rep,name=partition_params,json=partitionParams,proto3" json:"partition_params,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (x *PartitionInitRequest) Reset() { + *x = PartitionInitRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_ddsched_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PartitionInitRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PartitionInitRequest) ProtoMessage() {} + +func (x *PartitionInitRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_ddsched_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PartitionInitRequest.ProtoReflect.Descriptor instead. +func (*PartitionInitRequest) Descriptor() ([]byte, []int) { + return file_protos_ddsched_proto_rawDescGZIP(), []int{2} +} + +func (x *PartitionInitRequest) GetPartitionInfo() *PartitionInfo { + if x != nil { + return x.PartitionInfo + } + return nil +} + +func (x *PartitionInitRequest) GetStfbHostIdMap() map[string]string { + if x != nil { + return x.StfbHostIdMap + } + return nil +} + +func (x *PartitionInitRequest) GetStfsHostIdMap() map[string]string { + if x != nil { + return x.StfsHostIdMap + } + return nil +} + +func (x *PartitionInitRequest) GetPartitionParams() map[string]string { + if x != nil { + return x.PartitionParams + } + return nil +} + +// Request for partition to be terminated. +// This operation will terminate all connections between EPN and FLP, and remove the partition from DD control-plane. +// Lifetime of individual DD components is managed by the respective FairMQ controller. +type PartitionTermRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + PartitionInfo *PartitionInfo `protobuf:"bytes,1,opt,name=partition_info,json=partitionInfo,proto3" json:"partition_info,omitempty"` +} + +func (x *PartitionTermRequest) Reset() { + *x = PartitionTermRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_ddsched_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PartitionTermRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PartitionTermRequest) ProtoMessage() {} + +func (x *PartitionTermRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_ddsched_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PartitionTermRequest.ProtoReflect.Descriptor instead. +func (*PartitionTermRequest) Descriptor() ([]byte, []int) { + return file_protos_ddsched_proto_rawDescGZIP(), []int{3} +} + +func (x *PartitionTermRequest) GetPartitionInfo() *PartitionInfo { + if x != nil { + return x.PartitionInfo + } + return nil +} + +var File_protos_ddsched_proto protoreflect.FileDescriptor + +var file_protos_ddsched_proto_rawDesc = []byte{ + 0x0a, 0x14, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x64, 0x64, 0x73, 0x63, 0x68, 0x65, 0x64, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1b, 0x6f, 0x32, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x44, + 0x69, 0x73, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x43, 0x6f, 0x6e, 0x74, + 0x72, 0x6f, 0x6c, 0x22, 0x59, 0x0a, 0x0d, 0x50, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, + 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x25, 0x0a, 0x0e, 0x65, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, + 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x65, 0x6e, + 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x70, + 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0b, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x22, 0x8c, + 0x01, 0x0a, 0x11, 0x50, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x54, 0x0a, 0x0f, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, + 0x6e, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2b, 0x2e, + 0x6f, 0x32, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x44, 0x69, 0x73, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, + 0x69, 0x6f, 0x6e, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x50, 0x61, 0x72, 0x74, + 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x0e, 0x70, 0x61, 0x72, 0x74, + 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x6e, + 0x66, 0x6f, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0b, 0x69, 0x6e, 0x66, 0x6f, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x82, 0x05, + 0x0a, 0x14, 0x50, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x69, 0x74, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x51, 0x0a, 0x0e, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, + 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, + 0x2e, 0x6f, 0x32, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x44, 0x69, 0x73, 0x74, 0x72, 0x69, 0x62, 0x75, + 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x50, 0x61, 0x72, + 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0d, 0x70, 0x61, 0x72, 0x74, + 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x6d, 0x0a, 0x10, 0x73, 0x74, 0x66, + 0x62, 0x5f, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x5f, 0x6d, 0x61, 0x70, 0x18, 0x02, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x44, 0x2e, 0x6f, 0x32, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x44, 0x69, 0x73, + 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, + 0x6c, 0x2e, 0x50, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x69, 0x74, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x53, 0x74, 0x66, 0x62, 0x48, 0x6f, 0x73, 0x74, 0x49, + 0x64, 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0d, 0x73, 0x74, 0x66, 0x62, 0x48, + 0x6f, 0x73, 0x74, 0x49, 0x64, 0x4d, 0x61, 0x70, 0x12, 0x6d, 0x0a, 0x10, 0x73, 0x74, 0x66, 0x73, + 0x5f, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x5f, 0x6d, 0x61, 0x70, 0x18, 0x03, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x44, 0x2e, 0x6f, 0x32, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x44, 0x69, 0x73, 0x74, + 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, + 0x2e, 0x50, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x69, 0x74, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x53, 0x74, 0x66, 0x73, 0x48, 0x6f, 0x73, 0x74, 0x49, 0x64, + 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0d, 0x73, 0x74, 0x66, 0x73, 0x48, 0x6f, + 0x73, 0x74, 0x49, 0x64, 0x4d, 0x61, 0x70, 0x12, 0x71, 0x0a, 0x10, 0x70, 0x61, 0x72, 0x74, 0x69, + 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x46, 0x2e, 0x6f, 0x32, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x44, 0x69, 0x73, 0x74, 0x72, + 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, + 0x50, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x69, 0x74, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x2e, 0x50, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, + 0x72, 0x61, 0x6d, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0f, 0x70, 0x61, 0x72, 0x74, 0x69, + 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x40, 0x0a, 0x12, 0x53, 0x74, + 0x66, 0x62, 0x48, 0x6f, 0x73, 0x74, 0x49, 0x64, 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, + 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x40, 0x0a, 0x12, + 0x53, 0x74, 0x66, 0x73, 0x48, 0x6f, 0x73, 0x74, 0x49, 0x64, 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x42, + 0x0a, 0x14, 0x50, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x72, 0x61, 0x6d, + 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, + 0x38, 0x01, 0x22, 0x69, 0x0a, 0x14, 0x50, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x54, + 0x65, 0x72, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x51, 0x0a, 0x0e, 0x70, 0x61, + 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x6f, 0x32, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x44, 0x69, 0x73, 0x74, + 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, + 0x2e, 0x50, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0d, + 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x2a, 0xd3, 0x01, + 0x0a, 0x0e, 0x50, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, + 0x12, 0x0c, 0x0a, 0x08, 0x49, 0x47, 0x4e, 0x4f, 0x52, 0x45, 0x5f, 0x5f, 0x10, 0x00, 0x12, 0x15, + 0x0a, 0x11, 0x50, 0x41, 0x52, 0x54, 0x49, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, + 0x4f, 0x57, 0x4e, 0x10, 0x01, 0x12, 0x13, 0x0a, 0x0f, 0x50, 0x41, 0x52, 0x54, 0x49, 0x54, 0x49, + 0x4f, 0x4e, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x02, 0x12, 0x1d, 0x0a, 0x19, 0x50, 0x41, + 0x52, 0x54, 0x49, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x52, 0x45, 0x51, 0x55, 0x45, 0x53, 0x54, 0x5f, + 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x10, 0x03, 0x12, 0x19, 0x0a, 0x15, 0x50, 0x41, 0x52, + 0x54, 0x49, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x55, 0x52, 0x49, + 0x4e, 0x47, 0x10, 0x04, 0x12, 0x18, 0x0a, 0x14, 0x50, 0x41, 0x52, 0x54, 0x49, 0x54, 0x49, 0x4f, + 0x4e, 0x5f, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x55, 0x52, 0x45, 0x44, 0x10, 0x05, 0x12, 0x19, + 0x0a, 0x15, 0x50, 0x41, 0x52, 0x54, 0x49, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x54, 0x45, 0x52, 0x4d, + 0x49, 0x4e, 0x41, 0x54, 0x49, 0x4e, 0x47, 0x10, 0x06, 0x12, 0x18, 0x0a, 0x14, 0x50, 0x41, 0x52, + 0x54, 0x49, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x54, 0x45, 0x52, 0x4d, 0x49, 0x4e, 0x41, 0x54, 0x45, + 0x44, 0x10, 0x07, 0x32, 0x81, 0x03, 0x0a, 0x17, 0x44, 0x61, 0x74, 0x61, 0x44, 0x69, 0x73, 0x74, + 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x12, + 0x7a, 0x0a, 0x13, 0x50, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x69, 0x74, + 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x12, 0x31, 0x2e, 0x6f, 0x32, 0x2e, 0x44, 0x61, 0x74, 0x61, + 0x44, 0x69, 0x73, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x43, 0x6f, 0x6e, + 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x50, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, + 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x6f, 0x32, 0x2e, 0x44, + 0x61, 0x74, 0x61, 0x44, 0x69, 0x73, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x2e, + 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x50, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, + 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x79, 0x0a, 0x12, 0x50, + 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x74, + 0x65, 0x12, 0x31, 0x2e, 0x6f, 0x32, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x44, 0x69, 0x73, 0x74, 0x72, + 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, + 0x50, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x65, 0x72, 0x6d, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x6f, 0x32, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x44, 0x69, + 0x73, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x72, + 0x6f, 0x6c, 0x2e, 0x50, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x6f, 0x0a, 0x0f, 0x50, 0x61, 0x72, 0x74, 0x69, 0x74, + 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x2a, 0x2e, 0x6f, 0x32, 0x2e, 0x44, + 0x61, 0x74, 0x61, 0x44, 0x69, 0x73, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x2e, + 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x50, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, + 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x1a, 0x2e, 0x2e, 0x6f, 0x32, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x44, + 0x69, 0x73, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x43, 0x6f, 0x6e, 0x74, + 0x72, 0x6f, 0x6c, 0x2e, 0x50, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x46, 0x5a, 0x44, 0x67, 0x69, 0x74, 0x68, 0x75, + 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x41, 0x6c, 0x69, 0x63, 0x65, 0x4f, 0x32, 0x47, 0x72, 0x6f, + 0x75, 0x70, 0x2f, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2f, 0x63, 0x6f, 0x72, 0x65, 0x2f, + 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x64, 0x64, 0x73, 0x63, + 0x68, 0x65, 0x64, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x3b, 0x64, 0x64, 0x70, 0x62, 0x62, + 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_protos_ddsched_proto_rawDescOnce sync.Once + file_protos_ddsched_proto_rawDescData = file_protos_ddsched_proto_rawDesc +) + +func file_protos_ddsched_proto_rawDescGZIP() []byte { + file_protos_ddsched_proto_rawDescOnce.Do(func() { + file_protos_ddsched_proto_rawDescData = protoimpl.X.CompressGZIP(file_protos_ddsched_proto_rawDescData) + }) + return file_protos_ddsched_proto_rawDescData +} + +var file_protos_ddsched_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_protos_ddsched_proto_msgTypes = make([]protoimpl.MessageInfo, 7) +var file_protos_ddsched_proto_goTypes = []interface{}{ + (PartitionState)(0), // 0: o2.DataDistribution.Control.PartitionState + (*PartitionInfo)(nil), // 1: o2.DataDistribution.Control.PartitionInfo + (*PartitionResponse)(nil), // 2: o2.DataDistribution.Control.PartitionResponse + (*PartitionInitRequest)(nil), // 3: o2.DataDistribution.Control.PartitionInitRequest + (*PartitionTermRequest)(nil), // 4: o2.DataDistribution.Control.PartitionTermRequest + nil, // 5: o2.DataDistribution.Control.PartitionInitRequest.StfbHostIdMapEntry + nil, // 6: o2.DataDistribution.Control.PartitionInitRequest.StfsHostIdMapEntry + nil, // 7: o2.DataDistribution.Control.PartitionInitRequest.PartitionParamsEntry +} +var file_protos_ddsched_proto_depIdxs = []int32{ + 0, // 0: o2.DataDistribution.Control.PartitionResponse.partition_state:type_name -> o2.DataDistribution.Control.PartitionState + 1, // 1: o2.DataDistribution.Control.PartitionInitRequest.partition_info:type_name -> o2.DataDistribution.Control.PartitionInfo + 5, // 2: o2.DataDistribution.Control.PartitionInitRequest.stfb_host_id_map:type_name -> o2.DataDistribution.Control.PartitionInitRequest.StfbHostIdMapEntry + 6, // 3: o2.DataDistribution.Control.PartitionInitRequest.stfs_host_id_map:type_name -> o2.DataDistribution.Control.PartitionInitRequest.StfsHostIdMapEntry + 7, // 4: o2.DataDistribution.Control.PartitionInitRequest.partition_params:type_name -> o2.DataDistribution.Control.PartitionInitRequest.PartitionParamsEntry + 1, // 5: o2.DataDistribution.Control.PartitionTermRequest.partition_info:type_name -> o2.DataDistribution.Control.PartitionInfo + 3, // 6: o2.DataDistribution.Control.DataDistributionControl.PartitionInitialize:input_type -> o2.DataDistribution.Control.PartitionInitRequest + 4, // 7: o2.DataDistribution.Control.DataDistributionControl.PartitionTerminate:input_type -> o2.DataDistribution.Control.PartitionTermRequest + 1, // 8: o2.DataDistribution.Control.DataDistributionControl.PartitionStatus:input_type -> o2.DataDistribution.Control.PartitionInfo + 2, // 9: o2.DataDistribution.Control.DataDistributionControl.PartitionInitialize:output_type -> o2.DataDistribution.Control.PartitionResponse + 2, // 10: o2.DataDistribution.Control.DataDistributionControl.PartitionTerminate:output_type -> o2.DataDistribution.Control.PartitionResponse + 2, // 11: o2.DataDistribution.Control.DataDistributionControl.PartitionStatus:output_type -> o2.DataDistribution.Control.PartitionResponse + 9, // [9:12] is the sub-list for method output_type + 6, // [6:9] is the sub-list for method input_type + 6, // [6:6] is the sub-list for extension type_name + 6, // [6:6] is the sub-list for extension extendee + 0, // [0:6] is the sub-list for field type_name +} + +func init() { file_protos_ddsched_proto_init() } +func file_protos_ddsched_proto_init() { + if File_protos_ddsched_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_protos_ddsched_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PartitionInfo); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_ddsched_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PartitionResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_ddsched_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PartitionInitRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_ddsched_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PartitionTermRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_protos_ddsched_proto_rawDesc, + NumEnums: 1, + NumMessages: 7, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_protos_ddsched_proto_goTypes, + DependencyIndexes: file_protos_ddsched_proto_depIdxs, + EnumInfos: file_protos_ddsched_proto_enumTypes, + MessageInfos: file_protos_ddsched_proto_msgTypes, + }.Build() + File_protos_ddsched_proto = out.File + file_protos_ddsched_proto_rawDesc = nil + file_protos_ddsched_proto_goTypes = nil + file_protos_ddsched_proto_depIdxs = nil +} diff --git a/core/integration/ddsched/protos/ddsched.proto b/core/integration/ddsched/protos/ddsched.proto new file mode 100644 index 000000000..9f627d4e1 --- /dev/null +++ b/core/integration/ddsched/protos/ddsched.proto @@ -0,0 +1,106 @@ +// Copyright 2019-2022 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \author Gvozden Nešković, Frankfurt Institute for Advanced Studies and Goethe University Frankfurt + +syntax = "proto3"; + +// Changelog: +// 2021-02-08: - Initial version +// - Support for a single partition/environment: PartitionID = EnvironmentID + + +package o2.DataDistribution.Control; +option go_package = "github.com/AliceO2Group/Control/core/integration/ddsched/protos;ddpb"; + +service DataDistributionControl { + + // Partition management requests + rpc PartitionInitialize(PartitionInitRequest) returns (PartitionResponse) { } + rpc PartitionTerminate(PartitionTermRequest) returns (PartitionResponse) { } + rpc PartitionStatus(PartitionInfo) returns (PartitionResponse) { } + +} + +message PartitionInfo { + string environment_id = 1; + // AliECS environment ID (required) + string partition_id = 2; + // Partition ID. (required) +} + +enum PartitionState { + IGNORE__ = 0; + + PARTITION_UNKNOWN = 1; + // Partition not known to DataDistControl. Initialize by calling PartitionInitialize() + + PARTITION_ERROR = 2; + // Error state, requires manual Terminate/Initialize. + // DD control could not configure the partition according to required specifications + + PARTITION_REQUEST_INVALID = 3; + // Provided configuration does not meet requirements or the request is not accepted at the current state. + + PARTITION_CONFIGURING = 4; + // Configuration accepted, waiting on FLP and EPN DD components to respond and connect. + + PARTITION_CONFIGURED = 5; + // All components configured, ready for dataflow commands + + PARTITION_TERMINATING = 6; + // Partition is terminating. EPN-FLP connections will be cleanly closed. + + PARTITION_TERMINATED = 7; + // Partition is terminated. TfScheduler does not accept further requests +} + + +message PartitionResponse { + PartitionState partition_state = 1; + // Current or new state of the partition following reception of the request. + + string info_message = 2; + // Optional information message. +} + + +// Request for a new partition. +// AliECS provides the information about processes of DD in order for partition to be configured by TfScheduler. +// Partition request must include all processes. Adding additional processes to existing partition is not supported. +message PartitionInitRequest { + PartitionInfo partition_info = 1; + + map stfb_host_id_map = 2; + // Mapping between StfBuilder::discovery-id parameter and its FLP host name (must resolve to IB IP), for all + // StfBuilder processes in the partition. + map stfs_host_id_map = 3; + // Mapping between StfSender::discovery-id parameter and its FLP host name (must resolve to IB IP), for all + // StfSender processes in the partition. + + // NOTE: 1. Sizes of stfb_host_id_map and stfs_host_id_map must be the same. StfBuilders and StfSenders come in pairs. + // 2. Multiple discovery-id can map to the same FLP node. + // 3. DNS names (strings) in both maps must satisfy the property: + // stfb_host_id_map.values == stfb_host_id_map.values + // 4. discovery-ids are used for debugging purposes. It would be helpful if the values assigned by AliECS + // provided human-readable information. E.g. an id for the first StfSender on flp009 could be: + // "stfs-flp009#0-". + + // [optional] parameters passed from AliECS to TfScheduler + map partition_params = 4; +} + +// Request for partition to be terminated. +// This operation will terminate all connections between EPN and FLP, and remove the partition from DD control-plane. +// Lifetime of individual DD components is managed by the respective FairMQ controller. +message PartitionTermRequest { + PartitionInfo partition_info = 1; +} diff --git a/core/integration/ddsched/protos/ddsched_grpc.pb.go b/core/integration/ddsched/protos/ddsched_grpc.pb.go new file mode 100644 index 000000000..ed9482e81 --- /dev/null +++ b/core/integration/ddsched/protos/ddsched_grpc.pb.go @@ -0,0 +1,200 @@ +// Copyright 2019-2022 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \author Gvozden Nešković, Frankfurt Institute for Advanced Studies and Goethe University Frankfurt + +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.3.0 +// - protoc v5.29.3 +// source: protos/ddsched.proto + +// Changelog: +// 2021-02-08: - Initial version +// - Support for a single partition/environment: PartitionID = EnvironmentID + +package ddpb + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +const ( + DataDistributionControl_PartitionInitialize_FullMethodName = "/o2.DataDistribution.Control.DataDistributionControl/PartitionInitialize" + DataDistributionControl_PartitionTerminate_FullMethodName = "/o2.DataDistribution.Control.DataDistributionControl/PartitionTerminate" + DataDistributionControl_PartitionStatus_FullMethodName = "/o2.DataDistribution.Control.DataDistributionControl/PartitionStatus" +) + +// DataDistributionControlClient is the client API for DataDistributionControl service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type DataDistributionControlClient interface { + // Partition management requests + PartitionInitialize(ctx context.Context, in *PartitionInitRequest, opts ...grpc.CallOption) (*PartitionResponse, error) + PartitionTerminate(ctx context.Context, in *PartitionTermRequest, opts ...grpc.CallOption) (*PartitionResponse, error) + PartitionStatus(ctx context.Context, in *PartitionInfo, opts ...grpc.CallOption) (*PartitionResponse, error) +} + +type dataDistributionControlClient struct { + cc grpc.ClientConnInterface +} + +func NewDataDistributionControlClient(cc grpc.ClientConnInterface) DataDistributionControlClient { + return &dataDistributionControlClient{cc} +} + +func (c *dataDistributionControlClient) PartitionInitialize(ctx context.Context, in *PartitionInitRequest, opts ...grpc.CallOption) (*PartitionResponse, error) { + out := new(PartitionResponse) + err := c.cc.Invoke(ctx, DataDistributionControl_PartitionInitialize_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *dataDistributionControlClient) PartitionTerminate(ctx context.Context, in *PartitionTermRequest, opts ...grpc.CallOption) (*PartitionResponse, error) { + out := new(PartitionResponse) + err := c.cc.Invoke(ctx, DataDistributionControl_PartitionTerminate_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *dataDistributionControlClient) PartitionStatus(ctx context.Context, in *PartitionInfo, opts ...grpc.CallOption) (*PartitionResponse, error) { + out := new(PartitionResponse) + err := c.cc.Invoke(ctx, DataDistributionControl_PartitionStatus_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// DataDistributionControlServer is the server API for DataDistributionControl service. +// All implementations should embed UnimplementedDataDistributionControlServer +// for forward compatibility +type DataDistributionControlServer interface { + // Partition management requests + PartitionInitialize(context.Context, *PartitionInitRequest) (*PartitionResponse, error) + PartitionTerminate(context.Context, *PartitionTermRequest) (*PartitionResponse, error) + PartitionStatus(context.Context, *PartitionInfo) (*PartitionResponse, error) +} + +// UnimplementedDataDistributionControlServer should be embedded to have forward compatible implementations. +type UnimplementedDataDistributionControlServer struct { +} + +func (UnimplementedDataDistributionControlServer) PartitionInitialize(context.Context, *PartitionInitRequest) (*PartitionResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method PartitionInitialize not implemented") +} +func (UnimplementedDataDistributionControlServer) PartitionTerminate(context.Context, *PartitionTermRequest) (*PartitionResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method PartitionTerminate not implemented") +} +func (UnimplementedDataDistributionControlServer) PartitionStatus(context.Context, *PartitionInfo) (*PartitionResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method PartitionStatus not implemented") +} + +// UnsafeDataDistributionControlServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to DataDistributionControlServer will +// result in compilation errors. +type UnsafeDataDistributionControlServer interface { + mustEmbedUnimplementedDataDistributionControlServer() +} + +func RegisterDataDistributionControlServer(s grpc.ServiceRegistrar, srv DataDistributionControlServer) { + s.RegisterService(&DataDistributionControl_ServiceDesc, srv) +} + +func _DataDistributionControl_PartitionInitialize_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(PartitionInitRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(DataDistributionControlServer).PartitionInitialize(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: DataDistributionControl_PartitionInitialize_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(DataDistributionControlServer).PartitionInitialize(ctx, req.(*PartitionInitRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _DataDistributionControl_PartitionTerminate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(PartitionTermRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(DataDistributionControlServer).PartitionTerminate(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: DataDistributionControl_PartitionTerminate_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(DataDistributionControlServer).PartitionTerminate(ctx, req.(*PartitionTermRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _DataDistributionControl_PartitionStatus_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(PartitionInfo) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(DataDistributionControlServer).PartitionStatus(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: DataDistributionControl_PartitionStatus_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(DataDistributionControlServer).PartitionStatus(ctx, req.(*PartitionInfo)) + } + return interceptor(ctx, in, info, handler) +} + +// DataDistributionControl_ServiceDesc is the grpc.ServiceDesc for DataDistributionControl service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var DataDistributionControl_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "o2.DataDistribution.Control.DataDistributionControl", + HandlerType: (*DataDistributionControlServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "PartitionInitialize", + Handler: _DataDistributionControl_PartitionInitialize_Handler, + }, + { + MethodName: "PartitionTerminate", + Handler: _DataDistributionControl_PartitionTerminate_Handler, + }, + { + MethodName: "PartitionStatus", + Handler: _DataDistributionControl_PartitionStatus_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "protos/ddsched.proto", +} diff --git a/core/integration/kafka/plugin.go b/core/integration/kafka/plugin.go new file mode 100644 index 000000000..f20b50ee9 --- /dev/null +++ b/core/integration/kafka/plugin.go @@ -0,0 +1,488 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2022 CERN and copyright holders of ALICE O². + * Author: Piotr Konopka + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +//go:generate protoc --go_out=. --go_opt=paths=source_relative protos/kafka.proto + +package kafka + +import ( + "context" + "encoding/json" + "errors" + "net/url" + "slices" + "strconv" + "strings" + "sync" + "time" + + "github.com/AliceO2Group/Control/common/logger/infologger" + + "github.com/AliceO2Group/Control/common/logger" + "github.com/AliceO2Group/Control/common/utils/uid" + "github.com/AliceO2Group/Control/core/integration" + kafkapb "github.com/AliceO2Group/Control/core/integration/kafka/protos" + "github.com/AliceO2Group/Control/core/workflow/callable" + "github.com/segmentio/kafka-go" + "github.com/sirupsen/logrus" + "github.com/spf13/viper" + "google.golang.org/protobuf/proto" +) + +var log = logger.New(logrus.StandardLogger(), "kafkaclient") + +type Plugin struct { + endpoint string + kafkaWriter *kafka.Writer + envsInRunning map[string]*kafkapb.EnvInfo // env id is the key + envsInRunningMutex sync.RWMutex +} + +func NewPlugin(endpoint string) integration.Plugin { + _, err := url.Parse(endpoint) + if err != nil { + log.WithField("endpoint", endpoint). + WithError(err). + Error("bad service endpoint") + return nil + } + + return &Plugin{ + endpoint: endpoint, + kafkaWriter: nil, + } +} + +func (p *Plugin) GetName() string { + return "kafka" +} + +func (p *Plugin) GetPrettyName() string { + return "Kafka FSM transition information service" +} + +func (p *Plugin) GetEndpoint() string { + return viper.GetString("kafkaEndpoint") +} + +func (p *Plugin) GetConnectionState() string { + if p == nil || p.kafkaWriter == nil { + return "UNKNOWN" + } else { + return "READY" + } +} + +func (p *Plugin) GetData(_ []any) string { + return "" +} + +func (p *Plugin) GetEnvironmentsData(_ []uid.ID) map[uid.ID]string { + return nil +} + +func (p *Plugin) GetEnvironmentsShortData(_ []uid.ID) map[uid.ID]string { + return nil +} + +func (p *Plugin) FSMEnterStateTopic(state string) string { + return "aliecs.env_state." + state +} + +func (p *Plugin) FSMLeaveStateTopic(state string) string { + return "aliecs.env_leave_state." + state +} + +func (p *Plugin) ActiveRunsListTopic() string { + return "aliecs.env_list.RUNNING" +} + +func (p *Plugin) StartActivityTopic(trigger string) string { + if strings.Contains(trigger, "before_START_ACTIVITY") { + return "aliecs.before_start_activity" + } else if strings.Contains(trigger, "after_START_ACTIVITY") { + return "aliecs.after_start_activity" + } else { + return "aliecs.start_activity" + } +} + +func (p *Plugin) LoggerCallback(msg string, a ...interface{}) { + log.WithField("level", infologger.IL_Devel). + Debugf(msg, a...) +} + +func (p *Plugin) ErrorLoggerCallback(msg string, a ...interface{}) { + log.WithField("level", infologger.IL_Support). + Errorf(msg, a...) +} + +func (p *Plugin) Init(_ string) error { + if !viper.GetBool("enableKafka") { + log.WithField("level", infologger.IL_Support).Error("trying to initialize kafka plugin, when enableKafka flag was set as false") + return errors.New("Cannot init kafka plugin while \"enableKafka\" flags is true") + } + + const call = "Init" + var err error + + p.kafkaWriter = &kafka.Writer{ + Addr: kafka.TCP(p.endpoint), + Balancer: &kafka.CRC32Balancer{}, // same behaviour as confluent-kafka client + AllowAutoTopicCreation: true, + Logger: kafka.LoggerFunc(p.LoggerCallback), + ErrorLogger: kafka.LoggerFunc(p.ErrorLoggerCallback), + RequiredAcks: kafka.RequireAll, + } + + p.envsInRunning = make(map[string]*kafkapb.EnvInfo) + log.WithField("call", call). + WithField("level", infologger.IL_Support). + Info("successfully created a kafka producer with broker '" + p.endpoint + "'") + + // Prepare and send active run list (expected to be empty during init) + timestamp := uint64(time.Now().UnixMilli()) + activeRunsList := &kafkapb.ActiveRunsList{ + ActiveRuns: p.GetRunningEnvList(), + Timestamp: timestamp, + } + arlData, err := proto.Marshal(activeRunsList) + if err != nil { + log.WithField("call", call). + WithField("level", infologger.IL_Support). + Error("could not marshall an active runs list: ", err) + } + p.produceMessage(arlData, p.ActiveRunsListTopic(), "", "Init") + return nil +} + +func (p *Plugin) ObjectStack(_ map[string]string, _ map[string]string) (stack map[string]interface{}) { + stack = make(map[string]interface{}) + return stack +} + +func parseDetectors(detectorsParam string) (detectors []string, err error) { + detectorsSlice := make([]string, 0) + bytes := []byte(detectorsParam) + err = json.Unmarshal(bytes, &detectorsSlice) + if err != nil { + log.WithError(err). + Error("error processing the detectors list") + return + } + return detectorsSlice, nil +} + +func (p *Plugin) extractStateFromTrigger(trigger string) string { + if strings.Contains(trigger, "enter_") { + return strings.TrimPrefix(trigger, "enter_") + } else if strings.Contains(trigger, "leave_") { + return strings.TrimPrefix(trigger, "leave_") + } else if strings.Contains(trigger, "after_START_ACTIVITY") || strings.Contains(trigger, "before_STOP_ACTIVITY") { + return "RUNNING" + } else if strings.Contains(trigger, "before_START_ACTIVITY") || strings.Contains(trigger, "after_STOP_ACTIVITY") { + return "CONFIGURED" + } else { + return "UNKNOWN" + } +} + +func (p *Plugin) newEnvStateObject(varStack map[string]string, call string) *kafkapb.EnvInfo { + envId, ok := varStack["environment_id"] + if !ok { + log.WithField("call", call).Error("cannot acquire environment ID") + return nil + } + + trigger, ok := varStack["__call_trigger"] + if !ok { + log.WithField("call", call).WithField("partition", envId). + WithField("level", infologger.IL_Support). + Error("cannot acquire trigger from varStack") + return nil + } + state := p.extractStateFromTrigger(trigger) + + var runNumberOpt *uint32 = nil + var runTypeOpt *string = nil + if state == "RUNNING" || strings.Contains(trigger, "START_ACTIVITY") { + // the following fields are relevant only in RUNNING state + runNumberStr, ok := varStack["run_number"] + if ok { + runNumber, err := strconv.ParseUint(runNumberStr, 10, 32) + if err == nil { + runNumber32 := uint32(runNumber) + runNumberOpt = &runNumber32 + } + } + + runType, ok := varStack["run_type"] + if ok { + runTypeOpt = &runType + } + } + + enterStateTimeMsStr, ok := varStack["enter_state_time_ms"] + if !ok { + log.WithField("call", call). + WithField("partition", envId). + WithField("level", infologger.IL_Support). + Error("cannot acquire enter_state_time_ms") + return nil + } + enterStateTimeMs, err := strconv.ParseUint(enterStateTimeMsStr, 10, 64) + if err != nil { + log.WithError(err). + WithField("call", call). + WithField("partition", envId). + WithField("level", infologger.IL_Support). + Errorf("cannot convert enter_state_time_ms (%s) to an unsigned integer", enterStateTimeMsStr) + return nil + } + + detectorsStr, ok := varStack["detectors"] + if !ok { + log.WithField("call", call). + WithField("partition", envId). + WithField("level", infologger.IL_Support). + Error("cannot acquire general detector list from varStack") + return nil + } + detectorsSlice, err := parseDetectors(detectorsStr) + if err != nil { + log.WithField("call", call). + WithField("partition", envId). + WithField("level", infologger.IL_Support). + Error("cannot parse general detector list") + return nil + } + + return &kafkapb.EnvInfo{ + EnvironmentId: envId, + RunNumber: runNumberOpt, + RunType: runTypeOpt, + State: state, + Detectors: detectorsSlice, + EnterStateTimestamp: enterStateTimeMs, + } +} + +func (p *Plugin) UpdateRunningEnvList(envInfo *kafkapb.EnvInfo) { + p.envsInRunningMutex.Lock() + defer p.envsInRunningMutex.Unlock() + if envInfo.State == "RUNNING" { + p.envsInRunning[envInfo.EnvironmentId] = envInfo + } else { + delete(p.envsInRunning, envInfo.EnvironmentId) + } +} + +func (p *Plugin) GetRunningEnvList() []*kafkapb.EnvInfo { + var array []*kafkapb.EnvInfo + p.envsInRunningMutex.RLock() + defer p.envsInRunningMutex.RUnlock() + for _, v := range p.envsInRunning { + array = append(array, v) + } + + p.SortRunningEnvList(array) + p.SortDetectorsOfRunningEnvs(array) + return array +} + +// OCTRL-932 we sort the active envs here are grafana cannot do it +// order is defined as: +// 1) runs with more than 2 detectors first +// 2) if there are more than 2 runs with more than 2 detectors, those with ITS should be first +func (p *Plugin) SortRunningEnvList(activeEnvs []*kafkapb.EnvInfo) { + slices.SortStableFunc(activeEnvs, func(a, b *kafkapb.EnvInfo) int { + if len(a.Detectors) >= 2 && len(b.Detectors) >= 2 { + if slices.Contains(a.Detectors, "ITS") { + return -1 + } + if slices.Contains(b.Detectors, "ITS") { + return 1 + } + } + return len(b.Detectors) - len(a.Detectors) + }) +} + +func (p *Plugin) SortDetectorsOfRunningEnvs(activeEnvs []*kafkapb.EnvInfo) { + for _, env := range activeEnvs { + slices.Sort(env.Detectors) + } +} + +func (p *Plugin) produceMessage(message []byte, topic string, envId string, call string) { + log.WithField("call", call). + WithField("partition", envId). + WithField("level", infologger.IL_Support). + Debugf("producing a new kafka message on topic %s", topic) + + err := p.kafkaWriter.WriteMessages(context.Background(), kafka.Message{ + Topic: topic, + Value: message, + }) + if err != nil { + log.WithField("call", call). + WithField("partition", envId). + WithField("topic", topic). + WithField("level", infologger.IL_Support). + Errorf("Kafka message delivery failed: %s", err.Error()) + } +} + +func (p *Plugin) createNewStateCallback(varStack map[string]string, call string) func() string { + return func() (out string) { + // Retrieve and update the env info + timestamp := uint64(time.Now().UnixMilli()) + envInfo := p.newEnvStateObject(varStack, call) + p.UpdateRunningEnvList(envInfo) + + // Prepare and send new state notification + log.WithField("call", call). + WithField("partition", envInfo.EnvironmentId). + WithField("level", infologger.IL_Devel). + Debug("Notifying Kafka that the environment is entering the state " + envInfo.State) + newStateNotification := &kafkapb.NewStateNotification{ + EnvInfo: envInfo, + Timestamp: timestamp, + } + nsnData, err := proto.Marshal(newStateNotification) + if err != nil { + log.WithField("call", call). + WithField("partition", envInfo.EnvironmentId). + WithField("level", infologger.IL_Support). + Error("could not marshall a new state notification: ", err) + } + p.produceMessage(nsnData, p.FSMEnterStateTopic(envInfo.State), envInfo.EnvironmentId, call) + + log.WithField("call", call). + WithField("partition", envInfo.EnvironmentId). + WithField("level", infologger.IL_Devel). + Debug("Notifying Kafka about the new list of environments in RUNNING") + // Prepare and send active run list + activeRunsList := &kafkapb.ActiveRunsList{ + ActiveRuns: p.GetRunningEnvList(), + Timestamp: timestamp, + } + arlData, err := proto.Marshal(activeRunsList) + if err != nil { + log.WithField("call", call). + WithField("partition", envInfo.EnvironmentId). + WithField("level", infologger.IL_Support). + Error("could not marshall an active runs list: ", err) + } + p.produceMessage(arlData, p.ActiveRunsListTopic(), envInfo.EnvironmentId, call) + return + } +} + +func (p *Plugin) createLeaveStateCallback(varStack map[string]string, call string) func() string { + return func() (out string) { + // Retrieve and update the env info + timestamp := uint64(time.Now().UnixMilli()) + envInfo := p.newEnvStateObject(varStack, call) + + // Prepare and send new state notification + log.WithField("call", call). + WithField("partition", envInfo.EnvironmentId). + WithField("level", infologger.IL_Devel). + Debug("Notifying Kafka that the environment is leaving the state " + envInfo.State) + newStateNotification := &kafkapb.NewStateNotification{ + EnvInfo: envInfo, + Timestamp: timestamp, + } + nsnData, err := proto.Marshal(newStateNotification) + if err != nil { + log.WithField("call", call). + WithField("partition", envInfo.EnvironmentId). + WithField("level", infologger.IL_Support). + Error("could not marshall a new state notification: ", err) + } + p.produceMessage(nsnData, p.FSMLeaveStateTopic(envInfo.State), envInfo.EnvironmentId, call) + + return + } +} + +func (p *Plugin) createActivityStartCallback(varStack map[string]string, call string) func() string { + return func() (out string) { + // Retrieve and update the env info + timestamp := uint64(time.Now().UnixMilli()) + envInfo := p.newEnvStateObject(varStack, call) + + // Prepare and send new state notification + log.WithField("call", call). + WithField("partition", envInfo.EnvironmentId). + WithField("level", infologger.IL_Devel). + Debug("Notifying Kafka about activity start " + envInfo.State) + stateNotification := &kafkapb.NewStateNotification{ + EnvInfo: envInfo, + Timestamp: timestamp, + } + nsnData, err := proto.Marshal(stateNotification) + if err != nil { + log.WithField("call", call). + WithField("partition", envInfo.EnvironmentId). + WithField("level", infologger.IL_Support). + Error("could not marshall a state notification: ", err) + } + + trigger, ok := varStack["__call_trigger"] + if !ok { + log.WithField("call", call). + WithField("partition", envInfo.EnvironmentId). + WithField("level", infologger.IL_Support). + Error("cannot acquire trigger from varStack") + return "" + } + p.produceMessage(nsnData, p.StartActivityTopic(trigger), envInfo.EnvironmentId, call) + + return + } +} + +func (p *Plugin) CallStack(data interface{}) (stack map[string]interface{}) { + call, ok := data.(*callable.Call) + if !ok { + return + } + varStack := call.VarStack + + stack = make(map[string]interface{}) + // The first two are actually equal. We create both to allow to migrate to a more accurate name. + stack["PublishEnterStateUpdate"] = p.createNewStateCallback(varStack, "PublishEnterStateUpdate") + stack["PublishLeaveStateUpdate"] = p.createLeaveStateCallback(varStack, "PublishLeaveStateUpdate") + stack["PublishStartActivityUpdate"] = p.createActivityStartCallback(varStack, "PublishStartActivityUpdate") + return +} + +func (p *Plugin) Destroy() error { + if err := p.kafkaWriter.Close(); err != nil { + log.Fatal("failed to close Kafka writer:", err) + } + return nil +} diff --git a/core/integration/kafka/plugin_test.go b/core/integration/kafka/plugin_test.go new file mode 100644 index 000000000..6ae6442ba --- /dev/null +++ b/core/integration/kafka/plugin_test.go @@ -0,0 +1,65 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2024 CERN and copyright holders of ALICE O². + * Author: Teo Mrnjavac + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package kafka + +import ( + "testing" + + kafkapb "github.com/AliceO2Group/Control/core/integration/kafka/protos" +) + +func TestSortActiveRuns(t *testing.T) { + p := Plugin{} + p.SortRunningEnvList(nil) + + var envs []*kafkapb.EnvInfo + envs = append(envs, + &kafkapb.EnvInfo{EnvironmentId: "1", Detectors: []string{"first"}}, + &kafkapb.EnvInfo{EnvironmentId: "2", Detectors: []string{"second"}}, + &kafkapb.EnvInfo{EnvironmentId: "3", Detectors: []string{"ITS", "first"}}, + &kafkapb.EnvInfo{EnvironmentId: "4", Detectors: []string{"first", "second", "third"}}) + + p.SortRunningEnvList(envs) + + if len(envs) != 4 { + t.Error("wrong number of environments") + } + + if envs[0].EnvironmentId != "3" { + t.Errorf("first should have been environment 3, but is %s with dets %v", envs[0].EnvironmentId, envs[0].Detectors) + } + + if envs[1].EnvironmentId != "4" { + t.Errorf("second should have been environment 4, but is %s with dets %v", envs[1].EnvironmentId, envs[1].Detectors) + } + + if envs[2].EnvironmentId != "1" { + t.Errorf("third should have been environment 1, but is %s with dets %v", envs[2].EnvironmentId, envs[2].Detectors) + } + + if envs[3].EnvironmentId != "2" { + t.Errorf("fourth should have been environment 2, but is %s with dets %v", envs[3].EnvironmentId, envs[3].Detectors) + } +} diff --git a/core/integration/kafka/protos/kafka.pb.go b/core/integration/kafka/protos/kafka.pb.go new file mode 100644 index 000000000..54e07e02d --- /dev/null +++ b/core/integration/kafka/protos/kafka.pb.go @@ -0,0 +1,349 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.34.1 +// protoc v5.29.3 +// source: protos/kafka.proto + +package kafka + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type NewStateNotification struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + EnvInfo *EnvInfo `protobuf:"bytes,1,opt,name=envInfo,proto3" json:"envInfo,omitempty"` + Timestamp uint64 `protobuf:"varint,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"` // ms since epoch +} + +func (x *NewStateNotification) Reset() { + *x = NewStateNotification{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_kafka_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *NewStateNotification) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*NewStateNotification) ProtoMessage() {} + +func (x *NewStateNotification) ProtoReflect() protoreflect.Message { + mi := &file_protos_kafka_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use NewStateNotification.ProtoReflect.Descriptor instead. +func (*NewStateNotification) Descriptor() ([]byte, []int) { + return file_protos_kafka_proto_rawDescGZIP(), []int{0} +} + +func (x *NewStateNotification) GetEnvInfo() *EnvInfo { + if x != nil { + return x.EnvInfo + } + return nil +} + +func (x *NewStateNotification) GetTimestamp() uint64 { + if x != nil { + return x.Timestamp + } + return 0 +} + +type ActiveRunsList struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ActiveRuns []*EnvInfo `protobuf:"bytes,1,rep,name=activeRuns,proto3" json:"activeRuns,omitempty"` + Timestamp uint64 `protobuf:"varint,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"` // ms since epoch +} + +func (x *ActiveRunsList) Reset() { + *x = ActiveRunsList{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_kafka_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ActiveRunsList) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ActiveRunsList) ProtoMessage() {} + +func (x *ActiveRunsList) ProtoReflect() protoreflect.Message { + mi := &file_protos_kafka_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ActiveRunsList.ProtoReflect.Descriptor instead. +func (*ActiveRunsList) Descriptor() ([]byte, []int) { + return file_protos_kafka_proto_rawDescGZIP(), []int{1} +} + +func (x *ActiveRunsList) GetActiveRuns() []*EnvInfo { + if x != nil { + return x.ActiveRuns + } + return nil +} + +func (x *ActiveRunsList) GetTimestamp() uint64 { + if x != nil { + return x.Timestamp + } + return 0 +} + +type EnvInfo struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + EnvironmentId string `protobuf:"bytes,1,opt,name=environmentId,proto3" json:"environmentId,omitempty"` + RunNumber *uint32 `protobuf:"varint,2,opt,name=runNumber,proto3,oneof" json:"runNumber,omitempty"` + RunType *string `protobuf:"bytes,3,opt,name=runType,proto3,oneof" json:"runType,omitempty"` + State string `protobuf:"bytes,4,opt,name=state,proto3" json:"state,omitempty"` + Detectors []string `protobuf:"bytes,5,rep,name=detectors,proto3" json:"detectors,omitempty"` + EnterStateTimestamp uint64 `protobuf:"varint,6,opt,name=enterStateTimestamp,proto3" json:"enterStateTimestamp,omitempty"` // ms since epoch. +} + +func (x *EnvInfo) Reset() { + *x = EnvInfo{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_kafka_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *EnvInfo) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*EnvInfo) ProtoMessage() {} + +func (x *EnvInfo) ProtoReflect() protoreflect.Message { + mi := &file_protos_kafka_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use EnvInfo.ProtoReflect.Descriptor instead. +func (*EnvInfo) Descriptor() ([]byte, []int) { + return file_protos_kafka_proto_rawDescGZIP(), []int{2} +} + +func (x *EnvInfo) GetEnvironmentId() string { + if x != nil { + return x.EnvironmentId + } + return "" +} + +func (x *EnvInfo) GetRunNumber() uint32 { + if x != nil && x.RunNumber != nil { + return *x.RunNumber + } + return 0 +} + +func (x *EnvInfo) GetRunType() string { + if x != nil && x.RunType != nil { + return *x.RunType + } + return "" +} + +func (x *EnvInfo) GetState() string { + if x != nil { + return x.State + } + return "" +} + +func (x *EnvInfo) GetDetectors() []string { + if x != nil { + return x.Detectors + } + return nil +} + +func (x *EnvInfo) GetEnterStateTimestamp() uint64 { + if x != nil { + return x.EnterStateTimestamp + } + return 0 +} + +var File_protos_kafka_proto protoreflect.FileDescriptor + +var file_protos_kafka_proto_rawDesc = []byte{ + 0x0a, 0x12, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x6b, 0x61, 0x66, 0x6b, 0x61, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x05, 0x6b, 0x61, 0x66, 0x6b, 0x61, 0x22, 0x5e, 0x0a, 0x14, 0x4e, + 0x65, 0x77, 0x53, 0x74, 0x61, 0x74, 0x65, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x12, 0x28, 0x0a, 0x07, 0x65, 0x6e, 0x76, 0x49, 0x6e, 0x66, 0x6f, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6b, 0x61, 0x66, 0x6b, 0x61, 0x2e, 0x45, 0x6e, 0x76, + 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x07, 0x65, 0x6e, 0x76, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1c, 0x0a, + 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x5e, 0x0a, 0x0e, 0x41, + 0x63, 0x74, 0x69, 0x76, 0x65, 0x52, 0x75, 0x6e, 0x73, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x2e, 0x0a, + 0x0a, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x52, 0x75, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x0e, 0x2e, 0x6b, 0x61, 0x66, 0x6b, 0x61, 0x2e, 0x45, 0x6e, 0x76, 0x49, 0x6e, 0x66, + 0x6f, 0x52, 0x0a, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x52, 0x75, 0x6e, 0x73, 0x12, 0x1c, 0x0a, + 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0xf1, 0x01, 0x0a, 0x07, + 0x45, 0x6e, 0x76, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x24, 0x0a, 0x0d, 0x65, 0x6e, 0x76, 0x69, 0x72, + 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, + 0x65, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x21, 0x0a, + 0x09, 0x72, 0x75, 0x6e, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, + 0x48, 0x00, 0x52, 0x09, 0x72, 0x75, 0x6e, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x88, 0x01, 0x01, + 0x12, 0x1d, 0x0a, 0x07, 0x72, 0x75, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x48, 0x01, 0x52, 0x07, 0x72, 0x75, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x88, 0x01, 0x01, 0x12, + 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, + 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x64, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, + 0x72, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x64, 0x65, 0x74, 0x65, 0x63, 0x74, + 0x6f, 0x72, 0x73, 0x12, 0x30, 0x0a, 0x13, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x13, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x72, 0x75, 0x6e, 0x4e, 0x75, 0x6d, + 0x62, 0x65, 0x72, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x72, 0x75, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x42, + 0x45, 0x5a, 0x43, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x41, 0x6c, + 0x69, 0x63, 0x65, 0x4f, 0x32, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x2f, 0x43, 0x6f, 0x6e, 0x74, 0x72, + 0x6f, 0x6c, 0x2f, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x2f, 0x6b, 0x61, 0x66, 0x6b, 0x61, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, + 0x3b, 0x6b, 0x61, 0x66, 0x6b, 0x61, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_protos_kafka_proto_rawDescOnce sync.Once + file_protos_kafka_proto_rawDescData = file_protos_kafka_proto_rawDesc +) + +func file_protos_kafka_proto_rawDescGZIP() []byte { + file_protos_kafka_proto_rawDescOnce.Do(func() { + file_protos_kafka_proto_rawDescData = protoimpl.X.CompressGZIP(file_protos_kafka_proto_rawDescData) + }) + return file_protos_kafka_proto_rawDescData +} + +var file_protos_kafka_proto_msgTypes = make([]protoimpl.MessageInfo, 3) +var file_protos_kafka_proto_goTypes = []interface{}{ + (*NewStateNotification)(nil), // 0: kafka.NewStateNotification + (*ActiveRunsList)(nil), // 1: kafka.ActiveRunsList + (*EnvInfo)(nil), // 2: kafka.EnvInfo +} +var file_protos_kafka_proto_depIdxs = []int32{ + 2, // 0: kafka.NewStateNotification.envInfo:type_name -> kafka.EnvInfo + 2, // 1: kafka.ActiveRunsList.activeRuns:type_name -> kafka.EnvInfo + 2, // [2:2] is the sub-list for method output_type + 2, // [2:2] is the sub-list for method input_type + 2, // [2:2] is the sub-list for extension type_name + 2, // [2:2] is the sub-list for extension extendee + 0, // [0:2] is the sub-list for field type_name +} + +func init() { file_protos_kafka_proto_init() } +func file_protos_kafka_proto_init() { + if File_protos_kafka_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_protos_kafka_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NewStateNotification); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_kafka_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ActiveRunsList); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_kafka_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*EnvInfo); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + file_protos_kafka_proto_msgTypes[2].OneofWrappers = []interface{}{} + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_protos_kafka_proto_rawDesc, + NumEnums: 0, + NumMessages: 3, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_protos_kafka_proto_goTypes, + DependencyIndexes: file_protos_kafka_proto_depIdxs, + MessageInfos: file_protos_kafka_proto_msgTypes, + }.Build() + File_protos_kafka_proto = out.File + file_protos_kafka_proto_rawDesc = nil + file_protos_kafka_proto_goTypes = nil + file_protos_kafka_proto_depIdxs = nil +} diff --git a/core/integration/kafka/protos/kafka.proto b/core/integration/kafka/protos/kafka.proto new file mode 100644 index 000000000..f3b0a0f25 --- /dev/null +++ b/core/integration/kafka/protos/kafka.proto @@ -0,0 +1,22 @@ +syntax = "proto3"; +package kafka; +option go_package = "github.com/AliceO2Group/Control/core/integration/kafka/protos;kafka"; + +message NewStateNotification { + EnvInfo envInfo = 1; + uint64 timestamp = 2; // ms since epoch +} + +message ActiveRunsList { + repeated EnvInfo activeRuns = 1; + uint64 timestamp = 2; // ms since epoch +} + +message EnvInfo { + string environmentId = 1; + optional uint32 runNumber = 2; + optional string runType = 3; + string state = 4; + repeated string detectors = 5; + uint64 enterStateTimestamp = 6; // ms since epoch. +} \ No newline at end of file diff --git a/core/integration/lhc/event/lhcevent.go b/core/integration/lhc/event/lhcevent.go new file mode 100644 index 000000000..ecaa3cc6c --- /dev/null +++ b/core/integration/lhc/event/lhcevent.go @@ -0,0 +1,56 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2025 CERN and copyright holders of ALICE O². + * Author: Piotr Konopka + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package event + +import ( + "github.com/AliceO2Group/Control/common/event" + commonpb "github.com/AliceO2Group/Control/common/protos" +) + +// BeamInfo mirrors (a subset of) the information described in the proto draft. +type BeamInfo struct { + StableBeamsStart int64 `json:"stableBeamsStart,omitempty"` + StableBeamsEnd int64 `json:"stableBeamsEnd,omitempty"` + FillNumber int32 `json:"fillNumber,omitempty"` + FillingSchemeName string `json:"fillingSchemeName,omitempty"` + BeamType string `json:"beamType,omitempty"` + BeamMode commonpb.BeamMode `json:"beamMode,omitempty"` +} + +type LhcStateChangeEvent struct { + event.IntegratedServiceEventBase + BeamInfo BeamInfo +} + +func (e *LhcStateChangeEvent) GetName() string { + return "LHC_STATE_CHANGE_EVENT" +} + +func (e *LhcStateChangeEvent) GetBeamInfo() BeamInfo { + if e == nil { + return BeamInfo{} + } + return e.BeamInfo +} diff --git a/core/integration/lhc/plugin.go b/core/integration/lhc/plugin.go new file mode 100644 index 000000000..d29177c6a --- /dev/null +++ b/core/integration/lhc/plugin.go @@ -0,0 +1,342 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2025 CERN and copyright holders of ALICE O². + * Author: Piotr Konopka + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package lhc + +import ( + "context" + "encoding/json" + "errors" + "io" + "strconv" + "strings" + "sync" + "time" + + cmnevent "github.com/AliceO2Group/Control/common/event" + "github.com/AliceO2Group/Control/common/event/topic" + "github.com/AliceO2Group/Control/common/logger" + "github.com/AliceO2Group/Control/common/logger/infologger" + pb "github.com/AliceO2Group/Control/common/protos" + "github.com/AliceO2Group/Control/common/utils/uid" + "github.com/AliceO2Group/Control/core/environment" + "github.com/AliceO2Group/Control/core/integration" + lhcevent "github.com/AliceO2Group/Control/core/integration/lhc/event" + "github.com/AliceO2Group/Control/core/workflow/callable" + "github.com/sirupsen/logrus" + "github.com/spf13/viper" +) + +var log = logger.New(logrus.StandardLogger(), "lhcclient") +var dipClientTopic topic.Topic = "dip.lhc.beam_mode" + +// Plugin implements integration.Plugin and listens for LHC updates. +type Plugin struct { + endpoint string + ctx context.Context + mu sync.Mutex + currentState *pb.BeamInfo + reader cmnevent.Reader +} + +func NewPlugin(endpoint string) integration.Plugin { + + return &Plugin{endpoint: endpoint, mu: sync.Mutex{}, currentState: &pb.BeamInfo{BeamMode: pb.BeamMode_UNKNOWN}} +} + +func (p *Plugin) Init(_ string) error { + // use a background context for reader loop; Destroy will Close the reader + p.ctx = context.Background() + + p.reader = cmnevent.NewReaderWithTopic(dipClientTopic, "o2-aliecs-core.lhc") + if p.reader == nil { + return errors.New("could not create a kafka reader for LHC plugin") + } + + // Always perform a short pre-drain to consume any backlog without injecting. + log.WithField(infologger.Level, infologger.IL_Devel). + Info("LHC plugin: draining any initial backlog") + p.drainBacklog(2 * time.Second) + + // If state is still empty, try reading the latest message once. + p.mu.Lock() + empty := p.currentState == nil || p.currentState.BeamMode == pb.BeamMode_UNKNOWN + p.mu.Unlock() + if empty { + if last, err := p.reader.Last(p.ctx); err != nil { + log.WithField(infologger.Level, infologger.IL_Support).WithError(err).Warn("failed to read last LHC state on init") + } else if last != nil { + if bmEvt := last.GetBeamModeEvent(); bmEvt != nil && bmEvt.GetBeamInfo() != nil { + p.mu.Lock() + p.currentState = bmEvt.GetBeamInfo() + p.mu.Unlock() + } + } else { + // nothing to retrieve in the topic, we move on + } + } + + go p.readAndInjectLhcUpdates() + log.WithField(infologger.Level, infologger.IL_Devel).Debug("LHC plugin initialized (client started)") + return nil +} + +// drainBacklog reads messages for a limited time and only updates the plugin state, without injecting to env manager. +func (p *Plugin) drainBacklog(timeout time.Duration) { + drainCtx, cancel := context.WithTimeout(p.ctx, timeout) + defer cancel() + for { + msg, err := p.reader.Next(drainCtx) + if err != nil { + if errors.Is(err, context.DeadlineExceeded) || errors.Is(err, io.EOF) || errors.Is(err, context.Canceled) { + break + } + // transient error: small sleep and continue until timeout + time.Sleep(50 * time.Millisecond) + continue + } + if msg == nil { + continue + } + if beamModeEvent := msg.GetBeamModeEvent(); beamModeEvent != nil && beamModeEvent.GetBeamInfo() != nil { + beamInfo := beamModeEvent.GetBeamInfo() + log.WithField(infologger.Level, infologger.IL_Devel). + Debugf("new LHC update received while draining backlog: BeamMode=%s, FillNumber=%d, FillingScheme=%s, StableBeamsStart=%d, StableBeamsEnd=%d, BeamType=%s", + beamInfo.GetBeamMode().String(), beamInfo.GetFillNumber(), beamInfo.GetFillingSchemeName(), + beamInfo.GetStableBeamsStart(), beamInfo.GetStableBeamsEnd(), beamInfo.GetBeamType()) + + p.mu.Lock() + p.currentState = beamModeEvent.GetBeamInfo() + p.mu.Unlock() + } + } +} + +func (p *Plugin) GetName() string { return "lhc" } +func (p *Plugin) GetPrettyName() string { return "LHC (DIP/Kafka client)" } +func (p *Plugin) GetEndpoint() string { + return strings.Join(viper.GetStringSlice("kafkaEndpoints"), ",") +} + +func (p *Plugin) GetConnectionState() string { + if p == nil || p.reader == nil { + return "UNKNOWN" + } + return "READY" // Unfortunately, kafka.Reader does not provide any GetStatus method +} + +func (p *Plugin) GetData(_ []any) string { + p.mu.Lock() + defer p.mu.Unlock() + if p.currentState == nil { + return "" + } + + outMap := make(map[string]interface{}) + outMap["BeamMode"] = p.currentState.BeamMode.String() + outMap["BeamType"] = p.currentState.BeamType + outMap["FillingSchemeName"] = p.currentState.FillingSchemeName + outMap["FillNumber"] = p.currentState.FillNumber + outMap["StableBeamsEnd"] = p.currentState.StableBeamsEnd + outMap["StableBeamsStart"] = p.currentState.StableBeamsStart + + b, _ := json.Marshal(outMap) + return string(b) +} + +func (p *Plugin) GetEnvironmentsData(envIds []uid.ID) map[uid.ID]string { + // there is nothing sensible we could provide here, LHC client is not environment-specific + return nil +} + +func (p *Plugin) GetEnvironmentsShortData(envIds []uid.ID) map[uid.ID]string { + return p.GetEnvironmentsData(envIds) +} + +func (p *Plugin) ObjectStack(_ map[string]string, _ map[string]string) (stack map[string]interface{}) { + return make(map[string]interface{}) +} +func (p *Plugin) CallStack(data interface{}) (stack map[string]interface{}) { + call, ok := data.(*callable.Call) + if !ok { + return + } + + stack = make(map[string]interface{}) + stack["UpdateFillInfo"] = func() (out string) { + p.updateFillInfo(call) + return + } + return +} + +func (p *Plugin) Destroy() error { + if p == nil { + return nil + } + p.mu.Lock() + defer p.mu.Unlock() + + if p.reader != nil { + err := p.reader.Close() + if err != nil { + return err + } + } + return nil +} + +func (p *Plugin) readAndInjectLhcUpdates() { + for { + msg, err := p.reader.Next(p.ctx) + if errors.Is(err, io.EOF) { + log.WithField(infologger.Level, infologger.IL_Support). + Debug("received an EOF from Kafka reader, likely cancellation was requested, breaking") + break + } + if err != nil { + log.WithField(infologger.Level, infologger.IL_Support). + WithError(err). + Error("error while reading from Kafka") + // in case of errors, we throttle the loop to mitigate the risk a log spam if error persists + time.Sleep(time.Second * 1) + continue + } + if msg == nil { + log.WithField(infologger.Level, infologger.IL_Devel). + Warn("received an empty message with no error. it's unexpected, but continuing") + continue + } + + if bmEvt := msg.GetBeamModeEvent(); bmEvt != nil && bmEvt.GetBeamInfo() != nil { + beamInfo := bmEvt.GetBeamInfo() + log.WithField(infologger.Level, infologger.IL_Devel). + Debugf("new LHC update received: BeamMode=%s, FillNumber=%d, FillingScheme=%s, StableBeamsStart=%d, StableBeamsEnd=%d, BeamType=%s", + beamInfo.GetBeamMode().String(), beamInfo.GetFillNumber(), beamInfo.GetFillingSchemeName(), + beamInfo.GetStableBeamsStart(), beamInfo.GetStableBeamsEnd(), beamInfo.GetBeamType()) + // update plugin state + p.mu.Lock() + p.currentState = beamInfo + p.mu.Unlock() + + // convert to internal LHC event and notify environment manager + go func(beamInfo *pb.BeamInfo) { + envMan := environment.ManagerInstance() + + ev := &lhcevent.LhcStateChangeEvent{ + IntegratedServiceEventBase: cmnevent.IntegratedServiceEventBase{ServiceName: "LHC"}, + BeamInfo: lhcevent.BeamInfo{ + BeamMode: beamInfo.GetBeamMode(), + StableBeamsStart: beamInfo.GetStableBeamsStart(), + StableBeamsEnd: beamInfo.GetStableBeamsEnd(), + FillNumber: beamInfo.GetFillNumber(), + FillingSchemeName: beamInfo.GetFillingSchemeName(), + BeamType: beamInfo.GetBeamType(), + }, + } + envMan.NotifyIntegratedServiceEvent(ev) + }(beamInfo) + } + } +} + +// UpdateFillInfo: propagate latest LHC fill info into the environment's global runtime vars +func (p *Plugin) updateFillInfo(call *callable.Call) (out string) { + varStack := call.VarStack + envId, ok := varStack["environment_id"] + if !ok { + err := errors.New("cannot acquire environment ID") + log.Error(err) + + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = "LHC plugin Call Stack failed" + return + } + + log := log.WithFields(logrus.Fields{ + "partition": envId, + "call": "UpdateFillInfo", + }) + + parentRole, ok := call.GetParentRole().(callable.ParentRole) + if !ok || parentRole == nil { + log.WithField(infologger.Level, infologger.IL_Support). + Error("cannot access parent role to propagate LHC fill info") + return + } + + if p.currentState == nil { + log.WithField(infologger.Level, infologger.IL_Support). + Warn("attempted to update environment with fill info, but fill info is not available in plugin") + return + } + + // note: the following was causing very weird behaviours, which could be attributed to memory corruption. + // I did not manage to understand why can't we safely clone such a proto message. + // state := proto.Clone(p.currentState).(*pb.BeamInfo) + + p.mu.Lock() + defer p.mu.Unlock() + state := p.currentState + + parentRole.SetGlobalRuntimeVar("fill_info_beam_mode", state.BeamMode.String()) + + // If NO_BEAM, clear all other fill info and return + if state.BeamMode == pb.BeamMode_NO_BEAM { + parentRole.DeleteGlobalRuntimeVar("fill_info_fill_number") + parentRole.DeleteGlobalRuntimeVar("fill_info_filling_scheme") + parentRole.DeleteGlobalRuntimeVar("fill_info_beam_type") + parentRole.DeleteGlobalRuntimeVar("fill_info_stable_beams_start_ms") + parentRole.DeleteGlobalRuntimeVar("fill_info_stable_beams_end_ms") + + log.WithField(infologger.Level, infologger.IL_Devel). + Debug("NO_BEAM — cleared fill info vars and set beam mode only") + return + } + + // Otherwise, propagate latest known info + parentRole.SetGlobalRuntimeVar("fill_info_fill_number", strconv.FormatInt(int64(state.FillNumber), 10)) + parentRole.SetGlobalRuntimeVar("fill_info_filling_scheme", state.FillingSchemeName) + parentRole.SetGlobalRuntimeVar("fill_info_beam_type", state.BeamType) + if state.StableBeamsStart > 0 { + parentRole.SetGlobalRuntimeVar("fill_info_stable_beams_start_ms", strconv.FormatInt(state.StableBeamsStart, 10)) + } else { + parentRole.DeleteGlobalRuntimeVar("fill_info_stable_beams_start_ms") + } + if state.StableBeamsEnd > 0 { + parentRole.SetGlobalRuntimeVar("fill_info_stable_beams_end_ms", strconv.FormatInt(state.StableBeamsEnd, 10)) + } else { + parentRole.DeleteGlobalRuntimeVar("fill_info_stable_beams_end_ms") + } + + log.WithField("fillNumber", state.FillNumber). + WithField("fillingScheme", state.FillingSchemeName). + WithField("beamType", state.BeamType). + WithField("beamMode", state.BeamMode). + WithField("stableStartMs", state.StableBeamsStart). + WithField("stableEndMs", state.StableBeamsEnd). + WithField(infologger.Level, infologger.IL_Devel). + Debug("updated environment fill info from latest snapshot") + return +} diff --git a/core/integration/odc/client.go b/core/integration/odc/client.go new file mode 100644 index 000000000..d8d15cabd --- /dev/null +++ b/core/integration/odc/client.go @@ -0,0 +1,171 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2021 CERN and copyright holders of ALICE O². + * Author: Teo Mrnjavac + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package odc + +import ( + "context" + "time" + + "github.com/AliceO2Group/Control/common/logger" + "github.com/AliceO2Group/Control/common/monitoring" + odcpb "github.com/AliceO2Group/Control/core/integration/odc/protos" + "github.com/sirupsen/logrus" + "github.com/spf13/viper" + "google.golang.org/grpc" + "google.golang.org/grpc/backoff" + "google.golang.org/grpc/connectivity" + "google.golang.org/grpc/keepalive" +) + +var log = logger.New(logrus.StandardLogger(), "odcclient") + +func convertMethodName(method string) (converted string) { + switch method { + case odcpb.ODC_Initialize_FullMethodName: + converted = "Initialize" + case odcpb.ODC_Submit_FullMethodName: + converted = "Submit" + case odcpb.ODC_Activate_FullMethodName: + converted = "Activate" + case odcpb.ODC_Run_FullMethodName: + converted = "Run" + case odcpb.ODC_Update_FullMethodName: + converted = "Update" + case odcpb.ODC_Configure_FullMethodName: + converted = "Configure" + case odcpb.ODC_SetProperties_FullMethodName: + converted = "SetProperties" + case odcpb.ODC_GetState_FullMethodName: + converted = "GetState" + case odcpb.ODC_Start_FullMethodName: + converted = "Start" + case odcpb.ODC_Stop_FullMethodName: + converted = "Stop" + case odcpb.ODC_Reset_FullMethodName: + converted = "Reset" + case odcpb.ODC_Terminate_FullMethodName: + converted = "Terminate" + case odcpb.ODC_Shutdown_FullMethodName: + converted = "Shutdown" + case odcpb.ODC_Status_FullMethodName: + converted = "Status" + default: + converted = "Unknown" + } + return +} + +type RpcClient struct { + odcpb.ODCClient + conn *grpc.ClientConn + cancel context.CancelFunc +} + +func NewClient(cxt context.Context, cancel context.CancelFunc, endpoint string) *RpcClient { + log.WithFields(logrus.Fields{ + "endpoint": endpoint, + }).Debug("dialing ODC endpoint") + + dialOptions := []grpc.DialOption{ + grpc.WithInsecure(), + grpc.WithConnectParams(grpc.ConnectParams{ + Backoff: backoff.Config{ + BaseDelay: backoff.DefaultConfig.BaseDelay, + Multiplier: backoff.DefaultConfig.Multiplier, + Jitter: backoff.DefaultConfig.Jitter, + MaxDelay: 20 * time.Second, + }, + MinConnectTimeout: 10 * time.Second, + }), + grpc.WithKeepaliveParams(keepalive.ClientParameters{ + Time: 5 * time.Second, + Timeout: time.Second, + PermitWithoutStream: true, + }), + grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(ODC_MAX_INBOUND_MESSAGE_SIZE)), + grpc.WithUnaryInterceptor(monitoring.SetupUnaryClientInterceptor("odc", convertMethodName)), + } + if !viper.GetBool("odcUseSystemProxy") { + dialOptions = append(dialOptions, grpc.WithNoProxy()) + } + conn, err := grpc.DialContext(cxt, + endpoint, + dialOptions..., + ) + if err != nil { + log.WithField("error", err.Error()). + WithField("endpoint", endpoint). + Errorf("cannot dial RPC endpoint") + cancel() + return nil + } + + go func() { + connState := connectivity.Idle + stateChangedNotify := make(chan bool) + notifyFunc := func(st connectivity.State) { + ok := conn.WaitForStateChange(cxt, st) + stateChangedNotify <- ok + } + go notifyFunc(connState) + + for { + select { + case ok := <-stateChangedNotify: + if !ok { + return + } + connState = conn.GetState() + log.Debugf("ODC client %s", connState.String()) + go notifyFunc(connState) + case <-time.After(2 * time.Minute): + if conn.GetState() != connectivity.Ready { + conn.ResetConnectBackoff() + } + case <-cxt.Done(): + return + } + } + }() + + client := &RpcClient{ + ODCClient: odcpb.NewODCClient(conn), + conn: conn, + } + + return client +} + +func (m *RpcClient) GetConnState() connectivity.State { + if m.conn == nil { + return connectivity.Idle + } + return m.conn.GetState() +} + +func (m *RpcClient) Close() error { + m.cancel() + return m.conn.Close() +} diff --git a/core/integration/odc/event/odcevent.go b/core/integration/odc/event/odcevent.go new file mode 100644 index 000000000..b2176c838 --- /dev/null +++ b/core/integration/odc/event/odcevent.go @@ -0,0 +1,55 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2023 CERN and copyright holders of ALICE O². + * Author: Teo Mrnjavac + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package event + +import ( + "github.com/AliceO2Group/Control/common/event" + "github.com/AliceO2Group/Control/common/utils/uid" +) + +type OdcPartitionStateChangeEvent struct { + event.IntegratedServiceEventBase + EnvironmentId uid.ID `json:"serviceName"` + State string `json:"state"` + EcsState string `json:"ecsState"` +} + +func (e *OdcPartitionStateChangeEvent) GetName() string { + return "ODC_PARTITION_STATE_CHANGE_EVENT" +} + +func (e *OdcPartitionStateChangeEvent) GetEnvironmentId() uid.ID { + if e == nil { + return "" + } + return e.EnvironmentId +} + +func (e *OdcPartitionStateChangeEvent) GetState() string { + if e == nil { + return "" + } + return e.State +} diff --git a/core/integration/odc/fairmq/fairmq.go b/core/integration/odc/fairmq/fairmq.go new file mode 100644 index 000000000..52b747e39 --- /dev/null +++ b/core/integration/odc/fairmq/fairmq.go @@ -0,0 +1,71 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2024 CERN and copyright holders of ALICE O². + * Author: Teo Mrnjavac + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package fairmq + +import ( + "github.com/AliceO2Group/Control/core/task/sm" +) + +var ( + fairMqStateMap map[string]sm.State +) + +func init() { + // see https://github.com/FairRootGroup/FairMQ/blob/master/docs/images/device_states.svg + fairMqStateMap = map[string]sm.State{ + "IDLE": sm.STANDBY, + "READY": sm.CONFIGURED, + "RUNNING": sm.RUNNING, + "ERROR": sm.ERROR, + "EXITING": sm.DONE, + + "INITIALIZING DEVICE": sm.INVARIANT, + "INITIALIZED": sm.INVARIANT, + "BINDING": sm.INVARIANT, + "BOUND": sm.INVARIANT, + "CONNECTING": sm.INVARIANT, + "DEVICE READY": sm.INVARIANT, + "INITIALIZING TASK": sm.INVARIANT, + "RESETTING TASK": sm.INVARIANT, + "RESETTING DEVICE": sm.INVARIANT, + "OK": sm.INVARIANT, + "MIXED": sm.INVARIANT, + } +} + +func toEcsState(fairMqState string) sm.State { + if newEcsState, has := fairMqStateMap[fairMqState]; has { + return newEcsState + } + return sm.UNKNOWN +} + +func ToEcsState(fairMqState string, previousEcsState sm.State) sm.State { + if ecsState := toEcsState(fairMqState); ecsState != sm.INVARIANT { + return ecsState + } else { + return previousEcsState + } +} diff --git a/core/integration/odc/fairmq/fairmq_test.go b/core/integration/odc/fairmq/fairmq_test.go new file mode 100644 index 000000000..2e6856308 --- /dev/null +++ b/core/integration/odc/fairmq/fairmq_test.go @@ -0,0 +1,250 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2024 CERN and copyright holders of ALICE O². + * Author: Teo Mrnjavac + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package fairmq + +import ( + "testing" + + "github.com/AliceO2Group/Control/core/task/sm" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +var _ = Describe("fairmq", func() { + Describe("ODC to AliECS state mapping", func() { + var ( + ecsState sm.State + ) + When("transitioning ODC partition in sync with ECS environment", func() { + BeforeEach(func() { + ecsState = sm.UNKNOWN + }) + It("should correctly report ECS states for an ordinary environment lifecycle", func() { + ecsState = ToEcsState("UNKNOWN", ecsState) + Expect(ecsState).To(Equal(sm.UNKNOWN)) + ecsState = ToEcsState("IDLE", ecsState) + Expect(ecsState).To(Equal(sm.STANDBY)) + ecsState = ToEcsState("INITIALIZING DEVICE", ecsState) + Expect(ecsState).To(Equal(sm.STANDBY)) + ecsState = ToEcsState("INITIALIZED", ecsState) + Expect(ecsState).To(Equal(sm.STANDBY)) + ecsState = ToEcsState("BINDING", ecsState) + Expect(ecsState).To(Equal(sm.STANDBY)) + ecsState = ToEcsState("BOUND", ecsState) + Expect(ecsState).To(Equal(sm.STANDBY)) + ecsState = ToEcsState("CONNECTING", ecsState) + Expect(ecsState).To(Equal(sm.STANDBY)) + ecsState = ToEcsState("DEVICE READY", ecsState) + Expect(ecsState).To(Equal(sm.STANDBY)) + ecsState = ToEcsState("INITIALIZING TASK", ecsState) + Expect(ecsState).To(Equal(sm.STANDBY)) + ecsState = ToEcsState("READY", ecsState) + Expect(ecsState).To(Equal(sm.CONFIGURED)) + ecsState = ToEcsState("RUNNING", ecsState) + Expect(ecsState).To(Equal(sm.RUNNING)) + ecsState = ToEcsState("READY", ecsState) + Expect(ecsState).To(Equal(sm.CONFIGURED)) + ecsState = ToEcsState("RUNNING", ecsState) // start-stop-start + Expect(ecsState).To(Equal(sm.RUNNING)) + ecsState = ToEcsState("READY", ecsState) + Expect(ecsState).To(Equal(sm.CONFIGURED)) + ecsState = ToEcsState("RESETTING TASK", ecsState) + Expect(ecsState).To(Equal(sm.CONFIGURED)) + ecsState = ToEcsState("DEVICE READY", ecsState) + Expect(ecsState).To(Equal(sm.CONFIGURED)) + ecsState := ToEcsState("RESETTING DEVICE", ecsState) + Expect(ecsState).To(Equal(sm.CONFIGURED)) + ecsState = ToEcsState("IDLE", ecsState) + Expect(ecsState).To(Equal(sm.STANDBY)) + ecsState = ToEcsState("EXITING", ecsState) + Expect(ecsState).To(Equal(sm.DONE)) + }) + It("should correctly report ECS states for an environment lifecycle that goes to ERROR during CONNECTING", func() { + ecsState = ToEcsState("IDLE", ecsState) + Expect(ecsState).To(Equal(sm.STANDBY)) + ecsState = ToEcsState("INITIALIZING DEVICE", ecsState) + Expect(ecsState).To(Equal(sm.STANDBY)) + ecsState = ToEcsState("INITIALIZED", ecsState) + Expect(ecsState).To(Equal(sm.STANDBY)) + ecsState = ToEcsState("BINDING", ecsState) + Expect(ecsState).To(Equal(sm.STANDBY)) + ecsState = ToEcsState("BOUND", ecsState) + Expect(ecsState).To(Equal(sm.STANDBY)) + ecsState = ToEcsState("CONNECTING", ecsState) + Expect(ecsState).To(Equal(sm.STANDBY)) + ecsState = ToEcsState("ERROR", ecsState) + Expect(ecsState).To(Equal(sm.ERROR)) + ecsState = ToEcsState("EXITING", ecsState) + Expect(ecsState).To(Equal(sm.DONE)) + }) + It("should correctly report ECS states for an environment lifecycle that goes to ERROR during READY", func() { + ecsState = ToEcsState("IDLE", ecsState) + Expect(ecsState).To(Equal(sm.STANDBY)) + ecsState = ToEcsState("INITIALIZING DEVICE", ecsState) + Expect(ecsState).To(Equal(sm.STANDBY)) + ecsState = ToEcsState("INITIALIZED", ecsState) + Expect(ecsState).To(Equal(sm.STANDBY)) + ecsState = ToEcsState("BINDING", ecsState) + Expect(ecsState).To(Equal(sm.STANDBY)) + ecsState = ToEcsState("BOUND", ecsState) + Expect(ecsState).To(Equal(sm.STANDBY)) + ecsState = ToEcsState("CONNECTING", ecsState) + Expect(ecsState).To(Equal(sm.STANDBY)) + ecsState = ToEcsState("DEVICE READY", ecsState) + Expect(ecsState).To(Equal(sm.STANDBY)) + ecsState = ToEcsState("INITIALIZING TASK", ecsState) + Expect(ecsState).To(Equal(sm.STANDBY)) + ecsState = ToEcsState("READY", ecsState) + Expect(ecsState).To(Equal(sm.CONFIGURED)) + ecsState = ToEcsState("ERROR", ecsState) + Expect(ecsState).To(Equal(sm.ERROR)) + ecsState = ToEcsState("EXITING", ecsState) + Expect(ecsState).To(Equal(sm.DONE)) + }) + It("should correctly report ECS states for an environment lifecycle that goes to ERROR during RUNNING", func() { + ecsState = ToEcsState("IDLE", ecsState) + Expect(ecsState).To(Equal(sm.STANDBY)) + ecsState = ToEcsState("INITIALIZING DEVICE", ecsState) + Expect(ecsState).To(Equal(sm.STANDBY)) + ecsState = ToEcsState("INITIALIZED", ecsState) + Expect(ecsState).To(Equal(sm.STANDBY)) + ecsState = ToEcsState("BINDING", ecsState) + Expect(ecsState).To(Equal(sm.STANDBY)) + ecsState = ToEcsState("BOUND", ecsState) + Expect(ecsState).To(Equal(sm.STANDBY)) + ecsState = ToEcsState("CONNECTING", ecsState) + Expect(ecsState).To(Equal(sm.STANDBY)) + ecsState = ToEcsState("DEVICE READY", ecsState) + Expect(ecsState).To(Equal(sm.STANDBY)) + ecsState = ToEcsState("INITIALIZING TASK", ecsState) + Expect(ecsState).To(Equal(sm.STANDBY)) + ecsState = ToEcsState("READY", ecsState) + Expect(ecsState).To(Equal(sm.CONFIGURED)) + ecsState = ToEcsState("RUNNING", ecsState) + Expect(ecsState).To(Equal(sm.RUNNING)) + ecsState = ToEcsState("ERROR", ecsState) + Expect(ecsState).To(Equal(sm.ERROR)) + ecsState = ToEcsState("EXITING", ecsState) + Expect(ecsState).To(Equal(sm.DONE)) + }) + It("should correctly report ECS states for an environment lifecycle that unexpectedly resets at INITIALIZED", func() { + ecsState = ToEcsState("IDLE", ecsState) + Expect(ecsState).To(Equal(sm.STANDBY)) + ecsState = ToEcsState("INITIALIZING DEVICE", ecsState) + Expect(ecsState).To(Equal(sm.STANDBY)) + ecsState = ToEcsState("INITIALIZED", ecsState) + Expect(ecsState).To(Equal(sm.STANDBY)) + ecsState := ToEcsState("RESETTING DEVICE", ecsState) + Expect(ecsState).To(Equal(sm.STANDBY)) + ecsState = ToEcsState("IDLE", ecsState) + Expect(ecsState).To(Equal(sm.STANDBY)) + ecsState = ToEcsState("EXITING", ecsState) + Expect(ecsState).To(Equal(sm.DONE)) + }) + It("should correctly report ECS states for an environment lifecycle that unexpectedly resets at DEVICE READY", func() { + ecsState = ToEcsState("IDLE", ecsState) + Expect(ecsState).To(Equal(sm.STANDBY)) + ecsState = ToEcsState("INITIALIZING DEVICE", ecsState) + Expect(ecsState).To(Equal(sm.STANDBY)) + ecsState = ToEcsState("INITIALIZED", ecsState) + Expect(ecsState).To(Equal(sm.STANDBY)) + ecsState = ToEcsState("BINDING", ecsState) + Expect(ecsState).To(Equal(sm.STANDBY)) + ecsState = ToEcsState("BOUND", ecsState) + Expect(ecsState).To(Equal(sm.STANDBY)) + ecsState = ToEcsState("CONNECTING", ecsState) + Expect(ecsState).To(Equal(sm.STANDBY)) + ecsState = ToEcsState("DEVICE READY", ecsState) + Expect(ecsState).To(Equal(sm.STANDBY)) + ecsState := ToEcsState("RESETTING DEVICE", ecsState) + Expect(ecsState).To(Equal(sm.STANDBY)) + ecsState = ToEcsState("IDLE", ecsState) + Expect(ecsState).To(Equal(sm.STANDBY)) + ecsState = ToEcsState("EXITING", ecsState) + Expect(ecsState).To(Equal(sm.DONE)) + }) + It("should correctly report ECS states for an environment lifecycle that resets at READY without going to RUNNING", func() { + ecsState = ToEcsState("IDLE", ecsState) + Expect(ecsState).To(Equal(sm.STANDBY)) + ecsState = ToEcsState("INITIALIZING DEVICE", ecsState) + Expect(ecsState).To(Equal(sm.STANDBY)) + ecsState = ToEcsState("INITIALIZED", ecsState) + Expect(ecsState).To(Equal(sm.STANDBY)) + ecsState = ToEcsState("BINDING", ecsState) + Expect(ecsState).To(Equal(sm.STANDBY)) + ecsState = ToEcsState("BOUND", ecsState) + Expect(ecsState).To(Equal(sm.STANDBY)) + ecsState = ToEcsState("CONNECTING", ecsState) + Expect(ecsState).To(Equal(sm.STANDBY)) + ecsState = ToEcsState("DEVICE READY", ecsState) + Expect(ecsState).To(Equal(sm.STANDBY)) + ecsState = ToEcsState("INITIALIZING TASK", ecsState) + Expect(ecsState).To(Equal(sm.STANDBY)) + ecsState = ToEcsState("READY", ecsState) + Expect(ecsState).To(Equal(sm.CONFIGURED)) + ecsState = ToEcsState("RESETTING TASK", ecsState) + Expect(ecsState).To(Equal(sm.CONFIGURED)) + ecsState = ToEcsState("DEVICE READY", ecsState) + Expect(ecsState).To(Equal(sm.CONFIGURED)) + ecsState := ToEcsState("RESETTING DEVICE", ecsState) + Expect(ecsState).To(Equal(sm.CONFIGURED)) + ecsState = ToEcsState("IDLE", ecsState) + Expect(ecsState).To(Equal(sm.STANDBY)) + ecsState = ToEcsState("EXITING", ecsState) + Expect(ecsState).To(Equal(sm.DONE)) + }) + It("should correctly report ECS states for an environment lifecycle that sends invalid states", func() { + ecsState = ToEcsState("IDLE", ecsState) + Expect(ecsState).To(Equal(sm.STANDBY)) + ecsState = ToEcsState("INITIALIZING DEVICE", ecsState) + Expect(ecsState).To(Equal(sm.STANDBY)) + ecsState = ToEcsState("OK", ecsState) + Expect(ecsState).To(Equal(sm.STANDBY)) + ecsState = ToEcsState("INITIALIZED", ecsState) + Expect(ecsState).To(Equal(sm.STANDBY)) + ecsState = ToEcsState("", ecsState) + Expect(ecsState).To(Equal(sm.UNKNOWN)) + ecsState = ToEcsState("READY", ecsState) + Expect(ecsState).To(Equal(sm.CONFIGURED)) + ecsState = ToEcsState("ROLLING", ecsState) + Expect(ecsState).To(Equal(sm.UNKNOWN)) + ecsState = ToEcsState("OK", ecsState) + Expect(ecsState).To(Equal(sm.UNKNOWN)) + ecsState = ToEcsState("IDLE", ecsState) + Expect(ecsState).To(Equal(sm.STANDBY)) + ecsState = ToEcsState("MIXED", ecsState) + Expect(ecsState).To(Equal(sm.STANDBY)) + ecsState = ToEcsState("READY", ecsState) + Expect(ecsState).To(Equal(sm.CONFIGURED)) + }) + + }) + }) +}) + +func TestState(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "ODC state mapping test suite") +} diff --git a/core/integration/odc/handlers.go b/core/integration/odc/handlers.go new file mode 100644 index 000000000..6727c1af3 --- /dev/null +++ b/core/integration/odc/handlers.go @@ -0,0 +1,1668 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2020-2024 CERN and copyright holders of ALICE O². + * Author: Teo Mrnjavac + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package odc + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "strconv" + "strings" + "sync" + "time" + + "github.com/AliceO2Group/Control/common/logger/infologger" + pb "github.com/AliceO2Group/Control/common/protos" + "github.com/AliceO2Group/Control/common/utils" + "github.com/AliceO2Group/Control/core/environment" + "github.com/AliceO2Group/Control/core/integration/odc/odcutils" + odcpb "github.com/AliceO2Group/Control/core/integration/odc/protos" + "github.com/AliceO2Group/Control/core/the" + "github.com/AliceO2Group/Control/core/workflow/callable" + "github.com/sirupsen/logrus" + "google.golang.org/grpc" + "google.golang.org/grpc/status" +) + +const ODC_ERROR_MAX_LENGTH = 250 + +func handleGetState(ctx context.Context, odcClient *RpcClient, envId string) (string, error) { + defer utils.TimeTrackFunction(time.Now(), log.WithPrefix("odcclient")) + req := &odcpb.StateRequest{ + Partitionid: envId, + Path: "", + Detailed: false, + } + + var ( + err error = nil + newState = "UNKNOWN" + rep *odcpb.StateReply + ) + + if envId == "" { + return newState, errors.New("cannot proceed with empty environment id") + } + + rep, err = odcClient.GetState(ctx, req, grpc.EmptyCallOption{}) + if err != nil { + return newState, printGrpcError(err) + } + + if rep == nil || rep.Reply == nil { + // We got a nil response with nil error, this should never happen + return newState, errors.New("nil response error") + } + + newState = rep.Reply.State + + if odcErr := rep.Reply.GetError(); odcErr != nil { + return newState, fmt.Errorf("code %d from ODC: %s", odcErr.GetCode(), utils.TruncateString(odcErr.GetMsg(), ODC_ERROR_MAX_LENGTH)) + } + if replyStatus := rep.Reply.Status; replyStatus != odcpb.ReplyStatus_SUCCESS { + return newState, fmt.Errorf("status %s from ODC", replyStatus.String()) + } + log.WithFields(logrus.Fields{ + "odcMsg": rep.Reply.Msg, + "odcStatus": rep.Reply.Status.String(), + "odcExectime": rep.Reply.Exectime, + "partition": rep.Reply.Partitionid, + "odcSessionid": rep.Reply.Sessionid, + "odcState": rep.Reply.State, + }). + Debug("call to ODC complete: odc.GetState") + return odcutils.StateForOdcState(newState), err +} + +func setProperties(ctx context.Context, odcClient *RpcClient, arguments map[string]string, paddingTimeout time.Duration, envId string, runNumber uint64, call *callable.Call) error { + + var err error = nil + + setPropertiesRequest := &odcpb.SetPropertiesRequest{ + Partitionid: envId, + Path: "", + Properties: make([]*odcpb.Property, len(arguments)), + Runnr: runNumber, + } + // We ask this ODC call to complete within our own DEADLINE, minus 1 second + ctxDeadline, ok := ctx.Deadline() + if ok { + setPropertiesRequest.Timeout = uint32((time.Until(ctxDeadline) - paddingTimeout).Seconds()) + } + + // Extract relevant parameters from Arguments payload + // and build payload for SetProperty+Start + i := 0 + for k, v := range arguments { + setPropertiesRequest.Properties[i] = &odcpb.Property{ + Key: k, + Value: v, + } + i++ + } + + payload := map[string]interface{}{ + "odcRequest": &setPropertiesRequest, + } + payloadJson, _ := json.Marshal(payload) + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_STARTED, + OperationStep: "perform ODC call: SetProperties", + OperationStepStatus: pb.OpStatus_STARTED, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + }) + + var setPropertiesResponse *odcpb.GeneralReply + setPropertiesResponse, err = odcClient.SetProperties(ctx, setPropertiesRequest, grpc.EmptyCallOption{}) + if err != nil { + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_ERROR, + OperationStep: "perform ODC call: SetProperties", + OperationStepStatus: pb.OpStatus_DONE_ERROR, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + Error: err.Error(), + }) + + return printGrpcError(err) + } + + if setPropertiesResponse == nil { + err = fmt.Errorf("nil response error") + log.WithField("partition", envId).WithError(err). + Debugf("finished call odc.SetProperties, ERROR nil response") + + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_ERROR, + OperationStep: "perform ODC call: SetProperties", + OperationStepStatus: pb.OpStatus_DONE_ERROR, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + Error: err.Error(), + }) + + // We got a nil response with nil error, this should never happen + return err + } + + // We nullify setPropertiesResponse.Hosts because the payload is too large to be included in the outgoing event + setPropertiesResponse.Hosts = nil + + if odcErr := setPropertiesResponse.GetError(); odcErr != nil { + log.WithField("partition", envId). + WithError(err). + Debugf("finished call odc.SetProperties, ERROR in response payload") + + err = fmt.Errorf("code %d from ODC: %s", odcErr.GetCode(), utils.TruncateString(odcErr.GetMsg(), ODC_ERROR_MAX_LENGTH)) + + payload["odcResponse"] = &setPropertiesResponse + payloadJson, _ = json.Marshal(payload) + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_ERROR, + OperationStep: "perform ODC call: SetProperties", + OperationStepStatus: pb.OpStatus_DONE_ERROR, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + Error: err.Error(), + }) + + return err + } + if replyStatus := setPropertiesResponse.Status; replyStatus != odcpb.ReplyStatus_SUCCESS { + log.WithField("partition", envId). + WithError(err). + Debugf("finished call odc.SetProperties, bad status in response payload") + + err = fmt.Errorf("status %s from ODC", replyStatus.String()) + + payload["odcResponse"] = &setPropertiesResponse + payloadJson, _ = json.Marshal(payload) + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_ERROR, + OperationStep: "perform ODC call: SetProperties", + OperationStepStatus: pb.OpStatus_DONE_ERROR, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + Error: err.Error(), + }) + + return err + } + + payload["odcResponse"] = &setPropertiesResponse + payloadJson, _ = json.Marshal(payload) + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_ONGOING, + OperationStep: "perform ODC call: SetProperties", + OperationStepStatus: pb.OpStatus_DONE_OK, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + }) + + log.WithField("partition", envId). + WithFields(logrus.Fields{ + "odcMsg": setPropertiesResponse.Msg, + "odcStatus": setPropertiesResponse.Status.String(), + "odcExectime": setPropertiesResponse.Exectime, + "partition": setPropertiesResponse.Partitionid, + "odcSessionid": setPropertiesResponse.Sessionid, + }). + Debug("call to ODC complete: odc.SetProperties") + + return nil +} + +func handleStart(ctx context.Context, odcClient *RpcClient, arguments map[string]string, paddingTimeout time.Duration, envId string, runNumber uint64, call *callable.Call) error { + defer utils.TimeTrackFunction(time.Now(), log.WithPrefix("odcclient").WithField("partition", envId)) + + var err error = nil + var rep *odcpb.StateReply + + if envId == "" { + return errors.New("cannot proceed with empty environment id") + } + + // SetProperties before START + err = setProperties(ctx, odcClient, arguments, paddingTimeout, envId, runNumber, call) + if err != nil { + return err + } + + // The actual START operation starts here + req := &odcpb.StartRequest{ + Request: &odcpb.StateRequest{ + Partitionid: envId, + Path: "", + Detailed: false, + Runnr: runNumber, + }, + } + // We ask this ODC call to complete within our own DEADLINE, minus 1 second + ctxDeadline, ok := ctx.Deadline() + if ok { + req.Request.Timeout = uint32((time.Until(ctxDeadline) - paddingTimeout).Seconds()) + } + + payload := map[string]interface{}{ + "odcRequest": &req, + } + payloadJson, _ := json.Marshal(payload) + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_ONGOING, + OperationStep: "perform ODC call: Start", + OperationStepStatus: pb.OpStatus_STARTED, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + }) + + rep, err = odcClient.Start(ctx, req, grpc.EmptyCallOption{}) + if err != nil { + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_ERROR, + OperationStep: "perform ODC call: Start", + OperationStepStatus: pb.OpStatus_DONE_ERROR, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + Error: err.Error(), + }) + + return printGrpcError(err) + } + + if rep == nil || rep.Reply == nil { + err = fmt.Errorf("nil response error") + + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_ERROR, + OperationStep: "perform ODC call: Start", + OperationStepStatus: pb.OpStatus_DONE_ERROR, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + Error: err.Error(), + }) + + // We got a nil response with nil error, this should never happen + return err + } + + // We nullify rep.Devices and rep.Reply.Hosts because the payload is too large to be included in the outgoing event + rep.Devices = nil + rep.Reply.Hosts = nil + + if odcErr := rep.Reply.GetError(); odcErr != nil { + err = fmt.Errorf("code %d from ODC: %s", odcErr.GetCode(), utils.TruncateString(odcErr.GetMsg(), ODC_ERROR_MAX_LENGTH)) + + payload["odcResponse"] = &rep + payloadJson, _ = json.Marshal(payload) + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_ERROR, + OperationStep: "perform ODC call: Start", + OperationStepStatus: pb.OpStatus_DONE_ERROR, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + Error: err.Error(), + }) + + return err + } + if replyStatus := rep.Reply.Status; replyStatus != odcpb.ReplyStatus_SUCCESS { + err = fmt.Errorf("status %s from ODC", replyStatus.String()) + + payload["odcResponse"] = &rep + payloadJson, _ = json.Marshal(payload) + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_ERROR, + OperationStep: "perform ODC call: Start", + OperationStepStatus: pb.OpStatus_DONE_ERROR, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + Error: err.Error(), + }) + + return err + } + + payload["odcResponse"] = &rep + payloadJson, _ = json.Marshal(payload) + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_OK, + OperationStep: "perform ODC call: Start", + OperationStepStatus: pb.OpStatus_DONE_OK, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + }) + + log.WithField("partition", envId). + WithFields(logrus.Fields{ + "odcMsg": rep.Reply.Msg, + "odcStatus": rep.Reply.Status.String(), + "odcExectime": rep.Reply.Exectime, + "partition": rep.Reply.Partitionid, + "odcSessionid": rep.Reply.Sessionid, + }). + Debug("call to ODC complete: odc.Start") + return err +} + +func handleStop(ctx context.Context, odcClient *RpcClient, arguments map[string]string, paddingTimeout time.Duration, envId string, runNumber uint64, call *callable.Call) error { + defer utils.TimeTrackFunction(time.Now(), log.WithPrefix("odcclient").WithField("partition", envId)) + var err error = nil + + // SetProperties before STOP + if len(arguments) > 0 { + err = setProperties(ctx, odcClient, arguments, paddingTimeout, envId, runNumber, call) + if err != nil { + log.WithField("partition", envId). + WithField("level", infologger.IL_Support). + WithError(err). + Warn("setProperties call to ODC failed. will continue with odc.Stop") + } + } + + // The actual STOP operation starts here + req := &odcpb.StopRequest{ + Request: &odcpb.StateRequest{ + Partitionid: envId, + Path: "", + Detailed: false, + Runnr: runNumber, + }, + } + // We ask this ODC call to complete within our own DEADLINE, minus 1 second + ctxDeadline, ok := ctx.Deadline() + if ok { + req.Request.Timeout = uint32((time.Until(ctxDeadline) - paddingTimeout).Seconds()) + } + + var rep *odcpb.StateReply + + if envId == "" { + return errors.New("cannot proceed with empty environment id") + } + + payload := map[string]interface{}{ + "odcRequest": &req, + } + payloadJson, _ := json.Marshal(payload) + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_STARTED, + OperationStep: "perform ODC call: Stop", + OperationStepStatus: pb.OpStatus_STARTED, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + }) + + rep, err = odcClient.Stop(ctx, req, grpc.EmptyCallOption{}) + if err != nil { + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_ERROR, + OperationStep: "perform ODC call: Stop", + OperationStepStatus: pb.OpStatus_DONE_ERROR, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + Error: err.Error(), + }) + + return printGrpcError(err) + } + + if rep == nil || rep.Reply == nil { + err = fmt.Errorf("nil response error") + + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_ERROR, + OperationStep: "perform ODC call: Stop", + OperationStepStatus: pb.OpStatus_DONE_ERROR, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + Error: err.Error(), + }) + + // We got a nil response with nil error, this should never happen + return err + } + + // We nullify rep.Devices and rep.Reply.Hosts because the payload is too large to be included in the outgoing event + rep.Devices = nil + rep.Reply.Hosts = nil + + if odcErr := rep.Reply.GetError(); odcErr != nil { + err = fmt.Errorf("code %d from ODC: %s", odcErr.GetCode(), utils.TruncateString(odcErr.GetMsg(), ODC_ERROR_MAX_LENGTH)) + + payload["odcResponse"] = &rep + payloadJson, _ = json.Marshal(payload) + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_ERROR, + OperationStep: "perform ODC call: Stop", + OperationStepStatus: pb.OpStatus_DONE_ERROR, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + Error: err.Error(), + }) + + return err + } + if replyStatus := rep.Reply.Status; replyStatus != odcpb.ReplyStatus_SUCCESS { + err = fmt.Errorf("status %s from ODC", replyStatus.String()) + + payload["odcResponse"] = &rep + payloadJson, _ = json.Marshal(payload) + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_ERROR, + OperationStep: "perform ODC call: Stop", + OperationStepStatus: pb.OpStatus_DONE_ERROR, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + Error: err.Error(), + }) + + return err + } + + payload["odcResponse"] = &rep + payloadJson, _ = json.Marshal(payload) + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_OK, + OperationStep: "perform ODC call: Stop", + OperationStepStatus: pb.OpStatus_DONE_OK, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + }) + + log.WithField("partition", envId). + WithFields(logrus.Fields{ + "odcMsg": rep.Reply.Msg, + "odcStatus": rep.Reply.Status.String(), + "odcExectime": rep.Reply.Exectime, + "partition": rep.Reply.Partitionid, + "odcSessionid": rep.Reply.Sessionid, + }). + Debug("call to ODC complete: odc.Stop") + return err +} + +func handlePartitionTerminate(ctx context.Context, odcClient *RpcClient, arguments map[string]string, paddingTimeout time.Duration, envId string, call *callable.Call) error { + defer utils.TimeTrackFunction(time.Now(), log.WithPrefix("odcclient").WithField("partition", envId)) + if envId == "" { + return errors.New("cannot proceed with empty environment id") + } + + err := doTerminate(ctx, odcClient, arguments, paddingTimeout, envId, call) + if err != nil { + return printGrpcError(err) + } + + err = doShutdown(ctx, odcClient, arguments, paddingTimeout, envId, call) + if err != nil { + return printGrpcError(err) + } + return nil +} + +func handleReset(ctx context.Context, odcClient *RpcClient, arguments map[string]string, paddingTimeout time.Duration, envId string, call *callable.Call) error { + defer utils.TimeTrackFunction(time.Now(), log.WithPrefix("odcclient").WithField("partition", envId)) + if envId == "" { + return errors.New("cannot proceed with empty environment id") + } + + err := doReset(ctx, odcClient, arguments, paddingTimeout, envId, call) + if err != nil { + return printGrpcError(err) + } + + return nil +} + +func handleCleanupLegacy(ctx context.Context, odcClient *RpcClient, arguments map[string]string, paddingTimeout time.Duration, envId string, call *callable.Call) error { + defer utils.TimeTrackFunction(time.Now(), log.WithPrefix("odcclient").WithField("partition", envId)) + if envId == "" { + return errors.New("cannot proceed with empty environment id") + } + + // This function tries to perform the regular teardown sequence. + // Since Shutdown is supposed to work in any state, we don't bail on error. + err := doReset(ctx, odcClient, arguments, paddingTimeout, envId, call) + if err != nil { + log.WithError(printGrpcError(err)). + WithField("level", infologger.IL_Devel). + WithField("partition", envId). + Warn("ODC Reset call failed") + } + + err = doTerminate(ctx, odcClient, arguments, paddingTimeout, envId, call) + if err != nil { + log.WithError(printGrpcError(err)). + WithField("level", infologger.IL_Devel). + WithField("partition", envId). + Warn("ODC Terminate call failed") + } + + err = doShutdown(ctx, odcClient, arguments, paddingTimeout, envId, call) + if err != nil { + log.WithError(printGrpcError(err)). + WithField("level", infologger.IL_Devel). + WithField("partition", envId). + Warn("ODC Shutdown call failed") + } + return nil // We clobber the error because nothing can be done for a failed cleanup +} + +func handleCleanup(ctx context.Context, odcClient *RpcClient, arguments map[string]string, paddingTimeout time.Duration, envId string, call *callable.Call) error { + log.WithField("partition", envId). + Debug("handleCleanup starting") + defer utils.TimeTrackFunction(time.Now(), log.WithPrefix("odcclient").WithField("partition", envId)) + + // First we query ODC for the full list of active partitions + req := &odcpb.StatusRequest{Running: true} + + var err error = nil + var rep *odcpb.StatusReply + + payload := map[string]interface{}{ + "odcRequest": &req, + } + payloadJson, _ := json.Marshal(payload) + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_STARTED, + OperationStep: "perform ODC call: Status", + OperationStepStatus: pb.OpStatus_STARTED, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + }) + + rep, err = odcClient.Status(ctx, req, grpc.EmptyCallOption{}) + if err != nil { + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_ERROR, + OperationStep: "perform ODC call: Status", + OperationStepStatus: pb.OpStatus_DONE_ERROR, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + Error: err.Error(), + }) + + return printGrpcError(err) + } + + if rep == nil || rep.GetStatus() == odcpb.ReplyStatus_UNKNOWN { + err = fmt.Errorf("nil response error") + + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_ERROR, + OperationStep: "perform ODC call: Status", + OperationStepStatus: pb.OpStatus_DONE_ERROR, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + Error: err.Error(), + }) + + // We got a nil response with nil error, this should never happen + return err + } + + if odcErr := rep.GetError(); odcErr != nil { + err = fmt.Errorf("code %d from ODC: %s", odcErr.GetCode(), utils.TruncateString(odcErr.GetMsg(), ODC_ERROR_MAX_LENGTH)) + + payload["odcResponse"] = &rep + payloadJson, _ = json.Marshal(payload) + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_ERROR, + OperationStep: "perform ODC call: Status", + OperationStepStatus: pb.OpStatus_DONE_ERROR, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + Error: err.Error(), + }) + } + if replyStatus := rep.GetStatus(); replyStatus != odcpb.ReplyStatus_SUCCESS { + err = fmt.Errorf("status %s from ODC", replyStatus.String()) + + payload["odcResponse"] = &rep + payloadJson, _ = json.Marshal(payload) + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_ERROR, + OperationStep: "perform ODC call: Status", + OperationStepStatus: pb.OpStatus_DONE_ERROR, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + Error: err.Error(), + }) + + return err + } + + payload["odcResponse"] = &rep + payloadJson, _ = json.Marshal(payload) + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_ONGOING, + OperationStep: "perform ODC call: Status", + OperationStepStatus: pb.OpStatus_DONE_OK, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + }) + + log.WithField("partition", envId). + WithFields(logrus.Fields{ + "odcCall": "Status", + "odcMsg": rep.GetMsg(), + "odcStatus": rep.GetStatus().String(), + "odcExectime": rep.GetExectime(), + }). + Debug("call to ODC complete: odc.GetStatus") + + partitionIdsKnownToOdc := make([]string, len(rep.GetPartitions())) + for i, v := range rep.GetPartitions() { + partitionIdsKnownToOdc[i] = v.Partitionid + } + log.WithField("partition", envId). + Debugf("partitions known to ODC: %s", strings.Join(partitionIdsKnownToOdc, ", ")) + + knownEnvs := environment.ManagerInstance().Ids() + partitionsToClean := make(map[string]struct{}) + for _, odcPartition := range partitionIdsKnownToOdc { + isOrphan := true + for _, knownEnv := range knownEnvs { + if odcPartition == knownEnv.String() { // found a matching env + isOrphan = false + break + } + } + if isOrphan { // no env was found for the given ODC partition + partitionsToClean[odcPartition] = struct{}{} + } + } + + // The present function can in principle be called with envId = "", if the cleanup is triggered from + // outside of an active environment. + // If an envId is passed, we append it to the list of partitions to clean up just in case, otherwise we + // ignore it. + if envId != "" { + partitionsToClean[envId] = struct{}{} + } + + partitionsToCleanStr := make([]string, len(partitionsToClean)) + i := 0 + for k := range partitionsToClean { + partitionsToCleanStr[i] = k + i++ + } + log.WithField("partition", envId). + Debugf("partitions about to be cleaned: %s", strings.Join(partitionIdsKnownToOdc, ", ")) + + wg := &sync.WaitGroup{} + wg.Add(len(partitionsToClean)) + + // Then the actual cleanup calls begin, in parallel... + for odcPartitionId := range partitionsToClean { + go func(odcPartitionId string) { + defer wg.Done() + err = doShutdown(ctx, odcClient, arguments, paddingTimeout, odcPartitionId, call) // FIXME make this parallel + if err != nil { + log.WithError(printGrpcError(err)). + WithField("level", infologger.IL_Devel). + WithField("partition", odcPartitionId). + Warn("ODC Shutdown call failed") + } + }(odcPartitionId) + } + wg.Wait() + + if len(partitionsToClean) == 0 { + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_OK, + OperationStep: "no cleanup necessary", + OperationStepStatus: pb.OpStatus_DONE_OK, + EnvironmentId: envId, + }) + } else { + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_OK, + OperationStep: "perform ODC calls for all known partitions: Shutdown", + OperationStepStatus: pb.OpStatus_DONE_OK, + EnvironmentId: envId, + }) + } + return nil // We clobber the error because nothing can be done for a failed cleanup +} + +func doReset(ctx context.Context, odcClient *RpcClient, arguments map[string]string, paddingTimeout time.Duration, envId string, call *callable.Call) error { + // RESET + req := &odcpb.ResetRequest{ + Request: &odcpb.StateRequest{ + Partitionid: envId, + Path: "", + Detailed: false, + }, + } + // We ask this ODC call to complete within our own DEADLINE, minus 1 second + ctxDeadline, ok := ctx.Deadline() + if ok { + req.Request.Timeout = uint32((time.Until(ctxDeadline) - paddingTimeout).Seconds()) + } + + var err error = nil + var rep *odcpb.StateReply + + payload := map[string]interface{}{ + "odcRequest": &req, + } + payloadJson, _ := json.Marshal(payload) + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_STARTED, + OperationStep: "perform ODC call: Reset", + OperationStepStatus: pb.OpStatus_STARTED, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + }) + + rep, err = odcClient.Reset(ctx, req, grpc.EmptyCallOption{}) + if err != nil { + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_ERROR, + OperationStep: "perform ODC call: Reset", + OperationStepStatus: pb.OpStatus_DONE_ERROR, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + Error: err.Error(), + }) + + return printGrpcError(err) + } + + if rep == nil || rep.Reply == nil { + // We got a nil response with nil error, this should never happen + err = errors.New("nil response error") + + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_ERROR, + OperationStep: "perform ODC call: Reset", + OperationStepStatus: pb.OpStatus_DONE_ERROR, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + Error: err.Error(), + }) + + return err + } + + // We nullify rep.Devices and rep.Reply.Hosts because the payload is too large to be included in the outgoing event + rep.Devices = nil + rep.Reply.Hosts = nil + + if odcErr := rep.Reply.GetError(); odcErr != nil { + err = fmt.Errorf("code %d from ODC: %s", odcErr.GetCode(), utils.TruncateString(odcErr.GetMsg(), ODC_ERROR_MAX_LENGTH)) + + payload["odcResponse"] = &rep + payloadJson, _ = json.Marshal(payload) + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_ERROR, + OperationStep: "perform ODC call: Reset", + OperationStepStatus: pb.OpStatus_DONE_ERROR, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + Error: err.Error(), + }) + + return err + } + if replyStatus := rep.Reply.Status; replyStatus != odcpb.ReplyStatus_SUCCESS { + err = fmt.Errorf("status %s from ODC", replyStatus.String()) + + payload["odcResponse"] = &rep + payloadJson, _ = json.Marshal(payload) + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_ERROR, + OperationStep: "perform ODC call: Reset", + OperationStepStatus: pb.OpStatus_DONE_ERROR, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + Error: err.Error(), + }) + + return err + } + + payload["odcResponse"] = &rep + payloadJson, _ = json.Marshal(payload) + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_OK, + OperationStep: "perform ODC call: Reset", + OperationStepStatus: pb.OpStatus_DONE_OK, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + }) + + log.WithFields(logrus.Fields{ + "odcMsg": rep.Reply.Msg, + "odcStatus": rep.Reply.Status.String(), + "odcExectime": rep.Reply.Exectime, + "partition": rep.Reply.Partitionid, + "odcSessionid": rep.Reply.Sessionid, + }). + Debug("call to ODC complete: odc.Reset") + return err +} + +func doTerminate(ctx context.Context, odcClient *RpcClient, arguments map[string]string, paddingTimeout time.Duration, envId string, call *callable.Call) error { + // TERMINATE + req := &odcpb.TerminateRequest{ + Request: &odcpb.StateRequest{ + Partitionid: envId, + Path: "", + Detailed: false, + }, + } + // We ask this ODC call to complete within our own DEADLINE, minus 1 second + ctxDeadline, ok := ctx.Deadline() + if ok { + req.Request.Timeout = uint32((time.Until(ctxDeadline) - paddingTimeout).Seconds()) + } + + var err error = nil + var rep *odcpb.StateReply + + payload := map[string]interface{}{ + "odcRequest": &req, + } + payloadJson, _ := json.Marshal(payload) + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_STARTED, + OperationStep: "perform ODC call: Terminate", + OperationStepStatus: pb.OpStatus_STARTED, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + }) + + rep, err = odcClient.Terminate(ctx, req, grpc.EmptyCallOption{}) + if err != nil { + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_ERROR, + OperationStep: "perform ODC call: Terminate", + OperationStepStatus: pb.OpStatus_DONE_ERROR, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + Error: err.Error(), + }) + + return printGrpcError(err) + } + + if rep == nil || rep.Reply == nil { + // We got a nil response with nil error, this should never happen + err = errors.New("nil response error") + + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_ERROR, + OperationStep: "perform ODC call: Terminate", + OperationStepStatus: pb.OpStatus_DONE_ERROR, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + Error: err.Error(), + }) + + return err + } + + // We nullify rep.Devices and rep.Reply.Hosts because the payload is too large to be included in the outgoing event + rep.Devices = nil + rep.Reply.Hosts = nil + + if odcErr := rep.Reply.GetError(); odcErr != nil { + err = fmt.Errorf("code %d from ODC: %s", odcErr.GetCode(), utils.TruncateString(odcErr.GetMsg(), ODC_ERROR_MAX_LENGTH)) + + payload["odcResponse"] = &rep + payloadJson, _ = json.Marshal(payload) + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_ERROR, + OperationStep: "perform ODC call: Terminate", + OperationStepStatus: pb.OpStatus_DONE_ERROR, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + Error: err.Error(), + }) + + return err + } + if replyStatus := rep.Reply.Status; replyStatus != odcpb.ReplyStatus_SUCCESS { + err = fmt.Errorf("status %s from ODC", replyStatus.String()) + + payload["odcResponse"] = &rep + payloadJson, _ = json.Marshal(payload) + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_ERROR, + OperationStep: "perform ODC call: Terminate", + OperationStepStatus: pb.OpStatus_DONE_ERROR, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + Error: err.Error(), + }) + + return err + } + + payload["odcResponse"] = &rep + payloadJson, _ = json.Marshal(payload) + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_OK, + OperationStep: "perform ODC call: Terminate", + OperationStepStatus: pb.OpStatus_DONE_OK, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + }) + + log.WithFields(logrus.Fields{ + "odcMsg": rep.Reply.Msg, + "odcStatus": rep.Reply.Status.String(), + "odcExectime": rep.Reply.Exectime, + "partition": rep.Reply.Partitionid, + "odcSessionid": rep.Reply.Sessionid, + }). + Debug("call to ODC complete: odc.Terminate") + return err +} + +func doShutdown(ctx context.Context, odcClient *RpcClient, arguments map[string]string, paddingTimeout time.Duration, envId string, call *callable.Call) error { + // SHUTDOWN + shutdownRequest := &odcpb.ShutdownRequest{ + Partitionid: envId, + } + // We ask this ODC call to complete within our own DEADLINE, minus 1 second + ctxDeadline, ok := ctx.Deadline() + if ok { + shutdownRequest.Timeout = uint32((time.Until(ctxDeadline) - paddingTimeout).Seconds()) + } + + var err error = nil + var shutdownResponse *odcpb.GeneralReply + + payload := map[string]interface{}{ + "odcRequest": &shutdownRequest, + } + payloadJson, _ := json.Marshal(payload) + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_STARTED, + OperationStep: "perform ODC call: Shutdown", + OperationStepStatus: pb.OpStatus_STARTED, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + }) + + shutdownResponse, err = odcClient.Shutdown(ctx, shutdownRequest, grpc.EmptyCallOption{}) + if err != nil { + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_ERROR, + OperationStep: "perform ODC call: Shutdown", + OperationStepStatus: pb.OpStatus_DONE_ERROR, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + Error: err.Error(), + }) + + return printGrpcError(err) + } + + if shutdownResponse == nil { + // We got a nil response with nil error, this should never happen + err = errors.New("nil response error") + + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_ERROR, + OperationStep: "perform ODC call: Shutdown", + OperationStepStatus: pb.OpStatus_DONE_ERROR, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + Error: err.Error(), + }) + + return err + } + + // We nullify rep.Hosts because the payload is too large to be included in the outgoing event + shutdownResponse.Hosts = nil + + if odcErr := shutdownResponse.GetError(); odcErr != nil { + err = fmt.Errorf("code %d from ODC: %s", odcErr.GetCode(), utils.TruncateString(odcErr.GetMsg(), ODC_ERROR_MAX_LENGTH)) + + payload["odcResponse"] = &shutdownResponse + payloadJson, _ = json.Marshal(payload) + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_ERROR, + OperationStep: "perform ODC call: Shutdown", + OperationStepStatus: pb.OpStatus_DONE_ERROR, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + Error: err.Error(), + }) + + return err + } + if replyStatus := shutdownResponse.Status; replyStatus != odcpb.ReplyStatus_SUCCESS { + err = fmt.Errorf("status %s from ODC", replyStatus.String()) + + payload["odcResponse"] = &shutdownResponse + payloadJson, _ = json.Marshal(payload) + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_ERROR, + OperationStep: "perform ODC call: Shutdown", + OperationStepStatus: pb.OpStatus_DONE_ERROR, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + Error: err.Error(), + }) + + return err + } + + payload["odcResponse"] = &shutdownResponse + payloadJson, _ = json.Marshal(payload) + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_OK, + OperationStep: "perform ODC call: Shutdown", + OperationStepStatus: pb.OpStatus_DONE_OK, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + }) + + log.WithFields(logrus.Fields{ + "odcMsg": shutdownResponse.Msg, + "odcStatus": shutdownResponse.Status.String(), + "odcExectime": shutdownResponse.Exectime, + "partition": shutdownResponse.Partitionid, + "odcSessionid": shutdownResponse.Sessionid, + }). + Debug("call to ODC complete: odc.Shutdown") + return err +} + +func handleRun(ctx context.Context, odcClient *RpcClient, isManualXml bool, arguments map[string]string, paddingTimeout time.Duration, envId string, call *callable.Call) error { + defer utils.TimeTrackFunction(time.Now(), log.WithPrefix("odcclient")) + if envId == "" { + return errors.New("cannot proceed with empty environment id") + } + + log.Trace("BEGIN handleRun") + defer log.Trace("END handleRun") + + // RUN request, includes INITIALIZE+SUBMIT+ACTIVATE + var ( + topology, script, plugin, resources, extractTopoResourcesS string + extractTopoResources bool + ) + exists := false + + topology, exists = arguments["topology"] + if isManualXml && (!exists || len(topology) == 0) { + return errors.New("empty topology received") + } + script, exists = arguments["script"] + if !isManualXml && (!exists || len(script) == 0) { + return errors.New("empty script received") + } + extractTopoResourcesS, exists = arguments["extractTopoResources"] + if exists && len(extractTopoResourcesS) > 0 { + var err error + extractTopoResources, err = strconv.ParseBool(extractTopoResourcesS) + if err != nil { + return errors.New("invalid extractTopoResources value received") + } + } + + // absence of plugin and resources is only a problem if we don't extract resources from topology + plugin, exists = arguments["plugin"] + if !extractTopoResources && (!exists || len(plugin) == 0) { + return errors.New("empty plugin received") + } + resources, exists = arguments["resources"] + if !extractTopoResources && (!exists || len(resources) == 0) { + return errors.New("empty resources received") + } + + var runRequest *odcpb.RunRequest + if extractTopoResources { + runRequest = &odcpb.RunRequest{ + Partitionid: envId, + ExtractTopoResources: extractTopoResources, + } + } else { + runRequest = &odcpb.RunRequest{ + Partitionid: envId, + Plugin: plugin, + Resources: resources, + } + } + + // We ask this ODC call to complete within our own DEADLINE, minus 1 second + ctxDeadline, ok := ctx.Deadline() + if ok { + runRequest.Timeout = uint32((time.Until(ctxDeadline) - paddingTimeout).Seconds()) + } + + if isManualXml { + runRequest.Topology = topology + } else { + runRequest.Script = script + } + + var err error = nil + var runResponse *odcpb.GeneralReply + + payload := map[string]interface{}{ + "odcRequest": &runRequest, + } + payloadJson, _ := json.Marshal(payload) + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_STARTED, + OperationStep: "perform ODC call: Run", + OperationStepStatus: pb.OpStatus_STARTED, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + }) + + runResponse, err = odcClient.Run(ctx, runRequest, grpc.EmptyCallOption{}) + if err != nil { + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_ERROR, + OperationStep: "perform ODC call: Run", + OperationStepStatus: pb.OpStatus_DONE_ERROR, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + Error: err.Error(), + }) + + return printGrpcError(err) + } + + if runResponse == nil { + errMsg := "nil response error" + + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_ERROR, + OperationStep: "perform ODC call: Run", + OperationStepStatus: pb.OpStatus_DONE_ERROR, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + Error: err.Error(), + }) + + // We got a nil response with nil error, this should never happen + return errors.New(errMsg) + } + + // We nullify runResponse.Hosts because the payload is too large to be included in the outgoing event + runResponse.Hosts = nil + + if odcErr := runResponse.GetError(); odcErr != nil { + err = fmt.Errorf("code %d from ODC: %s", odcErr.GetCode(), utils.TruncateString(odcErr.GetMsg(), ODC_ERROR_MAX_LENGTH)) + } + if replyStatus := runResponse.Status; replyStatus != odcpb.ReplyStatus_SUCCESS { + payload["odcResponse"] = &runResponse + payloadJson, _ = json.Marshal(payload) + + errMsg := "" + if err != nil { + errMsg = err.Error() + } + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_ERROR, + OperationStep: "perform ODC call: Run", + OperationStepStatus: pb.OpStatus_DONE_ERROR, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + Error: errMsg, + }) + + return fmt.Errorf("status %s from ODC with error %w", replyStatus.String(), err) + } + log.WithFields(logrus.Fields{ + "odcMsg": runResponse.Msg, + "odcStatus": runResponse.Status.String(), + "odcExectime": runResponse.Exectime, + "partition": runResponse.Partitionid, + "odcSessionid": runResponse.Sessionid, + }). + Debug("call to ODC complete: odc.Run") + + payload["odcResponse"] = &runResponse + payloadJson, _ = json.Marshal(payload) + + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_OK, + OperationStep: "perform ODC call: Run", + OperationStepStatus: pb.OpStatus_DONE_OK, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + }) + + return err +} + +func handleConfigure(ctx context.Context, odcClient *RpcClient, arguments map[string]string, paddingTimeout time.Duration, envId string, call *callable.Call) error { + defer utils.TimeTrackFunction(time.Now(), log.WithPrefix("odcclient")) + if envId == "" { + return errors.New("cannot proceed with empty environment id") + } + + var err error = nil + + // SetProperties before CONFIGURE + setPropertiesRequest := &odcpb.SetPropertiesRequest{ + Partitionid: envId, + Path: "", + Properties: make([]*odcpb.Property, len(arguments)), + } + // We ask this ODC call to complete within our own DEADLINE, minus 1 second + ctxDeadline, ok := ctx.Deadline() + if ok { + setPropertiesRequest.Timeout = uint32((time.Until(ctxDeadline) - paddingTimeout).Seconds()) + } + + // Extract relevant parameters from Arguments payload + // and build payload for SetProperty+Configure + i := 0 + for k, v := range arguments { + setPropertiesRequest.Properties[i] = &odcpb.Property{ + Key: k, + Value: v, + } + i++ + } + + log.WithField("partition", envId).Debugf("preparing call odc.SetProperties") + + payload := map[string]interface{}{ + "odcRequest": &setPropertiesRequest, + } + payloadJson, _ := json.Marshal(payload) + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_STARTED, + OperationStep: "perform ODC call: SetProperties", + OperationStepStatus: pb.OpStatus_STARTED, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + }) + + var setPropertiesResponse *odcpb.GeneralReply + setPropertiesResponse, err = odcClient.SetProperties(ctx, setPropertiesRequest, grpc.EmptyCallOption{}) + if err != nil { + log.WithField("partition", envId). + WithError(err). + Debugf("finished call odc.SetProperties with ERROR") + + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_ERROR, + OperationStep: "perform ODC call: SetProperties", + OperationStepStatus: pb.OpStatus_DONE_ERROR, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + Error: err.Error(), + }) + + return printGrpcError(err) + } + + if setPropertiesResponse == nil { + err = fmt.Errorf("nil response error") + log.WithField("partition", envId).WithError(err). + Debugf("finished call odc.SetProperties, ERROR nil response") + + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_ERROR, + OperationStep: "perform ODC call: SetProperties", + OperationStepStatus: pb.OpStatus_DONE_ERROR, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + Error: err.Error(), + }) + + // We got a nil response with nil error, this should never happen + return err + } + + // We nullify setPropertiesResponse.Hosts because the payload is too large to be included in the outgoing event + setPropertiesResponse.Hosts = nil + + if odcErr := setPropertiesResponse.GetError(); odcErr != nil { + log.WithField("partition", envId). + WithError(err). + Debugf("finished call odc.SetProperties, ERROR in response payload") + + err = fmt.Errorf("code %d from ODC: %s", odcErr.GetCode(), utils.TruncateString(odcErr.GetMsg(), ODC_ERROR_MAX_LENGTH)) + + payload["odcResponse"] = &setPropertiesResponse + payloadJson, _ = json.Marshal(payload) + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_ERROR, + OperationStep: "perform ODC call: SetProperties", + OperationStepStatus: pb.OpStatus_DONE_ERROR, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + Error: err.Error(), + }) + + return err + } + if replyStatus := setPropertiesResponse.Status; replyStatus != odcpb.ReplyStatus_SUCCESS { + log.WithField("partition", envId). + WithError(err). + Debugf("finished call odc.SetProperties, bad status in response payload") + + err = fmt.Errorf("status %s from ODC", replyStatus.String()) + + payload["odcResponse"] = &setPropertiesResponse + payloadJson, _ = json.Marshal(payload) + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_ERROR, + OperationStep: "perform ODC call: SetProperties", + OperationStepStatus: pb.OpStatus_DONE_ERROR, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + Error: err.Error(), + }) + + return err + } + + log.WithField("partition", envId).Debugf("finished call odc.SetProperties with SUCCESS") + + payload["odcResponse"] = &setPropertiesResponse + payloadJson, _ = json.Marshal(payload) + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_ONGOING, + OperationStep: "perform ODC call: SetProperties", + OperationStepStatus: pb.OpStatus_DONE_OK, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + }) + + log.WithFields(logrus.Fields{ + "odcMsg": setPropertiesResponse.Msg, + "odcStatus": setPropertiesResponse.Status.String(), + "odcExectime": setPropertiesResponse.Exectime, + "partition": setPropertiesResponse.Partitionid, + "odcSessionid": setPropertiesResponse.Sessionid, + }). + Debug("call to ODC complete: odc.SetProperties") + + // CONFIGURE + configureRequest := &odcpb.ConfigureRequest{ + Request: &odcpb.StateRequest{ + Partitionid: envId, + Path: "", + Detailed: false, + }, + } + // We ask this ODC call to complete within our own DEADLINE, minus 1 second + ctxDeadline, ok = ctx.Deadline() + if ok { + configureRequest.Request.Timeout = uint32((time.Until(ctxDeadline) - paddingTimeout).Seconds()) + } + + payload = map[string]interface{}{ + "odcRequest": &configureRequest, + } + payloadJson, _ = json.Marshal(payload) + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_ONGOING, + OperationStep: "perform ODC call: Configure", + OperationStepStatus: pb.OpStatus_STARTED, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + }) + + var configureResponse *odcpb.StateReply + configureResponse, err = odcClient.Configure(ctx, configureRequest, grpc.EmptyCallOption{}) + if err != nil { + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_ERROR, + OperationStep: "perform ODC call: Configure", + OperationStepStatus: pb.OpStatus_DONE_ERROR, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + Error: err.Error(), + }) + + return printGrpcError(err) + } + + if configureResponse == nil || configureResponse.Reply == nil { + err = fmt.Errorf("nil response error") + + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_ERROR, + OperationStep: "perform ODC call: Configure", + OperationStepStatus: pb.OpStatus_DONE_ERROR, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + Error: err.Error(), + }) + + // We got a nil response with nil error, this should never happen + return err + } + + // We nullify configureResponse.Devices and configureResponse.Reply.Hosts because the payload is too large to be included in the outgoing event + configureResponse.Devices = nil + configureResponse.Reply.Hosts = nil + + if odcErr := configureResponse.Reply.GetError(); odcErr != nil { + err = fmt.Errorf("code %d from ODC: %s", odcErr.GetCode(), utils.TruncateString(odcErr.GetMsg(), ODC_ERROR_MAX_LENGTH)) + + payload["odcResponse"] = &configureResponse + payloadJson, _ = json.Marshal(payload) + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_ERROR, + OperationStep: "perform ODC call: Configure", + OperationStepStatus: pb.OpStatus_DONE_ERROR, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + Error: err.Error(), + }) + + return err + } + if replyStatus := configureResponse.Reply.Status; replyStatus != odcpb.ReplyStatus_SUCCESS { + err = fmt.Errorf("status %s from ODC", replyStatus.String()) + + payload["odcResponse"] = &configureResponse + payloadJson, _ = json.Marshal(payload) + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_ERROR, + OperationStep: "perform ODC call: Configure", + OperationStepStatus: pb.OpStatus_DONE_ERROR, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + Error: err.Error(), + }) + + return err + } + + payload["odcResponse"] = &configureResponse + payloadJson, _ = json.Marshal(payload) + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_OK, + OperationStep: "perform ODC call: Configure", + OperationStepStatus: pb.OpStatus_DONE_OK, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + }) + + log.WithFields(logrus.Fields{ + "odcMsg": configureResponse.Reply.Msg, + "odcStatus": configureResponse.Reply.Status.String(), + "odcExectime": configureResponse.Reply.Exectime, + "partition": configureResponse.Reply.Partitionid, + "odcSessionid": configureResponse.Reply.Sessionid, + }). + Debug("call to ODC complete: odc.Configure") + return err +} + +func printGrpcError(err error) error { + grpcStatus, ok := status.FromError(err) + if ok { + log.WithFields(logrus.Fields{ + "code": grpcStatus.Code().String(), + "message": grpcStatus.Message(), + "details": grpcStatus.Details(), + "error": grpcStatus.Err().Error(), + }). + Trace("ODC call error") + err = fmt.Errorf("ODC returned %s: %s", grpcStatus.Code().String(), grpcStatus.Message()) + } else { + if err == nil { + err = errors.New("nil gRPC status") + } else { + err = fmt.Errorf("invalid gRPC status: %w", err) + } + log.WithField("error", err.Error()). + Trace("ODC call error") + } + return err +} diff --git a/core/integration/odc/odcutils/state.go b/core/integration/odc/odcutils/state.go new file mode 100644 index 000000000..8d340a6e1 --- /dev/null +++ b/core/integration/odc/odcutils/state.go @@ -0,0 +1,60 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2020 CERN and copyright holders of ALICE O². + * Author: Teo Mrnjavac + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package odcutils + +import "github.com/AliceO2Group/Control/executor/executorcmd/transitioner/fairmq" + +var ( + stateMap = map[string]string{ + "STANDBY": fairmq.IDLE, + "CONFIGURED": fairmq.READY, + "RUNNING": fairmq.RUNNING, + "ERROR": fairmq.ERROR, + "DONE": fairmq.EXITING, + } + invStateMap = func() (inv map[string]string) { // invert stateMap + inv = make(map[string]string, len(stateMap)) + for k, v := range stateMap { + inv[v] = k + } + return + }() +) + +func OdcStateForState(stateName string) string { + fmqState, ok := stateMap[stateName] + if !ok { + return "" + } + return fmqState +} + +func StateForOdcState(fmqStateName string) string { + state, ok := invStateMap[fmqStateName] + if !ok { + return "" + } + return state +} diff --git a/core/integration/odc/plugin.go b/core/integration/odc/plugin.go new file mode 100644 index 000000000..1a9a9d261 --- /dev/null +++ b/core/integration/odc/plugin.go @@ -0,0 +1,1689 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2021-2024 CERN and copyright holders of ALICE O². + * Author: Teo Mrnjavac + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +//go:generate protoc --go_out=. --go_opt=paths=source_relative --go-grpc_opt=paths=source_relative --go-grpc_out=require_unimplemented_servers=false:. protos/odc.proto + +// Package odc provides integration with the Online Device Control (ODC) system +// for managing the ALICE event processing farm during data taking and offline processing. +package odc + +import ( + "context" + "crypto/md5" + "encoding/hex" + "encoding/json" + "fmt" + "net/url" + "slices" + "strconv" + "strings" + "sync" + "time" + + "github.com/AliceO2Group/Control/apricot" + common_event "github.com/AliceO2Group/Control/common/event" + "github.com/AliceO2Group/Control/common/event/topic" + "github.com/AliceO2Group/Control/common/logger/infologger" + pb "github.com/AliceO2Group/Control/common/protos" + "github.com/AliceO2Group/Control/common/utils" + "github.com/AliceO2Group/Control/common/utils/uid" + "github.com/AliceO2Group/Control/core/environment" + "github.com/AliceO2Group/Control/core/integration" + "github.com/AliceO2Group/Control/core/integration/odc/event" + "github.com/AliceO2Group/Control/core/integration/odc/fairmq" + odc "github.com/AliceO2Group/Control/core/integration/odc/protos" + "github.com/AliceO2Group/Control/core/task/sm" + "github.com/AliceO2Group/Control/core/the" + "github.com/AliceO2Group/Control/core/workflow/callable" + "github.com/spf13/viper" + "google.golang.org/grpc" +) + +const ( + ODC_DIAL_TIMEOUT = 2 * time.Second + ODC_GENERAL_OP_TIMEOUT = 5 * time.Second + ODC_CONFIGURE_TIMEOUT = 60 * time.Second + ODC_PARTITIONINITIALIZE_TIMEOUT = 60 * time.Second + ODC_START_TIMEOUT = 15 * time.Second + ODC_STOP_TIMEOUT = 15 * time.Second + ODC_RESET_TIMEOUT = 30 * time.Second + ODC_PARTITIONTERMINATE_TIMEOUT = 30 * time.Second + ODC_PADDING_TIMEOUT = 3 * time.Second + ODC_STATUS_TIMEOUT = 3 * time.Second + ODC_POLLING_INTERVAL = 3 * time.Second + ODC_MAX_INBOUND_MESSAGE_SIZE = 32 * 1024 * 1024 // 16 MiB + TOPIC = topic.IntegratedService + topic.Separator + "odc" +) + +type Plugin struct { + odcHost string + odcPort int + + odcClient *RpcClient + + cachedStatus *OdcStatus + cachedStatusMu sync.RWMutex + cachedStatusCancelFunc context.CancelFunc +} + +type OdcStatus struct { + Partitions map[uid.ID]*OdcPartitionInfo + Status odc.ReplyStatus + Message string + Error *odc.Error +} + +type OdcDeviceId uint64 +type OdcCollectionId uint64 + +func (o OdcDeviceId) MarshalJSON() ([]byte, error) { + return json.Marshal(strconv.FormatUint(uint64(o), 10)) +} + +type OdcPartitionInfo struct { + PartitionId uid.ID `json:"-"` + RunNumber uint32 `json:"runNumber"` + State string `json:"state"` + EcsState sm.State `json:"ecsState"` + DdsSessionId string `json:"ddsSessionId"` + DdsSessionStatus string `json:"ddsSessionStatus"` + Devices map[OdcDeviceId]*OdcDevice `json:"devices"` + Hosts []string `json:"hosts"` + Collections map[OdcCollectionId]*OdcCollection `json:"collections"` +} + +type OdcDevice struct { + TaskId string `json:"taskId"` + State string `json:"state"` + EcsState sm.State `json:"ecsState"` + Path string `json:"path"` + Ignored bool `json:"ignored"` + Host string `json:"host"` + Expendable bool `json:"expendable"` + Rmsjobid string `json:"rmsjobid"` +} + +type OdcCollection struct { + CollectionId OdcCollectionId `json:"collectionId"` + State string `json:"state"` + EcsState sm.State `json:"ecsState"` + Path string `json:"path"` + Host string `json:"host"` +} + +type partitionStateChangedEventPayload struct { + PartitionId uid.ID `json:"partitionId"` + DdsSessionId string `json:"ddsSessionId"` + DdsSessionStatus string `json:"ddsSessionStatus"` + State string `json:"state"` + EcsState sm.State `json:"ecsState"` +} + +type deviceStateChangedEventPayload struct { + PartitionId uid.ID `json:"partitionId"` + DdsSessionId string `json:"ddsSessionId"` + DdsSessionStatus string `json:"ddsSessionStatus"` + State string `json:"state"` + EcsState sm.State `json:"ecsState"` + TaskId string `json:"taskId"` + Path string `json:"path"` + Ignored bool `json:"ignored"` + Host string `json:"host"` + Expendable bool `json:"expendable"` + Rmsjobid string `json:"rmsjobid"` +} + +type collectionStateChangedEventPayload struct { + PartitionId uid.ID `json:"partitionId"` + DdsSessionId string `json:"ddsSessionId"` + DdsSessionStatus string `json:"ddsSessionStatus"` + State string `json:"state"` + EcsState sm.State `json:"ecsState"` + CollectionId OdcCollectionId `json:"collectionId"` + Path string `json:"path"` + Host string `json:"host"` +} + +func NewPlugin(endpoint string) integration.Plugin { + u, err := url.Parse(endpoint) + if err != nil { + log.WithField("endpoint", endpoint). + WithError(err). + Error("bad service endpoint") + return nil + } + + portNumber, _ := strconv.Atoi(u.Port()) + + return &Plugin{ + odcHost: u.Hostname(), + odcPort: portNumber, + odcClient: nil, + } +} + +func (p *Plugin) GetName() string { + return "odc" +} + +func (p *Plugin) GetPrettyName() string { + return "ODC (EPN subcontrol)" +} + +func (p *Plugin) GetEndpoint() string { + return viper.GetString("odcEndpoint") +} + +func (p *Plugin) GetConnectionState() string { + if p == nil || p.odcClient == nil { + return "UNKNOWN" + } + return p.odcClient.conn.GetState().String() +} + +func (p *Plugin) queryPartitionStatus() { + defer utils.TimeTrackFunction(time.Now(), log.WithPrefix("odcclient")) + ctx, cancel := integration.NewContextEmptyEnvIdRunType(ODC_STATUS_TIMEOUT) + defer cancel() + + statusRep := &odc.StatusReply{} + var err error + + statusRep, err = p.odcClient.Status(ctx, &odc.StatusRequest{Running: true}, grpc.EmptyCallOption{}) + if err != nil { + log.WithField("level", infologger.IL_Support). + WithField("call", "Status"). + WithError(err).Error("ODC error") + } + if statusRep == nil { + log.WithField("level", infologger.IL_Support). + WithField("call", "Status"). + WithError(fmt.Errorf("ODC Status response is nil")).Error("ODC error") + statusRep = &odc.StatusReply{} + } + + response := &OdcStatus{ + Status: statusRep.Status, + Message: statusRep.Msg, + Error: statusRep.Error, + Partitions: make(map[uid.ID]*OdcPartitionInfo), + } + + odcPartInfoSlice := make([]*OdcPartitionInfo, len(statusRep.Partitions)) + + // concurrent request for the detailed state of each partition + var wg sync.WaitGroup + + for idx, odcPartSt := range statusRep.Partitions { + if odcPartSt == nil { + continue + } + var id uid.ID + id, err = uid.FromString(odcPartSt.Partitionid) + if err != nil { + continue + } + + wg.Add(1) + + odcPartInfoSlice[idx] = &OdcPartitionInfo{ + PartitionId: id, + RunNumber: uint32(odcPartSt.Runnr), + State: odcPartSt.State, + EcsState: fairmq.ToEcsState(odcPartSt.State, sm.UNKNOWN), + DdsSessionId: odcPartSt.Sessionid, + DdsSessionStatus: odcPartSt.Status.String(), + } + i := idx + + go func(idx int, partId uid.ID) { + defer wg.Done() + + ctx, cancel := integration.NewContextEmptyEnvIdRunType(ODC_STATUS_TIMEOUT) + defer cancel() + + odcPartStateRep, err := p.odcClient.GetState(ctx, &odc.StateRequest{ + Partitionid: partId.String(), + Detailed: true, + }, grpc.EmptyCallOption{}) + if err != nil { + log.WithField("level", infologger.IL_Support). + WithField("call", "GetState"). + WithField("partition", partId.String()). + WithError(err).Error("ODC error") + return + } + if odcPartStateRep == nil || odcPartStateRep.Reply == nil { + log.WithField("level", infologger.IL_Support). + WithField("call", "GetState"). + WithField("partition", partId.String()). + WithError(fmt.Errorf("ODC GetState response is nil")).Error("ODC error") + return + } + + odcPartInfoSlice[idx].Hosts = odcPartStateRep.Reply.Hosts + odcPartInfoSlice[idx].Devices = make(map[OdcDeviceId]*OdcDevice, len(odcPartStateRep.Devices)) + for _, device := range odcPartStateRep.Devices { + odcPartInfoSlice[idx].Devices[OdcDeviceId(device.Id)] = &OdcDevice{ + TaskId: strconv.FormatUint(device.Id, 10), + State: device.State, + EcsState: fairmq.ToEcsState(device.State, sm.UNKNOWN), + Path: device.Path, + Ignored: device.Ignored, + Host: device.Host, + Expendable: device.Expendable, + Rmsjobid: device.Rmsjobid, + } + } + odcPartInfoSlice[idx].Collections = make(map[OdcCollectionId]*OdcCollection, len(odcPartStateRep.Collections)) + for _, collection := range odcPartStateRep.Collections { + odcPartInfoSlice[idx].Collections[OdcCollectionId(collection.Id)] = &OdcCollection{ + CollectionId: OdcCollectionId(collection.Id), + State: collection.State, + EcsState: fairmq.ToEcsState(collection.State, sm.UNKNOWN), + Path: collection.Path, + Host: collection.Host, + } + } + }(i, id) + } + wg.Wait() + + for _, odcPartSt := range odcPartInfoSlice { + if odcPartSt == nil || odcPartSt.PartitionId.IsNil() { + // The partition wasn't found in the ODC response + continue + } + response.Partitions[odcPartSt.PartitionId] = odcPartSt + } + + p.cachedStatusMu.Lock() + + // state change detection + if p.cachedStatus != nil && p.cachedStatus.Status == odc.ReplyStatus_SUCCESS { + for id, partitionInfo := range response.Partitions { + // do we have the given partition on record already? if not, no state change detection is possible + if existingPartition, ok := p.cachedStatus.Partitions[id]; ok { + + // detection of device state change + event publication + for deviceId, device := range partitionInfo.Devices { + existingDevice, hasDevice := existingPartition.Devices[deviceId] + + oldEcsState := sm.UNKNOWN // we presume the task didn't exist before + + // if a device with this ID is already known to us from before + if hasDevice { + // if device state has changed + if existingDevice.State != device.State { + // if the state has changed, we take note of the previous state + oldEcsState = existingDevice.EcsState + } else { + // if the state hasn't changed, we set the old ECS state and bail + device.EcsState = existingDevice.EcsState + continue + } + } + + device.EcsState = fairmq.ToEcsState(device.State, oldEcsState) + + // since the odc-state of the task has changed, we must publish the event + payload := deviceStateChangedEventPayload{ + PartitionId: partitionInfo.PartitionId, + DdsSessionId: partitionInfo.DdsSessionId, + DdsSessionStatus: partitionInfo.DdsSessionStatus, + State: device.State, + EcsState: device.EcsState, + TaskId: device.TaskId, + Path: device.Path, + Ignored: device.Ignored, + Host: device.Host, + Expendable: device.Expendable, + Rmsjobid: device.Rmsjobid, + } + payloadJson, _ := json.Marshal(payload) + + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: "odc.deviceStateChanged", + EnvironmentId: id.String(), + Payload: string(payloadJson[:]), + }) + } + + // detection of collection state change + event publication + for collectionId, collection := range partitionInfo.Collections { + existingCollection, hasCollection := existingPartition.Collections[collectionId] + + oldEcsState := sm.UNKNOWN // we presume the collection didn't exist before + + // if a collection with this ID is already known to us from before + if hasCollection { + // if collection state has changed + if existingCollection.State != collection.State { + // if the state has changed, we take note of the previous state + oldEcsState = existingCollection.EcsState + } else { + // if the state hasn't changed, we set the old ECS state and bail + collection.EcsState = existingCollection.EcsState + continue + } + } + + collection.EcsState = fairmq.ToEcsState(collection.State, oldEcsState) + + payload := collectionStateChangedEventPayload{ + PartitionId: partitionInfo.PartitionId, + DdsSessionId: partitionInfo.DdsSessionId, + DdsSessionStatus: partitionInfo.DdsSessionStatus, + State: collection.State, + EcsState: collection.EcsState, + CollectionId: collection.CollectionId, + Path: collection.Path, + Host: collection.Host, + } + payloadJson, _ := json.Marshal(payload) + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: "odc.collectionStateChanged", + EnvironmentId: id.String(), + Payload: string(payloadJson[:]), + }) + } + + // detection of env (ODC partition) state change + event publication + if existingPartition.State != partitionInfo.State { + partitionInfo.EcsState = fairmq.ToEcsState(partitionInfo.State, existingPartition.EcsState) + + log.WithField("level", infologger.IL_Support). + WithField("partition", id.String()). + WithField("oldState", existingPartition.State). + WithField("oldEcsState", existingPartition.EcsState). + WithField("ecsState", partitionInfo.EcsState). + WithField("state", partitionInfo.State). + Info("ODC Partition state changed") + + payload := partitionStateChangedEventPayload{ + PartitionId: partitionInfo.PartitionId, + DdsSessionId: partitionInfo.DdsSessionId, + DdsSessionStatus: partitionInfo.DdsSessionStatus, + State: partitionInfo.State, + EcsState: partitionInfo.EcsState, + } + + payloadJson, _ := json.Marshal(payload) + + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: "odc.partitionStateChanged", + EnvironmentId: id.String(), + Payload: string(payloadJson[:]), + }) + + envMan := environment.ManagerInstance() + if envMan != nil { + go envMan.NotifyIntegratedServiceEvent(&event.OdcPartitionStateChangeEvent{ + IntegratedServiceEventBase: common_event.IntegratedServiceEventBase{ServiceName: "ODC"}, + EnvironmentId: id, + State: partitionInfo.State, + EcsState: partitionInfo.EcsState.String(), + }) + } else { + log.WithField("level", infologger.IL_Support). + WithField("partition", id.String()). + WithField("oldState", existingPartition.State). + WithField("state", partitionInfo.State). + Warn("could not notify environment manager of ODC partition state change event") + } + } else { + partitionInfo.EcsState = existingPartition.EcsState + } + } + } + } + + p.cachedStatus = response + p.cachedStatusMu.Unlock() +} + +func (p *Plugin) GetData(_ []any) string { + if p == nil || p.odcClient == nil { + return "" + } + + p.cachedStatusMu.RLock() + r := p.cachedStatus + if r == nil { + p.cachedStatusMu.RUnlock() + return "" + } + + partitionStates := make(map[string]map[string]string) + + if r.Status == odc.ReplyStatus_SUCCESS { + for id, partitionInfo := range r.Partitions { + partitionStates[id.String()] = map[string]string{ + "state": partitionInfo.State, + "ecsState": partitionInfo.EcsState.String(), + } + } + } + p.cachedStatusMu.RUnlock() + + out, err := json.Marshal(partitionStates) + if err != nil { + return "" + } + return string(out[:]) +} + +func (p *Plugin) GetEnvironmentsData(envIds []uid.ID) map[uid.ID]string { + if p == nil || p.odcClient == nil { + return nil + } + + p.cachedStatusMu.RLock() + defer p.cachedStatusMu.RUnlock() + + if p.cachedStatus == nil { + return nil + } + + out := make(map[uid.ID]string) + for _, id := range envIds { + partitionInfo, ok := p.cachedStatus.Partitions[id] + if !ok { + continue + } + + partitionInfoOut, err := json.Marshal(partitionInfo) + if err != nil { + continue + } + out[id] = string(partitionInfoOut[:]) + } + + return out +} + +func (p *Plugin) GetEnvironmentsShortData(envIds []uid.ID) map[uid.ID]string { + if p == nil || p.odcClient == nil { + return nil + } + + p.cachedStatusMu.RLock() + defer p.cachedStatusMu.RUnlock() + + if p.cachedStatus == nil { + return nil + } + + out := make(map[uid.ID]string) + for _, id := range envIds { + partitionInfo, ok := p.cachedStatus.Partitions[id] + if !ok { + continue + } + + // return everything except the devices + partitionInfoPayload := &OdcPartitionInfo{ + PartitionId: partitionInfo.PartitionId, + RunNumber: partitionInfo.RunNumber, + State: partitionInfo.State, + EcsState: partitionInfo.EcsState, + DdsSessionId: partitionInfo.DdsSessionId, + DdsSessionStatus: partitionInfo.DdsSessionStatus, + Hosts: partitionInfo.Hosts, + } + + partitionInfoOut, err := json.Marshal(partitionInfoPayload) + if err != nil { + continue + } + out[id] = string(partitionInfoOut[:]) + } + + return out +} + +func getFlpIdList(varStack map[string]string) (flps []string, err error) { + payload, ok := varStack["hosts"] + if !ok { + return []string{}, fmt.Errorf("could not retrieve FLP list (\"hosts\") from varStack") + } + + flpHostnames := make([]string, 0) + err = json.Unmarshal([]byte(payload), &flpHostnames) + if err != nil { + return []string{}, err + } + + const prodFlpPrefix = "alio2-cr1-flp" + const stagingFlpPrefix = "alio2-cr1-mvs" + + // we take only prod and staging into account + // we add S to staging IDs as requested in OCTRL-753 + flpIds := make([]string, 0) + for _, flp := range flpHostnames { + if strings.HasPrefix(flp, prodFlpPrefix) { + id := strings.TrimPrefix(flp, prodFlpPrefix) + if len(id) > 0 { + flpIds = append(flpIds, id) + } + } else if strings.HasPrefix(flp, stagingFlpPrefix) { + id := strings.TrimPrefix(flp, stagingFlpPrefix) + if len(id) > 0 { + flpIds = append(flpIds, "S"+id) + } + } + } + return flpIds, nil +} + +func (p *Plugin) Init(_ string) error { + if p.odcClient == nil { + cxt, cancel := context.WithCancel(context.Background()) + p.odcClient = NewClient(cxt, cancel, viper.GetString("odcEndpoint")) + if p.odcClient == nil { + return fmt.Errorf("failed to connect to ODC service on %s", viper.GetString("ddSchedulerEndpoint")) + } + log.Debug("ODC plugin initialized") + } + + var ctx context.Context + ctx, p.cachedStatusCancelFunc = context.WithCancel(context.Background()) + + odcPollingIntervalStr := viper.GetString("odcPollingInterval") + odcPollingInterval, err := time.ParseDuration(odcPollingIntervalStr) + if err != nil { + odcPollingInterval = ODC_POLLING_INTERVAL + log.Debugf("ODC plugin cannot acquire polling interval, defaulting to %s", ODC_POLLING_INTERVAL.String()) + } + + // polling + go func() { + for { + select { + case <-ctx.Done(): + return + case <-time.After(odcPollingInterval): + p.queryPartitionStatus() + } + } + }() + return nil +} + +func (p *Plugin) ObjectStack(varStack map[string]string, baseConfigStack map[string]string) (stack map[string]interface{}) { + // baseConfigStack is this environment's defaults + vars from Consul but no user input + // It is passed around this way because it cannot be acquired from envMan.GetEnv(id).BaseConfigStack, since during + // this ObjectStack call the env isn't mapped in envMan yet (we're in the middle of creating + processing it with + // ProcessTemplates in loadWorkflow). + // The only reason we need the naked defaults + vars is for the non-standard processing of the "default" keyword in + // the ODC plugin, so this is a break from design. + envId, envIdOk := varStack["environment_id"] + if !envIdOk { + log.Error("ObjectStack cannot acquire environment ID") + return + } + + if baseConfigStack == nil { + baseConfigStack = make(map[string]string) + } + + stack = make(map[string]interface{}) + stack["GenerateEPNWorkflowScript"] = func() (out string) { + /* + OCTRL-558 example: + GEN_TOPO_HASH=[0/1] GEN_TOPO_SOURCE=[...] DDMODE=[TfBuilder Mode] GEN_TOPO_LIBRARY_FILE=[...] + GEN_TOPO_WORKFLOW_NAME=[...] WORKFLOW_DETECTORS=[...] WORKFLOW_DETECTORS_QC=[...] + WORKFLOW_DETECTORS_CALIB=[...] WORKFLOW_PARAMETERS=[...] RECO_NUM_NODES_OVERRIDE=[...] + MULTIPLICITY_FACTOR_RAWDECODERS=[...] MULTIPLICITY_FACTOR_CTFENCODERS=[...] + MULTIPLICITY_FACTOR_REST=[...] GEN_TOPO_WIPE_CACHE=[0/1] BEAMTYPE=[PbPb/pp/pPb/cosmic/technical] + NHBPERTF=[...] GEN_TOPO_ONTHEFLY=1 [Extra environment variables] + /home/epn/pdp/gen_topo.sh + + R3C-710: + `pdp_o2pdpsuite_version` is a new field. Its content should be sent in the string as `OVERRIDE_PDPSUITE_VERSION=[...]`. + In case it is set to `default`, instead of the string `default` the preconfigured default version in consul should be sent. + `pdp_qcjson_version`: similar to avove, new field. please send as `SET_QCJSON_VERSION`. + If set to the string `default`, please sent the default version configured in consul instead. + `pdp_o2_data_processing_hash`: if set to the string `default`, sent the default hash configured in consul instead. + `odc_n_epns_max_fail` : new field. Please send as `RECO_MAX_FAIL_NODES_OVERRIDE=[...]`. + `epn_store_raw_data_fraction` new field, please send as `DD_DISK_FRACTION=[...]`. + `pdp_nr_compute_nodes` removed this field since no longer needed. + Please send the value of `odc_n_epns` directly as `RECO_NUM_NODES_OVERRIDE=[...]`. + `pdp_epn_shmid`: new field, please send as `SHM_MANAGER_SHMID=[...]` + `pdp_epn_shm_recreate`: new field, please send as `SHM_MANAGER_SHM_RECREATE=[0|1]` + */ + + var ( + pdpConfigOption, o2DPSource, tfbDDMode string + pdpLibraryFile, pdpLibWorkflowName string + pdpDetectorList, pdpDetectorExcludeListQc, pdpDetectorExcludeListCalib string + pdpWorkflowParams string + pdpRawDecoderMultiFactor, pdpCtfEncoderMultiFactor, pdpRecoProcessMultiFactor string + pdpWipeWorkflowCache, pdpBeamType, pdpNHbfPerTf string + pdpExtraEnvVars, pdpEpnShmSizes, pdpGeneratorScriptPath string + odcNEpns string + ok bool + accumulator []string + pdpO2PdpSuiteVersion, pdpQcJsonVersion string + odcNEpnsMaxFail, epnStoreRawDataFraction string + pdpEpnShmId string + runType string + flpIds []string + deploymentType string + ) + accumulator = make([]string, 0) + + configStack := baseConfigStack + + pdpConfigOption, ok = varStack["pdp_config_option"] + if !ok { + log.WithField("partition", envId). + WithField("call", "GenerateEPNWorkflowScript"). + Error("cannot acquire PDP workflow configuration mode") + return + } + + switch pdpConfigOption { + case "Repository hash": + o2DPSource, ok = varStack["pdp_o2_data_processing_hash"] + if !ok { + log.WithField("partition", envId). + WithField("call", "GenerateEPNWorkflowScript"). + Error("cannot acquire PDP Repository hash") + return + } + if strings.TrimSpace(o2DPSource) == "default" { // if UI sends 'default', we look in Consul + o2DPSource, ok = configStack["pdp_o2_data_processing_hash"] + if !ok { + log.WithField("partition", envId). + WithField("call", "GenerateEPNWorkflowScript"). + Error("cannot acquire PDP Repository hash default") + return + } + } + accumulator = append(accumulator, "GEN_TOPO_HASH=1") + + case "Repository path": + o2DPSource, ok = varStack["pdp_o2_data_processing_path"] + if !ok { + log.WithField("partition", envId). + WithField("call", "GenerateEPNWorkflowScript"). + Error("cannot acquire PDP Repository path") + return + } + accumulator = append(accumulator, "GEN_TOPO_HASH=0") + + case "Manual XML": + fallthrough + default: + return + } + accumulator = append(accumulator, fmt.Sprintf("GEN_TOPO_SOURCE='%s'", strings.TrimSpace(o2DPSource))) + + tfbDDMode, ok = varStack["tfb_dd_mode"] + if !ok { + log.WithField("partition", envId). + WithField("call", "GenerateEPNWorkflowScript"). + Error("cannot acquire TF Builder mode") + return + } + accumulator = append(accumulator, fmt.Sprintf("DDMODE='%s'", strings.TrimSpace(tfbDDMode))) + + pdpLibraryFile, ok = varStack["pdp_topology_description_library_file"] + if !ok { + log.WithField("partition", envId). + WithField("call", "GenerateEPNWorkflowScript"). + Error("cannot acquire topology description library file") + return + } + accumulator = append(accumulator, fmt.Sprintf("GEN_TOPO_LIBRARY_FILE='%s'", strings.TrimSpace(pdpLibraryFile))) + + pdpLibWorkflowName, ok = varStack["pdp_workflow_name"] + if !ok { + log.WithField("partition", envId). + WithField("call", "GenerateEPNWorkflowScript"). + Error("cannot acquire PDP workflow name in topology library file") + return + } + accumulator = append(accumulator, fmt.Sprintf("GEN_TOPO_WORKFLOW_NAME='%s'", strings.TrimSpace(pdpLibWorkflowName))) + + pdpDetectorList, ok = varStack["pdp_detector_list_global"] + if !ok { + log.WithField("partition", envId). + WithField("call", "GenerateEPNWorkflowScript"). + Error("cannot acquire PDP workflow name in topology library file") + return + } + if strings.TrimSpace(pdpDetectorList) == "default" { + pdpDetectorList, ok = varStack["detectors"] + if !ok { + log.WithField("partition", envId). + WithField("call", "GenerateEPNWorkflowScript"). + Error("cannot acquire general detector list from varStack") + return + } + detectorsSlice, err := p.parseDetectors(pdpDetectorList) + if err != nil { + log.WithField("partition", envId). + WithField("detectorList", pdpDetectorList). + WithField("call", "GenerateEPNWorkflowScript"). + Error("cannot parse general detector list") + return + } + + // Special case: if the detector list is "default" and ctp_readout_enabled==true, we include TRG + ctpReadoutEnabled := "false" + ctpReadoutEnabled, ok = varStack["ctp_readout_enabled"] + if ok && strings.ToLower(strings.TrimSpace(ctpReadoutEnabled)) == "true" { + detectorsSlice = append(detectorsSlice, "TRG") + } + slices.Sort(detectorsSlice) + pdpDetectorList = strings.Join(detectorsSlice, ",") + } + accumulator = append(accumulator, fmt.Sprintf("WORKFLOW_DETECTORS='%s'", strings.TrimSpace(pdpDetectorList))) + + pdpDetectorExcludeListQc, ok = varStack["pdp_detector_exclude_list_qc"] + if !ok { + log.WithField("partition", envId). + WithField("call", "GenerateEPNWorkflowScript"). + Error("cannot acquire QC detector exclude list in topology library file") + return + } + accumulator = append(accumulator, fmt.Sprintf("WORKFLOW_DETECTORS_EXCLUDE_QC='%s'", strings.TrimSpace(pdpDetectorExcludeListQc))) + + pdpDetectorExcludeListCalib, ok = varStack["pdp_detector_exclude_list_calib"] + if !ok { + log.WithField("partition", envId). + WithField("call", "GenerateEPNWorkflowScript"). + Error("cannot acquire calibration detector exclude list in topology library file") + return + } + accumulator = append(accumulator, fmt.Sprintf("WORKFLOW_DETECTORS_EXCLUDE_CALIB='%s'", strings.TrimSpace(pdpDetectorExcludeListCalib))) + + pdpWorkflowParams, ok = varStack["pdp_workflow_parameters"] + if !ok { + log.WithField("partition", envId). + WithField("call", "GenerateEPNWorkflowScript"). + Error("cannot acquire PDP workflow parameters") + return + } + accumulator = append(accumulator, fmt.Sprintf("WORKFLOW_PARAMETERS='%s'", strings.TrimSpace(pdpWorkflowParams))) + + odcNEpns, ok = varStack["odc_n_epns"] + if !ok { + log.WithField("partition", envId). + WithField("call", "GenerateEPNWorkflowScript"). + Error("cannot acquire ODC number of EPNs") + return + } + odcNEpnsI, err := strconv.Atoi(odcNEpns) + if err != nil { + log.WithField("partition", envId). + WithField("call", "GenerateEPNWorkflowScript"). + Error("cannot parse ODC number of EPNs") + return + } + accumulator = append(accumulator, fmt.Sprintf("RECO_NUM_NODES_OVERRIDE=%d", odcNEpnsI)) + + odcNEpnsMaxFail, ok = varStack["odc_n_epns_max_fail"] + if !ok { + log.WithField("partition", envId). + WithField("call", "GenerateEPNWorkflowScript"). + Error("cannot acquire ODC number of EPNs max fail") + return + } + odcNEpnsMaxFailI, err := strconv.Atoi(odcNEpnsMaxFail) + if err != nil { + log.WithField("partition", envId). + WithField("call", "GenerateEPNWorkflowScript"). + Error("cannot parse ODC number of EPNs max fail") + return + } + accumulator = append(accumulator, fmt.Sprintf("RECO_MAX_FAIL_NODES_OVERRIDE=%d", odcNEpnsMaxFailI)) + + pdpRawDecoderMultiFactor, ok = varStack["pdp_raw_decoder_multi_factor"] + if !ok { + log.WithField("partition", envId). + WithField("call", "GenerateEPNWorkflowScript"). + Error("cannot acquire PDP number of raw decoder processing instances") + return + } + accumulator = append(accumulator, fmt.Sprintf("MULTIPLICITY_FACTOR_RAWDECODERS=%s", strings.TrimSpace(pdpRawDecoderMultiFactor))) + + pdpCtfEncoderMultiFactor, ok = varStack["pdp_ctf_encoder_multi_factor"] + if !ok { + log.WithField("partition", envId). + WithField("call", "GenerateEPNWorkflowScript"). + Error("cannot acquire PDP number of CTF encoder processing instances") + return + } + accumulator = append(accumulator, fmt.Sprintf("MULTIPLICITY_FACTOR_CTFENCODERS=%s", strings.TrimSpace(pdpCtfEncoderMultiFactor))) + + pdpRecoProcessMultiFactor, ok = varStack["pdp_reco_process_multi_factor"] + if !ok { + log.WithField("partition", envId). + WithField("call", "GenerateEPNWorkflowScript"). + Error("cannot acquire PDP number of other reconstruction processing instances") + return + } + accumulator = append(accumulator, fmt.Sprintf("MULTIPLICITY_FACTOR_REST=%s", strings.TrimSpace(pdpRecoProcessMultiFactor))) + + pdpBeamType, ok = varStack["pdp_beam_type"] + if !ok { + log.WithField("partition", envId). + WithField("call", "GenerateEPNWorkflowScript"). + Error("cannot acquire beam type") + return + } + accumulator = append(accumulator, fmt.Sprintf("BEAMTYPE='%s'", strings.TrimSpace(pdpBeamType))) + + pdpNHbfPerTf, ok = varStack["pdp_n_hbf_per_tf"] + if !ok { + log.WithField("partition", envId). + WithField("call", "GenerateEPNWorkflowScript"). + Error("cannot acquire number of HBFs per TF") + return + } + accumulator = append(accumulator, fmt.Sprintf("NHBPERTF=%s", strings.TrimSpace(pdpNHbfPerTf))) + + accumulator = append(accumulator, "GEN_TOPO_ONTHEFLY=1") + + pdpO2PdpSuiteVersion, ok = varStack["pdp_o2pdpsuite_version"] + if !ok { + log.WithField("partition", envId). + WithField("call", "GenerateEPNWorkflowScript"). + Error("cannot acquire PDP Suite version") + return + } + if strings.TrimSpace(pdpO2PdpSuiteVersion) == "default" { // if UI sends 'default', we look in Consul + pdpO2PdpSuiteVersion, ok = configStack["pdp_o2pdpsuite_version"] + if !ok { + log.WithField("partition", envId). + WithField("call", "GenerateEPNWorkflowScript"). + Error("cannot acquire PDP Suite version default") + return + } + } + accumulator = append(accumulator, fmt.Sprintf("OVERRIDE_PDPSUITE_VERSION='%s'", pdpO2PdpSuiteVersion)) + + // SET_QCJSON_VERSION does not come from user input or vars any more, it's instead a direct query to QC runtime + pdpQcJsonVersion, err = apricot.Instance().GetRuntimeEntry("qc", "config_hash") + if err != nil { + log.WithField("partition", envId). + WithError(err). + WithField("call", "GenerateEPNWorkflowScript"). + Error("cannot acquire PDP QCJson config_hash from QC runtime KV") + return + } + + accumulator = append(accumulator, fmt.Sprintf("SET_QCJSON_VERSION='%s'", pdpQcJsonVersion)) + + epnStoreRawDataFraction, ok = varStack["epn_store_raw_data_fraction"] + if !ok { + log.WithField("partition", envId). + WithField("call", "GenerateEPNWorkflowScript"). + Error("cannot acquire EPN DD disk raw data fraction") + return + } + accumulator = append(accumulator, fmt.Sprintf("DD_DISK_FRACTION='%s'", epnStoreRawDataFraction)) + + pdpEpnShmId, ok = varStack["pdp_epn_shmid"] + if !ok { + log.WithField("partition", envId). + WithField("call", "GenerateEPNWorkflowScript"). + Error("cannot acquire PDP EPN SHMID") + return + } + accumulator = append(accumulator, fmt.Sprintf("SHM_MANAGER_SHMID='%s'", pdpEpnShmId)) + + runType, ok = varStack["run_type"] + if !ok { + log.WithField("partition", envId). + WithField("call", "GenerateEPNWorkflowScript"). + Warn("could not get variable run_type from environment context, using NONE") + runType = "NONE" + } + accumulator = append(accumulator, fmt.Sprintf("RUNTYPE=%s", strings.TrimSpace(runType))) + + flpIds, err = getFlpIdList(varStack) + if err != nil { + log.WithField("partition", envId). + WithField("call", "GenerateEPNWorkflowScript"). + WithError(err). + Error("cannot acquire FLP ID list, it will be empty") + } + accumulator = append(accumulator, fmt.Sprintf("FLP_IDS='%s'", strings.Join(flpIds, ","))) + + deploymentType, ok = varStack["setup_name"] + if !ok { + log.WithField("partition", envId). + WithField("call", "GenerateEPNWorkflowScript"). + Error("cannot acquire setup_name, needed for GEN_TOPO_DEPLOYMENT_TYPE, using UNKNOWN") + deploymentType = "UNKNOWN" + } + // we replace spaces with underscores, since David requested no spaces in OCTRL-751 + deploymentType = strings.ReplaceAll(strings.TrimSpace(deploymentType), " ", "_") + accumulator = append(accumulator, fmt.Sprintf("GEN_TOPO_DEPLOYMENT_TYPE=%s", deploymentType)) + + pdpExtraEnvVars, ok = varStack["pdp_extra_env_vars"] + if !ok { + log.WithField("partition", envId). + WithField("call", "GenerateEPNWorkflowScript"). + Error("cannot acquire PDP extra environment variables") + return + } + accumulator = append(accumulator, strings.TrimSpace(pdpExtraEnvVars)) + + pdpEpnShmSizes, ok = varStack["pdp_epn_shm_sizes"] + if !ok { + log.WithField("partition", envId). + WithField("call", "GenerateEPNWorkflowScript"). + Error("cannot acquire PDP EPN SHM sizes") + return + } + accumulator = append(accumulator, strings.TrimSpace(pdpEpnShmSizes)) + + pdpGeneratorScriptPath, ok = varStack["pdp_generator_script_path"] + if !ok { + log.WithField("partition", envId). + WithField("call", "GenerateEPNWorkflowScript"). + Error("cannot acquire PDP generator script path") + return + } + accumulator = append(accumulator, strings.TrimSpace(pdpGeneratorScriptPath)) + + out = strings.Join(accumulator, " ") + + // before we ship out the payload, we take the hash of the full string and prepend a few last variables with the + // hash of everything else that follows, except ECS_ENVIRONMENT_ID and GEN_TOPO_WIPE_CACHE, the only + // variables that must stay unhashed + // see https://alice.its.cern.ch/jira/browse/OCTRL-736 + hash := md5.Sum([]byte(out)) + hashS := hex.EncodeToString(hash[:]) + out = fmt.Sprintf("GEN_TOPO_CACHE_HASH=%s", hashS) + " " + out + + pdpWipeWorkflowCache, ok = varStack["pdp_wipe_workflow_cache"] + if !ok { + log.WithField("partition", envId). + WithField("call", "GenerateEPNWorkflowScript"). + Warn("cannot acquire PDP workflow cache wipe option, assuming false") + pdpWipeWorkflowCache = "false" + } + pdpWipeWorkflowCacheB, err := strconv.ParseBool(pdpWipeWorkflowCache) + if err != nil { + log.WithField("partition", envId). + WithField("call", "GenerateEPNWorkflowScript"). + Error("cannot parse PDP workflow cache wipe option") + pdpWipeWorkflowCacheB = false + } + pdpWipeWorkflowCacheI := 0 + if pdpWipeWorkflowCacheB { + pdpWipeWorkflowCacheI = 1 + } + out = fmt.Sprintf("GEN_TOPO_WIPE_CACHE=%d", pdpWipeWorkflowCacheI) + " " + out + + // finally we prepend ECS_ENVIRONMENT_ID + out = fmt.Sprintf("ECS_ENVIRONMENT_ID=%s", envId) + " " + out + + return + } + stack["GenerateEPNTopologyFullname"] = func() (out string) { + pdpConfigOption, ok := varStack["pdp_config_option"] + if !ok { + log.WithField("partition", envId). + WithField("call", "GenerateEPNTopologyFullname"). + Error("cannot acquire PDP workflow configuration mode") + return + } + + pdpLibraryFile, ok := varStack["pdp_topology_description_library_file"] + if !ok { + log.WithField("partition", envId). + WithField("call", "GenerateEPNTopologyFullname"). + Error("cannot acquire topology description library file") + return + } + + pdpLibWorkflowName, ok := varStack["pdp_workflow_name"] + if !ok { + log.WithField("partition", envId). + WithField("call", "GenerateEPNTopologyFullname"). + Error("cannot acquire PDP workflow name in topology library file") + return + } + + odcTopologyFullname := "" + switch pdpConfigOption { + case "Repository hash": + o2DPSource, ok := varStack["pdp_o2_data_processing_hash"] + if !ok { + log.WithField("partition", envId). + WithField("call", "GenerateEPNTopologyFullname"). + Error("cannot acquire PDP Repository hash") + return + } + odcTopologyFullname = "(hash, " + o2DPSource + ", " + pdpLibraryFile + ", " + pdpLibWorkflowName + ")" + + case "Repository path": + o2DPSource, ok := varStack["pdp_o2_data_processing_path"] + if !ok { + log.WithField("partition", envId). + WithField("call", "GenerateEPNTopologyFullname"). + Error("cannot acquire PDP Repository hash") + return + } + odcTopologyFullname = "(path, " + o2DPSource + ", " + pdpLibraryFile + ", " + pdpLibWorkflowName + ")" + + case "Manual XML": + odc_topology, ok := varStack["odc_topology"] + if !ok { + log.WithField("partition", envId). + WithField("call", "GenerateEPNTopologyFullname"). + Error("cannot acquire ODC topology variable") + return + } + odcTopologyFullname = "(xml, " + odc_topology + ")" + } + return odcTopologyFullname + } + return stack +} + +func (p *Plugin) CallStack(data interface{}) (stack map[string]interface{}) { + call, ok := data.(*callable.Call) + if !ok { + return + } + varStack := call.VarStack + envId, ok := varStack["environment_id"] + if !ok { + log.Error("CallStack cannot acquire environment ID") + return + } + + paddingTimeout := ODC_PADDING_TIMEOUT + paddingTimeoutStr, ok := varStack["odc_padding_timeout"] + if ok { + var err error + paddingTimeout, err = time.ParseDuration(paddingTimeoutStr) + if err != nil { + paddingTimeout = ODC_PADDING_TIMEOUT + log.Debugf("CallStack cannot acquire ODC padding timeout, defaulting to %s", ODC_PADDING_TIMEOUT.String()) + } + } else { + log.Debugf("CallStack cannot acquire ODC padding timeout, defaulting to %s", ODC_PADDING_TIMEOUT.String()) + } + + stack = make(map[string]interface{}) + stack["PartitionInitialize"] = func() (out string) { + // ODC Run + var err error = nil + + log.WithField("partition", envId).Debugf("preparing call odc.PartitionInitialize") + + var ( + pdpConfigOption, script, topology, plugin, resources string + extractTopoResources bool + ) + ok := false + isManualXml := false + callFailedStr := "EPN PartitionInitialize call failed" + + extractTopoResourcesS, extractTopoResourcesSok := varStack["odc_extract_topology_resources"] + if extractTopoResourcesSok && extractTopoResourcesS != "" { // if set and true, plugin and resources are not to be included in requests + extractTopoResources, err = strconv.ParseBool(extractTopoResourcesS) + if err != nil { + msg := "cannot parse odc_extract_topology_resources" + log.WithField("partition", envId). + WithField("call", "PartitionInitialize"). + Error(msg) + call.VarStack["__call_error_reason"] = msg + call.VarStack["__call_error"] = callFailedStr + return + } + } + + pdpConfigOption, ok = varStack["pdp_config_option"] + if !ok { + msg := "cannot acquire PDP workflow configuration mode" + log.WithField("partition", envId). + WithField("call", "PartitionInitialize"). + Error(msg) + call.VarStack["__call_error_reason"] = msg + call.VarStack["__call_error"] = callFailedStr + return + } + switch pdpConfigOption { + case "Repository hash": + fallthrough + case "Repository path": + script, ok = varStack["odc_script"] + if !ok { + msg := "cannot acquire ODC script, make sure GenerateEPNWorkflowScript is called and its " + + "output is written to odc_script" + log.WithField("partition", envId). + WithField("call", "PartitionInitialize"). + Error(msg) + call.VarStack["__call_error_reason"] = msg + call.VarStack["__call_error"] = callFailedStr + return + } + + case "Manual XML": + topology, ok = varStack["odc_topology"] + if !ok { + msg := "cannot acquire ODC topology" + log.WithField("partition", envId). + WithField("call", "PartitionInitialize"). + Error(msg) + call.VarStack["__call_error_reason"] = msg + call.VarStack["__call_error"] = callFailedStr + return + } + isManualXml = true + + default: + msg := "cannot acquire valid PDP workflow configuration mode value" + log.WithField("partition", envId). + WithField("call", "PartitionInitialize"). + WithField("value", pdpConfigOption). + Error(msg) + call.VarStack["__call_error_reason"] = msg + call.VarStack["__call_error"] = callFailedStr + return + } + + if !extractTopoResources { + plugin, ok = varStack["odc_plugin"] + if !ok { + msg := "cannot acquire ODC RMS plugin declaration" + log.WithField("partition", envId). + WithField("call", "PartitionInitialize"). + Error(msg) + call.VarStack["__call_error_reason"] = msg + call.VarStack["__call_error"] = callFailedStr + return + } + + resources, ok = varStack["odc_resources"] + if !ok { + msg := "cannot acquire ODC resources declaration" + log.WithField("partition", envId). + WithField("call", "PartitionInitialize"). + Error(msg) + call.VarStack["__call_error_reason"] = msg + call.VarStack["__call_error"] = callFailedStr + return + } + } else { + log.WithField("partition", envId). + WithField("call", "PartitionInitialize"). + WithField(infologger.Level, infologger.IL_Support). + Info("odc_extract_topology_resources is set to true, plugin and resources will not be included in the ODC Run request") + } + + // right before making the call, we try to write it to the env's KV + parentRole, ok := call.GetParentRole().(callable.ParentRole) + if ok { + parentRole.SetGlobalRuntimeVar("__odc_partitioninitialize_called", "true") + } + + timeout := callable.AcquireTimeout(ODC_PARTITIONINITIALIZE_TIMEOUT, varStack, "PartitionInitialize", envId) + + ctx, cancel := integration.NewContext(envId, varStack, timeout) + defer cancel() + + err = handleRun(ctx, p.odcClient, isManualXml, map[string]string{ + "topology": topology, + "script": script, + "plugin": plugin, + "resources": resources, + "extractTopoResources": strconv.FormatBool(extractTopoResources), + }, + paddingTimeout, envId, call) + if err != nil { + log.WithError(err). + WithField("level", infologger.IL_Support). + WithField("partition", envId). + WithField("call", "PartitionInitialize"). + Error("ODC error") + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + return + } + log.WithField("partition", envId).Debugf("finished call odc.PartitionInitialize with SUCCESS") + return + } + stack["Configure"] = func() (out string) { + // ODC SetProperties + Configure + + callFailedStr := "EPN Configure call failed" + + timeout := callable.AcquireTimeout(ODC_CONFIGURE_TIMEOUT, varStack, "Configure", envId) + + arguments := make(map[string]string) + arguments["environment_id"] = envId + + runType, ok := varStack["run_type"] + if ok { + arguments["run_type"] = runType + } else { + log.WithField("partition", envId). + WithField("call", "Configure"). + Warn("could not get get variable run_type from environment context") + } + + lhcPeriod, ok := varStack["lhc_period"] + if ok { + arguments["lhc_period"] = lhcPeriod + } else { + log.WithField("partition", envId). + WithField("call", "Configure"). + Warn("could not get get variable lhc_period from environment context") + } + + forceRunAsRaw, ok := varStack["pdp_force_run_as_raw"] + if ok { + arguments["force_run_as_raw"] = forceRunAsRaw + } else { + log.WithField("partition", envId). + WithField("call", "Configure"). + Warn("could not get get variable force_run_as_raw from environment context") + } + + pdpBeamType, ok := varStack["pdp_beam_type"] + if ok { + arguments["pdp_beam_type"] = pdpBeamType + } else { + log.WithField("partition", envId). + WithField("call", "Configure"). + Warn("could not get get variable pdp_beam_type from environment context") + } + + detectorListS, ok := varStack["detectors"] + if ok { + detectorsSlice, err := p.parseDetectors(detectorListS) + if err == nil { + slices.Sort(detectorsSlice) + arguments["detectors"] = strings.Join(detectorsSlice, ",") + } else { + log.WithField("partition", envId). + WithField("detectorList", detectorsSlice). + WithField("call", "Configure"). + Warn("cannot parse general detector list") + } + } else { + log.WithField("partition", envId). + WithField("call", "Configure"). + Warn("cannot acquire general detector list from varStack") + } + + // Push orbit-reset-time if pdp_override_run_start_time set + pdpOverrideRunStartTime, ok := varStack["pdp_override_run_start_time"] + if ok && len(pdpOverrideRunStartTime) > 0 { + arguments["orbit-reset-time"] = pdpOverrideRunStartTime + if strings.Contains(runType, "SYNTHETIC") { + log.WithField("partition", envId). + WithField("call", "Configure"). + WithField("runType", runType). + WithField(infologger.Level, infologger.IL_Support). + Infof("overriding run start time (orbit-reset-time) to %s for SYNTHETIC run", pdpOverrideRunStartTime) + } else { + log.WithField("partition", envId). + WithField("call", "Configure"). + WithField("runType", runType). + Warnf("overriding run start time (orbit-reset-time) to %s for non-SYNTHETIC run", pdpOverrideRunStartTime) + } + } else { // no run start override defined + if strings.Contains(runType, "SYNTHETIC") { + log.WithField("partition", envId). + WithField("call", "Configure"). + WithField("runType", runType). + Warnf("requested SYNTHETIC run but run start time (orbit-reset-time) override not provided") + } + } + + ctx, cancel := integration.NewContext(envId, varStack, timeout) + defer cancel() + err := handleConfigure(ctx, p.odcClient, arguments, paddingTimeout, envId, call) + if err != nil { + log.WithField("level", infologger.IL_Support). + WithField("partition", envId). + WithField("call", "Configure"). + WithError(err). + Error("ODC error") + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + } + + return + } + stack["Reset"] = func() (out string) { + // ODC Reset + + timeout := callable.AcquireTimeout(ODC_RESET_TIMEOUT, varStack, "Reset", envId) + + callFailedStr := "EPN Reset call failed" + + ctx, cancel := integration.NewContext(envId, call.VarStack, timeout) + defer cancel() + err := handleReset(ctx, p.odcClient, nil, paddingTimeout, envId, call) + if err != nil { + log.WithError(err). + WithField("level", infologger.IL_Support). + WithField("partition", envId). + WithField("call", "Reset"). + Error("ODC error") + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + } + return + } + stack["PartitionTerminate"] = func() (out string) { + // ODC Terminate + Shutdown + + _, isPartitionInitializeCalled := varStack["__odc_partitioninitialize_called"] + if !isPartitionInitializeCalled { + log.WithField("partition", envId). + WithField("level", infologger.IL_Support). + WithField("call", "PartitionTerminate"). + Warn("PartitionInitialize was not called, skipping PartitionTerminate") + return + } + timeout := callable.AcquireTimeout(ODC_PARTITIONTERMINATE_TIMEOUT, varStack, "PartitionTerminate", envId) + + callFailedStr := "EPN PartitionTerminate call failed" + + ctx, cancel := integration.NewContext(envId, varStack, timeout) + defer cancel() + err := handlePartitionTerminate(ctx, p.odcClient, nil, paddingTimeout, envId, call) + if err != nil { + log.WithError(err). + WithField("level", infologger.IL_Support). + WithField("partition", envId). + WithField("call", "PartitionTerminate"). + Error("ODC error") + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + } + return + } + stack["Start"] = func() (out string) { // must formally return string even when we return nothing + + // ODC SetProperties + Start + + rn, ok := varStack["run_number"] + if !ok { + log.WithField("partition", envId). + WithField("call", "Start"). + Warn("cannot acquire run number for ODC") + } + cleanupCountS, ok := varStack["__fmq_cleanup_count"] + if !ok { + log.WithField("partition", envId). + WithField("call", "Start"). + Warn("cannot acquire FairMQ devices cleanup count for ODC") + } + + var ( + runNumberu64 uint64 + cleanupCount int + err error + ) + callFailedStr := "EPN Start call failed" + + runNumberu64, err = strconv.ParseUint(rn, 10, 32) + if err != nil { + log.WithField("partition", envId). + WithError(err). + Error("cannot acquire run number for ODC SOR") + runNumberu64 = 0 + } + cleanupCount, err = strconv.Atoi(cleanupCountS) + if err != nil { + log.WithField("partition", envId). + WithError(err). + Error("cannot acquire FairMQ devices cleanup count for ODC SOR") + cleanupCount = 1 + } + + timeout := callable.AcquireTimeout(ODC_START_TIMEOUT, varStack, "Start", envId) + + arguments := make(map[string]string) + arguments["runNumber"] = rn + arguments["cleanup"] = strconv.Itoa(cleanupCount) + for _, key := range environment.StartActivityParameterKeys { + if value, ok := varStack[key]; ok { + arguments[key] = value + } + } + + ctx, cancel := integration.NewContext(envId, varStack, timeout) + defer cancel() + err = handleStart(ctx, p.odcClient, arguments, paddingTimeout, envId, runNumberu64, call) + if err != nil { + log.WithError(err). + WithField("level", infologger.IL_Support). + WithField("partition", envId). + WithField("call", "Start"). + Error("ODC error") + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + } + return + } + stack["Stop"] = func() (out string) { + // ODC Stop + callFailedStr := "EPN Stop call failed" + var ( + runNumberu64 uint64 + err error + ) + + rn, ok := varStack["run_number"] + if !ok { + log.WithField("partition", envId). + WithField("call", "Stop"). + Warn("cannot acquire run number for ODC Stop") + } + runNumberu64, err = strconv.ParseUint(rn, 10, 32) + if err != nil { + log.WithField("partition", envId). + WithField("call", "Stop"). + WithError(err). + Error("cannot acquire run number for ODC EOR") + runNumberu64 = 0 + } + + arguments := make(map[string]string) + for _, key := range environment.StopActivityParameterKeys { + if value, ok := varStack[key]; ok { + arguments[key] = value + } + } + + timeout := callable.AcquireTimeout(ODC_STOP_TIMEOUT, varStack, "Stop", envId) + + ctx, cancel := integration.NewContext(envId, varStack, timeout) + defer cancel() + err = handleStop(ctx, p.odcClient, arguments, paddingTimeout, envId, runNumberu64, call) + if err != nil { + log.WithError(err). + WithField("level", infologger.IL_Support). + WithField("partition", envId). + WithField("call", "Stop"). + Error("ODC error") + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + } + return + } + stack["EnsureStop"] = func() (out string) { + // ODC Stop + callFailedStr := "EPN EnsureStop call failed" + var ( + runNumberu64 uint64 + err error + ) + + timeout := callable.AcquireTimeout(ODC_STOP_TIMEOUT, varStack, "EnsureStop", envId) + + ctx, cancel := integration.NewContext(envId, varStack, timeout) + defer cancel() + + state, err := handleGetState(ctx, p.odcClient, envId) + if err != nil { + log.WithError(err). + WithField("level", infologger.IL_Support). + WithField("partition", envId). + WithField("call", "EnsureStop"). + Error("ODC error") + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + return + } + if state != "RUNNING" { + log.WithField("level", infologger.IL_Devel). + WithField("partition", envId). + WithField("call", "EnsureStop"). + Infof("ODC EnsureStop attempted, while ODC partition is not in 'RUNNING' but '%s', skipping", state) + return + } + + rn, ok := varStack["run_number"] + if !ok { + log.WithField("partition", envId). + WithField("call", "EnsureStop"). + Warn("cannot acquire run number for ODC EnsureStop") + } + runNumberu64, err = strconv.ParseUint(rn, 10, 32) + if err != nil { + log.WithField("partition", envId). + WithField("call", "EnsureStop"). + WithError(err). + Error("cannot acquire run number for ODC EOR") + runNumberu64 = 0 + } + arguments := make(map[string]string) + for _, key := range environment.StopActivityParameterKeys { + if value, ok := varStack[key]; ok { + arguments[key] = value + } + } + + err = handleStop(ctx, p.odcClient, arguments, paddingTimeout, envId, runNumberu64, call) + if err != nil { + log.WithError(err). + WithField("level", infologger.IL_Support). + WithField("partition", envId). + WithField("call", "EnsureStop"). + Error("ODC error") + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + } + return + } + stack["EnsureCleanup"] = func() (out string) { + // ODC Shutdown for current env + all orphans + + timeout := callable.AcquireTimeout(ODC_GENERAL_OP_TIMEOUT, varStack, "EnsureCleanup", envId) + + callFailedStr := "EPN EnsureCleanup call failed" + + ctx, cancel := integration.NewContext(envId, varStack, timeout) + defer cancel() + err := handleCleanup(ctx, p.odcClient, nil, paddingTimeout, envId, call) + if err != nil { + log.WithError(err). + WithField("level", infologger.IL_Support). + WithField("partition", envId). + WithField("call", "EnsureCleanup"). + Error("ODC error") + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + } + return + } + stack["PreDeploymentCleanup"] = func() (out string) { + // ODC Shutdown for all orphans + + timeout := callable.AcquireTimeout(ODC_GENERAL_OP_TIMEOUT, varStack, "PreDeploymentCleanup", envId) + + callFailedStr := "EPN PreDeploymentCleanup call failed" + + ctx, cancel := integration.NewContext(envId, varStack, timeout) + defer cancel() + err := handleCleanup(ctx, p.odcClient, nil, paddingTimeout, "", call) + if err != nil { + log.WithError(err). + WithField("level", infologger.IL_Support). + WithField("partition", envId). + WithField("call", "PreDeploymentCleanup"). + Error("ODC error") + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + } + return + } + stack["EnsureCleanupLegacy"] = func() (out string) { + // ODC Reset + Terminate + Shutdown for current env + + timeout := callable.AcquireTimeout(ODC_GENERAL_OP_TIMEOUT, varStack, "EnsureCleanupLegacy", envId) + + callFailedStr := "EPN EnsureCleanupLegacy call failed" + + ctx, cancel := integration.NewContext(envId, varStack, timeout) + defer cancel() + err := handleCleanupLegacy(ctx, p.odcClient, nil, paddingTimeout, envId, call) + if err != nil { + log.WithError(err). + WithField("level", infologger.IL_Support). + WithField("partition", envId). + WithField("call", "EnsureCleanupLegacy"). + Error("ODC error") + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + } + return + } + return +} + +func (p *Plugin) parseDetectors(detectorsParam string) (detectors []string, err error) { + detectorsSlice := make([]string, 0) + bytes := []byte(detectorsParam) + err = json.Unmarshal(bytes, &detectorsSlice) + if err != nil { + log.WithError(err). + Error("error processing EPN/PDP detectors list") + return + } + detectors = detectorsSlice + return +} + +func (p *Plugin) Destroy() error { + p.cachedStatusCancelFunc() + return p.odcClient.Close() +} diff --git a/core/integration/odc/protos/odc.pb.go b/core/integration/odc/protos/odc.pb.go new file mode 100644 index 000000000..9475e2b02 --- /dev/null +++ b/core/integration/odc/protos/odc.pb.go @@ -0,0 +1,2339 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.34.1 +// protoc v5.29.3 +// source: protos/odc.proto + +package odc + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// Request status +type ReplyStatus int32 + +const ( + ReplyStatus_UNKNOWN ReplyStatus = 0 // Status is unknown + ReplyStatus_SUCCESS ReplyStatus = 1 // Request performed successfully + ReplyStatus_ERROR ReplyStatus = 2 // Failed to perform request +) + +// Enum value maps for ReplyStatus. +var ( + ReplyStatus_name = map[int32]string{ + 0: "UNKNOWN", + 1: "SUCCESS", + 2: "ERROR", + } + ReplyStatus_value = map[string]int32{ + "UNKNOWN": 0, + "SUCCESS": 1, + "ERROR": 2, + } +) + +func (x ReplyStatus) Enum() *ReplyStatus { + p := new(ReplyStatus) + *p = x + return p +} + +func (x ReplyStatus) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (ReplyStatus) Descriptor() protoreflect.EnumDescriptor { + return file_protos_odc_proto_enumTypes[0].Descriptor() +} + +func (ReplyStatus) Type() protoreflect.EnumType { + return &file_protos_odc_proto_enumTypes[0] +} + +func (x ReplyStatus) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use ReplyStatus.Descriptor instead. +func (ReplyStatus) EnumDescriptor() ([]byte, []int) { + return file_protos_odc_proto_rawDescGZIP(), []int{0} +} + +// Session status as defined by DDS +type SessionStatus int32 + +const ( + SessionStatus_UNKNOWN_ SessionStatus = 0 // Status is unknown + SessionStatus_RUNNING SessionStatus = 1 // DDS session is running + SessionStatus_STOPPED SessionStatus = 2 // DDS session is stopped +) + +// Enum value maps for SessionStatus. +var ( + SessionStatus_name = map[int32]string{ + 0: "UNKNOWN_", + 1: "RUNNING", + 2: "STOPPED", + } + SessionStatus_value = map[string]int32{ + "UNKNOWN_": 0, + "RUNNING": 1, + "STOPPED": 2, + } +) + +func (x SessionStatus) Enum() *SessionStatus { + p := new(SessionStatus) + *p = x + return p +} + +func (x SessionStatus) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (SessionStatus) Descriptor() protoreflect.EnumDescriptor { + return file_protos_odc_proto_enumTypes[1].Descriptor() +} + +func (SessionStatus) Type() protoreflect.EnumType { + return &file_protos_odc_proto_enumTypes[1] +} + +func (x SessionStatus) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use SessionStatus.Descriptor instead. +func (SessionStatus) EnumDescriptor() ([]byte, []int) { + return file_protos_odc_proto_rawDescGZIP(), []int{1} +} + +// General error +type Error struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Msg string `protobuf:"bytes,1,opt,name=msg,proto3" json:"msg,omitempty"` // Detailed error message + Code int32 `protobuf:"varint,2,opt,name=code,proto3" json:"code,omitempty"` // Error code. +} + +func (x *Error) Reset() { + *x = Error{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_odc_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Error) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Error) ProtoMessage() {} + +func (x *Error) ProtoReflect() protoreflect.Message { + mi := &file_protos_odc_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Error.ProtoReflect.Descriptor instead. +func (*Error) Descriptor() ([]byte, []int) { + return file_protos_odc_proto_rawDescGZIP(), []int{0} +} + +func (x *Error) GetMsg() string { + if x != nil { + return x.Msg + } + return "" +} + +func (x *Error) GetCode() int32 { + if x != nil { + return x.Code + } + return 0 +} + +// General reply to requests +type GeneralReply struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Msg string `protobuf:"bytes,1,opt,name=msg,proto3" json:"msg,omitempty"` // Detailed reply message + Status ReplyStatus `protobuf:"varint,2,opt,name=status,proto3,enum=odc.ReplyStatus" json:"status,omitempty"` // Request status code (UNKNOWN, SUCCESS, ERROR) + Error *Error `protobuf:"bytes,3,opt,name=error,proto3" json:"error,omitempty"` // If status is ERROR than this field contains error description otherwise it's empty + Exectime int32 `protobuf:"varint,4,opt,name=exectime,proto3" json:"exectime,omitempty"` // Request execution time in ms + Partitionid string `protobuf:"bytes,5,opt,name=partitionid,proto3" json:"partitionid,omitempty"` // Partition ID from ECS + Sessionid string `protobuf:"bytes,6,opt,name=sessionid,proto3" json:"sessionid,omitempty"` // DDS session ID + State string `protobuf:"bytes,7,opt,name=state,proto3" json:"state,omitempty"` // If successful and applicable to a request then contains an aggregated FairMQ device state, otherwise UNDEFINED. + Runnr uint64 `protobuf:"varint,8,opt,name=runnr,proto3" json:"runnr,omitempty"` // Run number from ECS (optional) + Hosts []string `protobuf:"bytes,9,rep,name=hosts,proto3" json:"hosts,omitempty"` // Where applicable, provides a list of used hosts (Submit/Run requests) + Rmsjobids string `protobuf:"bytes,10,opt,name=rmsjobids,proto3" json:"rmsjobids,omitempty"` // Where applicable, provides a list of job IDs from the resource management system +} + +func (x *GeneralReply) Reset() { + *x = GeneralReply{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_odc_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GeneralReply) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GeneralReply) ProtoMessage() {} + +func (x *GeneralReply) ProtoReflect() protoreflect.Message { + mi := &file_protos_odc_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GeneralReply.ProtoReflect.Descriptor instead. +func (*GeneralReply) Descriptor() ([]byte, []int) { + return file_protos_odc_proto_rawDescGZIP(), []int{1} +} + +func (x *GeneralReply) GetMsg() string { + if x != nil { + return x.Msg + } + return "" +} + +func (x *GeneralReply) GetStatus() ReplyStatus { + if x != nil { + return x.Status + } + return ReplyStatus_UNKNOWN +} + +func (x *GeneralReply) GetError() *Error { + if x != nil { + return x.Error + } + return nil +} + +func (x *GeneralReply) GetExectime() int32 { + if x != nil { + return x.Exectime + } + return 0 +} + +func (x *GeneralReply) GetPartitionid() string { + if x != nil { + return x.Partitionid + } + return "" +} + +func (x *GeneralReply) GetSessionid() string { + if x != nil { + return x.Sessionid + } + return "" +} + +func (x *GeneralReply) GetState() string { + if x != nil { + return x.State + } + return "" +} + +func (x *GeneralReply) GetRunnr() uint64 { + if x != nil { + return x.Runnr + } + return 0 +} + +func (x *GeneralReply) GetHosts() []string { + if x != nil { + return x.Hosts + } + return nil +} + +func (x *GeneralReply) GetRmsjobids() string { + if x != nil { + return x.Rmsjobids + } + return "" +} + +// Device information +// Runtime task ID and path are the same as in DDS. +// To get task details use DDS Topology API. +type Device struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id uint64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` // Runtime task ID (same as in DDS) + State string `protobuf:"bytes,2,opt,name=state,proto3" json:"state,omitempty"` // FairMQ device state as string + Path string `protobuf:"bytes,3,opt,name=path,proto3" json:"path,omitempty"` // Runtime task path (same as in DDS) + Ignored bool `protobuf:"varint,4,opt,name=ignored,proto3" json:"ignored,omitempty"` // Device was stopped and set to be ignored + Host string `protobuf:"bytes,5,opt,name=host,proto3" json:"host,omitempty"` // Host where the task runs + Expendable bool `protobuf:"varint,6,opt,name=expendable,proto3" json:"expendable,omitempty"` // Device is expendable + Rmsjobid string `protobuf:"bytes,7,opt,name=rmsjobid,proto3" json:"rmsjobid,omitempty"` // job IDs from the resource management system +} + +func (x *Device) Reset() { + *x = Device{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_odc_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Device) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Device) ProtoMessage() {} + +func (x *Device) ProtoReflect() protoreflect.Message { + mi := &file_protos_odc_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Device.ProtoReflect.Descriptor instead. +func (*Device) Descriptor() ([]byte, []int) { + return file_protos_odc_proto_rawDescGZIP(), []int{2} +} + +func (x *Device) GetId() uint64 { + if x != nil { + return x.Id + } + return 0 +} + +func (x *Device) GetState() string { + if x != nil { + return x.State + } + return "" +} + +func (x *Device) GetPath() string { + if x != nil { + return x.Path + } + return "" +} + +func (x *Device) GetIgnored() bool { + if x != nil { + return x.Ignored + } + return false +} + +func (x *Device) GetHost() string { + if x != nil { + return x.Host + } + return "" +} + +func (x *Device) GetExpendable() bool { + if x != nil { + return x.Expendable + } + return false +} + +func (x *Device) GetRmsjobid() string { + if x != nil { + return x.Rmsjobid + } + return "" +} + +// Collection information +type Collection struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id uint64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` // Runtime collection ID (same as in DDS) + State string `protobuf:"bytes,2,opt,name=state,proto3" json:"state,omitempty"` // Aggregated state as string + Path string `protobuf:"bytes,3,opt,name=path,proto3" json:"path,omitempty"` // Path in the topology + Host string `protobuf:"bytes,5,opt,name=host,proto3" json:"host,omitempty"` // Host where the collection runs +} + +func (x *Collection) Reset() { + *x = Collection{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_odc_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Collection) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Collection) ProtoMessage() {} + +func (x *Collection) ProtoReflect() protoreflect.Message { + mi := &file_protos_odc_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Collection.ProtoReflect.Descriptor instead. +func (*Collection) Descriptor() ([]byte, []int) { + return file_protos_odc_proto_rawDescGZIP(), []int{3} +} + +func (x *Collection) GetId() uint64 { + if x != nil { + return x.Id + } + return 0 +} + +func (x *Collection) GetState() string { + if x != nil { + return x.State + } + return "" +} + +func (x *Collection) GetPath() string { + if x != nil { + return x.Path + } + return "" +} + +func (x *Collection) GetHost() string { + if x != nil { + return x.Host + } + return "" +} + +// Device change/get state request +type StateRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Partitionid string `protobuf:"bytes,1,opt,name=partitionid,proto3" json:"partitionid,omitempty"` // Partition ID from ECS + Runnr uint64 `protobuf:"varint,4,opt,name=runnr,proto3" json:"runnr,omitempty"` // Run number from ECS + Timeout uint32 `protobuf:"varint,5,opt,name=timeout,proto3" json:"timeout,omitempty"` // Request timeout in sec. If not set or 0 than default is used. + Path string `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"` // Task path in the DDS topology. Can be a regular expression. + Detailed bool `protobuf:"varint,3,opt,name=detailed,proto3" json:"detailed,omitempty"` // If true then a list of affected devices is populated in the reply. +} + +func (x *StateRequest) Reset() { + *x = StateRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_odc_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StateRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StateRequest) ProtoMessage() {} + +func (x *StateRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_odc_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StateRequest.ProtoReflect.Descriptor instead. +func (*StateRequest) Descriptor() ([]byte, []int) { + return file_protos_odc_proto_rawDescGZIP(), []int{4} +} + +func (x *StateRequest) GetPartitionid() string { + if x != nil { + return x.Partitionid + } + return "" +} + +func (x *StateRequest) GetRunnr() uint64 { + if x != nil { + return x.Runnr + } + return 0 +} + +func (x *StateRequest) GetTimeout() uint32 { + if x != nil { + return x.Timeout + } + return 0 +} + +func (x *StateRequest) GetPath() string { + if x != nil { + return x.Path + } + return "" +} + +func (x *StateRequest) GetDetailed() bool { + if x != nil { + return x.Detailed + } + return false +} + +// Device change/get state reply +type StateReply struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Reply *GeneralReply `protobuf:"bytes,1,opt,name=reply,proto3" json:"reply,omitempty"` // General reply. See GeneralReply message for details. + Devices []*Device `protobuf:"bytes,2,rep,name=devices,proto3" json:"devices,omitempty"` // If detailed reply is requested then this field contains a list of affected devices otherwise it's empty. + Collections []*Collection `protobuf:"bytes,3,rep,name=collections,proto3" json:"collections,omitempty"` // If detailed reply is requested then this field contains a list of affected collections otherwise it's empty. +} + +func (x *StateReply) Reset() { + *x = StateReply{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_odc_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StateReply) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StateReply) ProtoMessage() {} + +func (x *StateReply) ProtoReflect() protoreflect.Message { + mi := &file_protos_odc_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StateReply.ProtoReflect.Descriptor instead. +func (*StateReply) Descriptor() ([]byte, []int) { + return file_protos_odc_proto_rawDescGZIP(), []int{5} +} + +func (x *StateReply) GetReply() *GeneralReply { + if x != nil { + return x.Reply + } + return nil +} + +func (x *StateReply) GetDevices() []*Device { + if x != nil { + return x.Devices + } + return nil +} + +func (x *StateReply) GetCollections() []*Collection { + if x != nil { + return x.Collections + } + return nil +} + +// Status of each partition +type PartitionStatus struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Partitionid string `protobuf:"bytes,1,opt,name=partitionid,proto3" json:"partitionid,omitempty"` // Partition ID from ECS + Runnr uint64 `protobuf:"varint,5,opt,name=runnr,proto3" json:"runnr,omitempty"` // Run number from ECS + Timeout uint32 `protobuf:"varint,6,opt,name=timeout,proto3" json:"timeout,omitempty"` // Request timeout in sec. If not set or 0 than default is used. + Sessionid string `protobuf:"bytes,2,opt,name=sessionid,proto3" json:"sessionid,omitempty"` // DDS session ID + Status SessionStatus `protobuf:"varint,3,opt,name=status,proto3,enum=odc.SessionStatus" json:"status,omitempty"` // DDS session status + State string `protobuf:"bytes,4,opt,name=state,proto3" json:"state,omitempty"` // If successful and applicable to a request then contains an aggregated FairMQ device state, otherwise UNDEFINED. +} + +func (x *PartitionStatus) Reset() { + *x = PartitionStatus{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_odc_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PartitionStatus) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PartitionStatus) ProtoMessage() {} + +func (x *PartitionStatus) ProtoReflect() protoreflect.Message { + mi := &file_protos_odc_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PartitionStatus.ProtoReflect.Descriptor instead. +func (*PartitionStatus) Descriptor() ([]byte, []int) { + return file_protos_odc_proto_rawDescGZIP(), []int{6} +} + +func (x *PartitionStatus) GetPartitionid() string { + if x != nil { + return x.Partitionid + } + return "" +} + +func (x *PartitionStatus) GetRunnr() uint64 { + if x != nil { + return x.Runnr + } + return 0 +} + +func (x *PartitionStatus) GetTimeout() uint32 { + if x != nil { + return x.Timeout + } + return 0 +} + +func (x *PartitionStatus) GetSessionid() string { + if x != nil { + return x.Sessionid + } + return "" +} + +func (x *PartitionStatus) GetStatus() SessionStatus { + if x != nil { + return x.Status + } + return SessionStatus_UNKNOWN_ +} + +func (x *PartitionStatus) GetState() string { + if x != nil { + return x.State + } + return "" +} + +// ODC status reply +type StatusReply struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Msg string `protobuf:"bytes,1,opt,name=msg,proto3" json:"msg,omitempty"` // Detailed reply message + Status ReplyStatus `protobuf:"varint,2,opt,name=status,proto3,enum=odc.ReplyStatus" json:"status,omitempty"` // Request status code (UNKNOWN, SUCCESS, ERROR) + Error *Error `protobuf:"bytes,3,opt,name=error,proto3" json:"error,omitempty"` // If status is ERROR than this field contains error description otherwise it's empty + Exectime int32 `protobuf:"varint,4,opt,name=exectime,proto3" json:"exectime,omitempty"` // Request execution time in ms + Partitions []*PartitionStatus `protobuf:"bytes,5,rep,name=partitions,proto3" json:"partitions,omitempty"` // Status of each partition +} + +func (x *StatusReply) Reset() { + *x = StatusReply{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_odc_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StatusReply) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StatusReply) ProtoMessage() {} + +func (x *StatusReply) ProtoReflect() protoreflect.Message { + mi := &file_protos_odc_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StatusReply.ProtoReflect.Descriptor instead. +func (*StatusReply) Descriptor() ([]byte, []int) { + return file_protos_odc_proto_rawDescGZIP(), []int{7} +} + +func (x *StatusReply) GetMsg() string { + if x != nil { + return x.Msg + } + return "" +} + +func (x *StatusReply) GetStatus() ReplyStatus { + if x != nil { + return x.Status + } + return ReplyStatus_UNKNOWN +} + +func (x *StatusReply) GetError() *Error { + if x != nil { + return x.Error + } + return nil +} + +func (x *StatusReply) GetExectime() int32 { + if x != nil { + return x.Exectime + } + return 0 +} + +func (x *StatusReply) GetPartitions() []*PartitionStatus { + if x != nil { + return x.Partitions + } + return nil +} + +// Initialize request +type InitializeRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Partitionid string `protobuf:"bytes,1,opt,name=partitionid,proto3" json:"partitionid,omitempty"` // Partition ID from ECS + Runnr uint64 `protobuf:"varint,3,opt,name=runnr,proto3" json:"runnr,omitempty"` // Run number from ECS + Timeout uint32 `protobuf:"varint,4,opt,name=timeout,proto3" json:"timeout,omitempty"` // Request timeout in sec. If not set or 0 than default is used. + Sessionid string `protobuf:"bytes,2,opt,name=sessionid,proto3" json:"sessionid,omitempty"` // DDS session ID. If session ID is provided that ODC connects to an existing DDS session. If session ID is an empty string that a new DDS session is created. +} + +func (x *InitializeRequest) Reset() { + *x = InitializeRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_odc_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *InitializeRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*InitializeRequest) ProtoMessage() {} + +func (x *InitializeRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_odc_proto_msgTypes[8] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use InitializeRequest.ProtoReflect.Descriptor instead. +func (*InitializeRequest) Descriptor() ([]byte, []int) { + return file_protos_odc_proto_rawDescGZIP(), []int{8} +} + +func (x *InitializeRequest) GetPartitionid() string { + if x != nil { + return x.Partitionid + } + return "" +} + +func (x *InitializeRequest) GetRunnr() uint64 { + if x != nil { + return x.Runnr + } + return 0 +} + +func (x *InitializeRequest) GetTimeout() uint32 { + if x != nil { + return x.Timeout + } + return 0 +} + +func (x *InitializeRequest) GetSessionid() string { + if x != nil { + return x.Sessionid + } + return "" +} + +// Submit request +type SubmitRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Partitionid string `protobuf:"bytes,1,opt,name=partitionid,proto3" json:"partitionid,omitempty"` // Partition ID from ECS + Runnr uint64 `protobuf:"varint,4,opt,name=runnr,proto3" json:"runnr,omitempty"` // Run number from ECS + Timeout uint32 `protobuf:"varint,5,opt,name=timeout,proto3" json:"timeout,omitempty"` // Request timeout in sec. If not set or 0 than default is used. + Plugin string `protobuf:"bytes,2,opt,name=plugin,proto3" json:"plugin,omitempty"` // Name of the resource plugin registered in odc-server + Resources string `protobuf:"bytes,3,opt,name=resources,proto3" json:"resources,omitempty"` // Resource description +} + +func (x *SubmitRequest) Reset() { + *x = SubmitRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_odc_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SubmitRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SubmitRequest) ProtoMessage() {} + +func (x *SubmitRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_odc_proto_msgTypes[9] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SubmitRequest.ProtoReflect.Descriptor instead. +func (*SubmitRequest) Descriptor() ([]byte, []int) { + return file_protos_odc_proto_rawDescGZIP(), []int{9} +} + +func (x *SubmitRequest) GetPartitionid() string { + if x != nil { + return x.Partitionid + } + return "" +} + +func (x *SubmitRequest) GetRunnr() uint64 { + if x != nil { + return x.Runnr + } + return 0 +} + +func (x *SubmitRequest) GetTimeout() uint32 { + if x != nil { + return x.Timeout + } + return 0 +} + +func (x *SubmitRequest) GetPlugin() string { + if x != nil { + return x.Plugin + } + return "" +} + +func (x *SubmitRequest) GetResources() string { + if x != nil { + return x.Resources + } + return "" +} + +// Activate request +type ActivateRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Partitionid string `protobuf:"bytes,1,opt,name=partitionid,proto3" json:"partitionid,omitempty"` // Partition ID from ECS + Runnr uint64 `protobuf:"varint,5,opt,name=runnr,proto3" json:"runnr,omitempty"` // Run number from ECS + Timeout uint32 `protobuf:"varint,6,opt,name=timeout,proto3" json:"timeout,omitempty"` // Request timeout in sec. If not set or 0 than default is used. + // Either `topology`, `content` or `script` has to be set. If all or none is set then an error is returned. + Topology string `protobuf:"bytes,2,opt,name=topology,proto3" json:"topology,omitempty"` // Filepath to the XML DDS topology file + Content string `protobuf:"bytes,3,opt,name=content,proto3" json:"content,omitempty"` // Content of the XML DDS topology + Script string `protobuf:"bytes,4,opt,name=script,proto3" json:"script,omitempty"` // Shell commands to be executed by ODC in order to generate content of the XML DDS topology +} + +func (x *ActivateRequest) Reset() { + *x = ActivateRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_odc_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ActivateRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ActivateRequest) ProtoMessage() {} + +func (x *ActivateRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_odc_proto_msgTypes[10] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ActivateRequest.ProtoReflect.Descriptor instead. +func (*ActivateRequest) Descriptor() ([]byte, []int) { + return file_protos_odc_proto_rawDescGZIP(), []int{10} +} + +func (x *ActivateRequest) GetPartitionid() string { + if x != nil { + return x.Partitionid + } + return "" +} + +func (x *ActivateRequest) GetRunnr() uint64 { + if x != nil { + return x.Runnr + } + return 0 +} + +func (x *ActivateRequest) GetTimeout() uint32 { + if x != nil { + return x.Timeout + } + return 0 +} + +func (x *ActivateRequest) GetTopology() string { + if x != nil { + return x.Topology + } + return "" +} + +func (x *ActivateRequest) GetContent() string { + if x != nil { + return x.Content + } + return "" +} + +func (x *ActivateRequest) GetScript() string { + if x != nil { + return x.Script + } + return "" +} + +// Run request +type RunRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Partitionid string `protobuf:"bytes,1,opt,name=partitionid,proto3" json:"partitionid,omitempty"` // Partition ID from ECS + Runnr uint64 `protobuf:"varint,7,opt,name=runnr,proto3" json:"runnr,omitempty"` // Run number from ECS + Timeout uint32 `protobuf:"varint,8,opt,name=timeout,proto3" json:"timeout,omitempty"` // Request timeout in sec. If not set or 0 than default is used. + // Either `topology`, `content` or `script` has to be set. If all or none is set then an error is returned. + Topology string `protobuf:"bytes,2,opt,name=topology,proto3" json:"topology,omitempty"` // Filepath to the XML DDS topology file + Content string `protobuf:"bytes,5,opt,name=content,proto3" json:"content,omitempty"` // Content of the XML DDS topology + Script string `protobuf:"bytes,6,opt,name=script,proto3" json:"script,omitempty"` // Shell commands to be executed by ODC in order to generate content of the XML DDS topology + Plugin string `protobuf:"bytes,3,opt,name=plugin,proto3" json:"plugin,omitempty"` // Name of the resource plugin registered in odc-server + Resources string `protobuf:"bytes,4,opt,name=resources,proto3" json:"resources,omitempty"` // Resource description + ExtractTopoResources bool `protobuf:"varint,9,opt,name=extractTopoResources,proto3" json:"extractTopoResources,omitempty"` // extract required resources from the topology file only (plugin & resources fields are ignored) +} + +func (x *RunRequest) Reset() { + *x = RunRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_odc_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RunRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RunRequest) ProtoMessage() {} + +func (x *RunRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_odc_proto_msgTypes[11] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RunRequest.ProtoReflect.Descriptor instead. +func (*RunRequest) Descriptor() ([]byte, []int) { + return file_protos_odc_proto_rawDescGZIP(), []int{11} +} + +func (x *RunRequest) GetPartitionid() string { + if x != nil { + return x.Partitionid + } + return "" +} + +func (x *RunRequest) GetRunnr() uint64 { + if x != nil { + return x.Runnr + } + return 0 +} + +func (x *RunRequest) GetTimeout() uint32 { + if x != nil { + return x.Timeout + } + return 0 +} + +func (x *RunRequest) GetTopology() string { + if x != nil { + return x.Topology + } + return "" +} + +func (x *RunRequest) GetContent() string { + if x != nil { + return x.Content + } + return "" +} + +func (x *RunRequest) GetScript() string { + if x != nil { + return x.Script + } + return "" +} + +func (x *RunRequest) GetPlugin() string { + if x != nil { + return x.Plugin + } + return "" +} + +func (x *RunRequest) GetResources() string { + if x != nil { + return x.Resources + } + return "" +} + +func (x *RunRequest) GetExtractTopoResources() bool { + if x != nil { + return x.ExtractTopoResources + } + return false +} + +// Update request +type UpdateRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Partitionid string `protobuf:"bytes,1,opt,name=partitionid,proto3" json:"partitionid,omitempty"` // Partition ID from ECS + Runnr uint64 `protobuf:"varint,5,opt,name=runnr,proto3" json:"runnr,omitempty"` // Run number from ECS + Timeout uint32 `protobuf:"varint,6,opt,name=timeout,proto3" json:"timeout,omitempty"` // Request timeout in sec. If not set or 0 than default is used. + // Either `topology`, `content` or `script` has to be set. If all or none is set then an error is returned. + Topology string `protobuf:"bytes,2,opt,name=topology,proto3" json:"topology,omitempty"` // Filepath to the XML DDS topology file + Content string `protobuf:"bytes,3,opt,name=content,proto3" json:"content,omitempty"` // Content of the XML DDS topology + Script string `protobuf:"bytes,4,opt,name=script,proto3" json:"script,omitempty"` // Shell commands to be executed by ODC in order to generate content of the XML DDS topology +} + +func (x *UpdateRequest) Reset() { + *x = UpdateRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_odc_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UpdateRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdateRequest) ProtoMessage() {} + +func (x *UpdateRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_odc_proto_msgTypes[12] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UpdateRequest.ProtoReflect.Descriptor instead. +func (*UpdateRequest) Descriptor() ([]byte, []int) { + return file_protos_odc_proto_rawDescGZIP(), []int{12} +} + +func (x *UpdateRequest) GetPartitionid() string { + if x != nil { + return x.Partitionid + } + return "" +} + +func (x *UpdateRequest) GetRunnr() uint64 { + if x != nil { + return x.Runnr + } + return 0 +} + +func (x *UpdateRequest) GetTimeout() uint32 { + if x != nil { + return x.Timeout + } + return 0 +} + +func (x *UpdateRequest) GetTopology() string { + if x != nil { + return x.Topology + } + return "" +} + +func (x *UpdateRequest) GetContent() string { + if x != nil { + return x.Content + } + return "" +} + +func (x *UpdateRequest) GetScript() string { + if x != nil { + return x.Script + } + return "" +} + +// Shutdown request +type ShutdownRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Partitionid string `protobuf:"bytes,1,opt,name=partitionid,proto3" json:"partitionid,omitempty"` // Partition ID from ECS + Runnr uint64 `protobuf:"varint,2,opt,name=runnr,proto3" json:"runnr,omitempty"` // Run number from ECS + Timeout uint32 `protobuf:"varint,3,opt,name=timeout,proto3" json:"timeout,omitempty"` // Request timeout in sec. If not set or 0 than default is used. +} + +func (x *ShutdownRequest) Reset() { + *x = ShutdownRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_odc_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ShutdownRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ShutdownRequest) ProtoMessage() {} + +func (x *ShutdownRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_odc_proto_msgTypes[13] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ShutdownRequest.ProtoReflect.Descriptor instead. +func (*ShutdownRequest) Descriptor() ([]byte, []int) { + return file_protos_odc_proto_rawDescGZIP(), []int{13} +} + +func (x *ShutdownRequest) GetPartitionid() string { + if x != nil { + return x.Partitionid + } + return "" +} + +func (x *ShutdownRequest) GetRunnr() uint64 { + if x != nil { + return x.Runnr + } + return 0 +} + +func (x *ShutdownRequest) GetTimeout() uint32 { + if x != nil { + return x.Timeout + } + return 0 +} + +// Key-Value property +type Property struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` // Property key + Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` // Property value +} + +func (x *Property) Reset() { + *x = Property{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_odc_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Property) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Property) ProtoMessage() {} + +func (x *Property) ProtoReflect() protoreflect.Message { + mi := &file_protos_odc_proto_msgTypes[14] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Property.ProtoReflect.Descriptor instead. +func (*Property) Descriptor() ([]byte, []int) { + return file_protos_odc_proto_rawDescGZIP(), []int{14} +} + +func (x *Property) GetKey() string { + if x != nil { + return x.Key + } + return "" +} + +func (x *Property) GetValue() string { + if x != nil { + return x.Value + } + return "" +} + +// Set properties request +type SetPropertiesRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Partitionid string `protobuf:"bytes,1,opt,name=partitionid,proto3" json:"partitionid,omitempty"` // Partition ID from ECS + Runnr uint64 `protobuf:"varint,4,opt,name=runnr,proto3" json:"runnr,omitempty"` // Run number from ECS + Timeout uint32 `protobuf:"varint,5,opt,name=timeout,proto3" json:"timeout,omitempty"` // Request timeout in sec. If not set or 0 than default is used. + Path string `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"` // Task path in the DDS topology. Can be a regular expression. + Properties []*Property `protobuf:"bytes,3,rep,name=properties,proto3" json:"properties,omitempty"` // List of properties to be set +} + +func (x *SetPropertiesRequest) Reset() { + *x = SetPropertiesRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_odc_proto_msgTypes[15] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SetPropertiesRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SetPropertiesRequest) ProtoMessage() {} + +func (x *SetPropertiesRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_odc_proto_msgTypes[15] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SetPropertiesRequest.ProtoReflect.Descriptor instead. +func (*SetPropertiesRequest) Descriptor() ([]byte, []int) { + return file_protos_odc_proto_rawDescGZIP(), []int{15} +} + +func (x *SetPropertiesRequest) GetPartitionid() string { + if x != nil { + return x.Partitionid + } + return "" +} + +func (x *SetPropertiesRequest) GetRunnr() uint64 { + if x != nil { + return x.Runnr + } + return 0 +} + +func (x *SetPropertiesRequest) GetTimeout() uint32 { + if x != nil { + return x.Timeout + } + return 0 +} + +func (x *SetPropertiesRequest) GetPath() string { + if x != nil { + return x.Path + } + return "" +} + +func (x *SetPropertiesRequest) GetProperties() []*Property { + if x != nil { + return x.Properties + } + return nil +} + +// Configure request +type ConfigureRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Request *StateRequest `protobuf:"bytes,1,opt,name=request,proto3" json:"request,omitempty"` // State change request. See StateRequest for details. +} + +func (x *ConfigureRequest) Reset() { + *x = ConfigureRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_odc_proto_msgTypes[16] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ConfigureRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ConfigureRequest) ProtoMessage() {} + +func (x *ConfigureRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_odc_proto_msgTypes[16] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ConfigureRequest.ProtoReflect.Descriptor instead. +func (*ConfigureRequest) Descriptor() ([]byte, []int) { + return file_protos_odc_proto_rawDescGZIP(), []int{16} +} + +func (x *ConfigureRequest) GetRequest() *StateRequest { + if x != nil { + return x.Request + } + return nil +} + +// Start request +type StartRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Request *StateRequest `protobuf:"bytes,1,opt,name=request,proto3" json:"request,omitempty"` // State change request. See StateRequest for details. +} + +func (x *StartRequest) Reset() { + *x = StartRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_odc_proto_msgTypes[17] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StartRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StartRequest) ProtoMessage() {} + +func (x *StartRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_odc_proto_msgTypes[17] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StartRequest.ProtoReflect.Descriptor instead. +func (*StartRequest) Descriptor() ([]byte, []int) { + return file_protos_odc_proto_rawDescGZIP(), []int{17} +} + +func (x *StartRequest) GetRequest() *StateRequest { + if x != nil { + return x.Request + } + return nil +} + +// Stop request +type StopRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Request *StateRequest `protobuf:"bytes,1,opt,name=request,proto3" json:"request,omitempty"` // State change request. See StateRequest for details. +} + +func (x *StopRequest) Reset() { + *x = StopRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_odc_proto_msgTypes[18] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StopRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StopRequest) ProtoMessage() {} + +func (x *StopRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_odc_proto_msgTypes[18] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StopRequest.ProtoReflect.Descriptor instead. +func (*StopRequest) Descriptor() ([]byte, []int) { + return file_protos_odc_proto_rawDescGZIP(), []int{18} +} + +func (x *StopRequest) GetRequest() *StateRequest { + if x != nil { + return x.Request + } + return nil +} + +// Reset request +type ResetRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Request *StateRequest `protobuf:"bytes,1,opt,name=request,proto3" json:"request,omitempty"` // State change request. See StateRequest for details. +} + +func (x *ResetRequest) Reset() { + *x = ResetRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_odc_proto_msgTypes[19] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ResetRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ResetRequest) ProtoMessage() {} + +func (x *ResetRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_odc_proto_msgTypes[19] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ResetRequest.ProtoReflect.Descriptor instead. +func (*ResetRequest) Descriptor() ([]byte, []int) { + return file_protos_odc_proto_rawDescGZIP(), []int{19} +} + +func (x *ResetRequest) GetRequest() *StateRequest { + if x != nil { + return x.Request + } + return nil +} + +// Terminate request +type TerminateRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Request *StateRequest `protobuf:"bytes,1,opt,name=request,proto3" json:"request,omitempty"` // State change request. See StateRequest for details. +} + +func (x *TerminateRequest) Reset() { + *x = TerminateRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_odc_proto_msgTypes[20] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *TerminateRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TerminateRequest) ProtoMessage() {} + +func (x *TerminateRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_odc_proto_msgTypes[20] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TerminateRequest.ProtoReflect.Descriptor instead. +func (*TerminateRequest) Descriptor() ([]byte, []int) { + return file_protos_odc_proto_rawDescGZIP(), []int{20} +} + +func (x *TerminateRequest) GetRequest() *StateRequest { + if x != nil { + return x.Request + } + return nil +} + +// Status request +type StatusRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Running bool `protobuf:"varint,1,opt,name=running,proto3" json:"running,omitempty"` // Select only running DDS sessions +} + +func (x *StatusRequest) Reset() { + *x = StatusRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_odc_proto_msgTypes[21] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StatusRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StatusRequest) ProtoMessage() {} + +func (x *StatusRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_odc_proto_msgTypes[21] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StatusRequest.ProtoReflect.Descriptor instead. +func (*StatusRequest) Descriptor() ([]byte, []int) { + return file_protos_odc_proto_rawDescGZIP(), []int{21} +} + +func (x *StatusRequest) GetRunning() bool { + if x != nil { + return x.Running + } + return false +} + +var File_protos_odc_proto protoreflect.FileDescriptor + +var file_protos_odc_proto_rawDesc = []byte{ + 0x0a, 0x10, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x6f, 0x64, 0x63, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x12, 0x03, 0x6f, 0x64, 0x63, 0x22, 0x2d, 0x0a, 0x05, 0x45, 0x72, 0x72, 0x6f, 0x72, + 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x73, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6d, + 0x73, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, + 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x22, 0xa8, 0x02, 0x0a, 0x0c, 0x47, 0x65, 0x6e, 0x65, 0x72, + 0x61, 0x6c, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x73, 0x67, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6d, 0x73, 0x67, 0x12, 0x28, 0x0a, 0x06, 0x73, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x10, 0x2e, 0x6f, 0x64, 0x63, 0x2e, + 0x52, 0x65, 0x70, 0x6c, 0x79, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x12, 0x20, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x6f, 0x64, 0x63, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x05, + 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x65, 0x63, 0x74, 0x69, 0x6d, + 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x65, 0x78, 0x65, 0x63, 0x74, 0x69, 0x6d, + 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x69, 0x64, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, + 0x6e, 0x69, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x69, 0x64, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x69, + 0x64, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x72, 0x75, 0x6e, 0x6e, 0x72, + 0x18, 0x08, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x72, 0x75, 0x6e, 0x6e, 0x72, 0x12, 0x14, 0x0a, + 0x05, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x68, 0x6f, + 0x73, 0x74, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x72, 0x6d, 0x73, 0x6a, 0x6f, 0x62, 0x69, 0x64, 0x73, + 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x6d, 0x73, 0x6a, 0x6f, 0x62, 0x69, 0x64, + 0x73, 0x22, 0xac, 0x01, 0x0a, 0x06, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x12, 0x0e, 0x0a, 0x02, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, 0x69, 0x64, 0x12, 0x14, 0x0a, 0x05, + 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, + 0x74, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x18, 0x0a, 0x07, 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x65, + 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x64, + 0x12, 0x12, 0x0a, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x68, 0x6f, 0x73, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x65, 0x6e, 0x64, 0x61, 0x62, + 0x6c, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x65, 0x78, 0x70, 0x65, 0x6e, 0x64, + 0x61, 0x62, 0x6c, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x6d, 0x73, 0x6a, 0x6f, 0x62, 0x69, 0x64, + 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x6d, 0x73, 0x6a, 0x6f, 0x62, 0x69, 0x64, + 0x22, 0x5a, 0x0a, 0x0a, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0e, + 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, 0x69, 0x64, 0x12, 0x14, + 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, + 0x74, 0x61, 0x74, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x6f, 0x73, 0x74, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x22, 0x90, 0x01, 0x0a, + 0x0c, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x20, 0x0a, + 0x0b, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0b, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x69, 0x64, 0x12, + 0x14, 0x0a, 0x05, 0x72, 0x75, 0x6e, 0x6e, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, + 0x72, 0x75, 0x6e, 0x6e, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, + 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, + 0x61, 0x74, 0x68, 0x12, 0x1a, 0x0a, 0x08, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x22, + 0x8f, 0x01, 0x0a, 0x0a, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x27, + 0x0a, 0x05, 0x72, 0x65, 0x70, 0x6c, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, + 0x6f, 0x64, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c, 0x52, 0x65, 0x70, 0x6c, 0x79, + 0x52, 0x05, 0x72, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x25, 0x0a, 0x07, 0x64, 0x65, 0x76, 0x69, 0x63, + 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x6f, 0x64, 0x63, 0x2e, 0x44, + 0x65, 0x76, 0x69, 0x63, 0x65, 0x52, 0x07, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x12, 0x31, + 0x0a, 0x0b, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x6f, 0x64, 0x63, 0x2e, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x22, 0xc3, 0x01, 0x0a, 0x0f, 0x50, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x53, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, + 0x6f, 0x6e, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x61, 0x72, 0x74, + 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x69, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x72, 0x75, 0x6e, 0x6e, 0x72, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x72, 0x75, 0x6e, 0x6e, 0x72, 0x12, 0x18, 0x0a, + 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, + 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, + 0x6f, 0x6e, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, + 0x69, 0x6f, 0x6e, 0x69, 0x64, 0x12, 0x2a, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x12, 0x2e, 0x6f, 0x64, 0x63, 0x2e, 0x53, 0x65, 0x73, 0x73, + 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0xbd, 0x01, 0x0a, 0x0b, 0x53, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x73, 0x67, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6d, 0x73, 0x67, 0x12, 0x28, 0x0a, 0x06, 0x73, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x10, 0x2e, 0x6f, 0x64, 0x63, 0x2e, + 0x52, 0x65, 0x70, 0x6c, 0x79, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x12, 0x20, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x6f, 0x64, 0x63, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x05, + 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x65, 0x63, 0x74, 0x69, 0x6d, + 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x65, 0x78, 0x65, 0x63, 0x74, 0x69, 0x6d, + 0x65, 0x12, 0x34, 0x0a, 0x0a, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, + 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6f, 0x64, 0x63, 0x2e, 0x50, 0x61, 0x72, 0x74, + 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x0a, 0x70, 0x61, 0x72, + 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x83, 0x01, 0x0a, 0x11, 0x49, 0x6e, 0x69, 0x74, + 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x20, 0x0a, + 0x0b, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0b, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x69, 0x64, 0x12, + 0x14, 0x0a, 0x05, 0x72, 0x75, 0x6e, 0x6e, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, + 0x72, 0x75, 0x6e, 0x6e, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, + 0x1c, 0x0a, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x69, 0x64, 0x22, 0x97, 0x01, + 0x0a, 0x0d, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x20, 0x0a, 0x0b, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x69, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x69, + 0x64, 0x12, 0x14, 0x0a, 0x05, 0x72, 0x75, 0x6e, 0x6e, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x05, 0x72, 0x75, 0x6e, 0x6e, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, + 0x75, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, + 0x74, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x06, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x72, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x22, 0xb1, 0x01, 0x0a, 0x0f, 0x41, 0x63, 0x74, 0x69, + 0x76, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x70, + 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0b, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x69, 0x64, 0x12, 0x14, 0x0a, + 0x05, 0x72, 0x75, 0x6e, 0x6e, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x72, 0x75, + 0x6e, 0x6e, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x1a, 0x0a, + 0x08, 0x74, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x08, 0x74, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6e, + 0x74, 0x65, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, + 0x65, 0x6e, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x22, 0x96, 0x02, 0x0a, 0x0a, + 0x52, 0x75, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x70, 0x61, + 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0b, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x69, 0x64, 0x12, 0x14, 0x0a, 0x05, + 0x72, 0x75, 0x6e, 0x6e, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x72, 0x75, 0x6e, + 0x6e, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x08, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x1a, 0x0a, 0x08, + 0x74, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, + 0x74, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, + 0x65, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, + 0x6e, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x6c, + 0x75, 0x67, 0x69, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x6c, 0x75, 0x67, + 0x69, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, + 0x12, 0x32, 0x0a, 0x14, 0x65, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x54, 0x6f, 0x70, 0x6f, 0x52, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, + 0x65, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x54, 0x6f, 0x70, 0x6f, 0x52, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x73, 0x22, 0xaf, 0x01, 0x0a, 0x0d, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, + 0x69, 0x6f, 0x6e, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x61, 0x72, + 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x69, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x72, 0x75, 0x6e, 0x6e, + 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x72, 0x75, 0x6e, 0x6e, 0x72, 0x12, 0x18, + 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x74, 0x6f, 0x70, 0x6f, + 0x6c, 0x6f, 0x67, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x6f, 0x70, 0x6f, + 0x6c, 0x6f, 0x67, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x12, 0x16, + 0x0a, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, + 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x22, 0x63, 0x0a, 0x0f, 0x53, 0x68, 0x75, 0x74, 0x64, 0x6f, + 0x77, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x70, 0x61, 0x72, + 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, + 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x69, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x72, + 0x75, 0x6e, 0x6e, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x72, 0x75, 0x6e, 0x6e, + 0x72, 0x12, 0x18, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x22, 0x32, 0x0a, 0x08, 0x50, + 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, + 0xab, 0x01, 0x0a, 0x14, 0x53, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x70, 0x61, 0x72, 0x74, + 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, + 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x69, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x72, 0x75, + 0x6e, 0x6e, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x72, 0x75, 0x6e, 0x6e, 0x72, + 0x12, 0x18, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, + 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x2d, + 0x0a, 0x0a, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x6f, 0x64, 0x63, 0x2e, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, + 0x79, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x22, 0x3f, 0x0a, + 0x10, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x2b, 0x0a, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x6f, 0x64, 0x63, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x3b, + 0x0a, 0x0c, 0x53, 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2b, + 0x0a, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x11, 0x2e, 0x6f, 0x64, 0x63, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x52, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x3a, 0x0a, 0x0b, 0x53, + 0x74, 0x6f, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2b, 0x0a, 0x07, 0x72, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x6f, 0x64, + 0x63, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x07, + 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x3b, 0x0a, 0x0c, 0x52, 0x65, 0x73, 0x65, 0x74, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2b, 0x0a, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x6f, 0x64, 0x63, 0x2e, 0x53, + 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x07, 0x72, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x22, 0x3f, 0x0a, 0x10, 0x54, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x74, + 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2b, 0x0a, 0x07, 0x72, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x6f, 0x64, 0x63, 0x2e, + 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x07, 0x72, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x29, 0x0a, 0x0d, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x75, 0x6e, 0x6e, 0x69, 0x6e, + 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x72, 0x75, 0x6e, 0x6e, 0x69, 0x6e, 0x67, + 0x2a, 0x32, 0x0a, 0x0b, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, + 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, + 0x53, 0x55, 0x43, 0x43, 0x45, 0x53, 0x53, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x45, 0x52, 0x52, + 0x4f, 0x52, 0x10, 0x02, 0x2a, 0x37, 0x0a, 0x0d, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x53, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x0c, 0x0a, 0x08, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, + 0x5f, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x52, 0x55, 0x4e, 0x4e, 0x49, 0x4e, 0x47, 0x10, 0x01, + 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x54, 0x4f, 0x50, 0x50, 0x45, 0x44, 0x10, 0x02, 0x32, 0xdf, 0x05, + 0x0a, 0x03, 0x4f, 0x44, 0x43, 0x12, 0x39, 0x0a, 0x0a, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, + 0x69, 0x7a, 0x65, 0x12, 0x16, 0x2e, 0x6f, 0x64, 0x63, 0x2e, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, + 0x6c, 0x69, 0x7a, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x11, 0x2e, 0x6f, 0x64, + 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, + 0x12, 0x31, 0x0a, 0x06, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x12, 0x12, 0x2e, 0x6f, 0x64, 0x63, + 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x11, + 0x2e, 0x6f, 0x64, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c, 0x52, 0x65, 0x70, 0x6c, + 0x79, 0x22, 0x00, 0x12, 0x35, 0x0a, 0x08, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x12, + 0x14, 0x2e, 0x6f, 0x64, 0x63, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x11, 0x2e, 0x6f, 0x64, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, + 0x72, 0x61, 0x6c, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x2b, 0x0a, 0x03, 0x52, 0x75, + 0x6e, 0x12, 0x0f, 0x2e, 0x6f, 0x64, 0x63, 0x2e, 0x52, 0x75, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x11, 0x2e, 0x6f, 0x64, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c, + 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x31, 0x0a, 0x06, 0x55, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x12, 0x12, 0x2e, 0x6f, 0x64, 0x63, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x11, 0x2e, 0x6f, 0x64, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, + 0x72, 0x61, 0x6c, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x35, 0x0a, 0x09, 0x43, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x65, 0x12, 0x15, 0x2e, 0x6f, 0x64, 0x63, 0x2e, 0x43, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0f, + 0x2e, 0x6f, 0x64, 0x63, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, + 0x00, 0x12, 0x3f, 0x0a, 0x0d, 0x53, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, + 0x65, 0x73, 0x12, 0x19, 0x2e, 0x6f, 0x64, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x70, + 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x11, 0x2e, + 0x6f, 0x64, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c, 0x52, 0x65, 0x70, 0x6c, 0x79, + 0x22, 0x00, 0x12, 0x30, 0x0a, 0x08, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x11, + 0x2e, 0x6f, 0x64, 0x63, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x0f, 0x2e, 0x6f, 0x64, 0x63, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x70, + 0x6c, 0x79, 0x22, 0x00, 0x12, 0x2d, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x11, 0x2e, + 0x6f, 0x64, 0x63, 0x2e, 0x53, 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x0f, 0x2e, 0x6f, 0x64, 0x63, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x70, 0x6c, + 0x79, 0x22, 0x00, 0x12, 0x2b, 0x0a, 0x04, 0x53, 0x74, 0x6f, 0x70, 0x12, 0x10, 0x2e, 0x6f, 0x64, + 0x63, 0x2e, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0f, 0x2e, + 0x6f, 0x64, 0x63, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, + 0x12, 0x2d, 0x0a, 0x05, 0x52, 0x65, 0x73, 0x65, 0x74, 0x12, 0x11, 0x2e, 0x6f, 0x64, 0x63, 0x2e, + 0x52, 0x65, 0x73, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0f, 0x2e, 0x6f, + 0x64, 0x63, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, + 0x35, 0x0a, 0x09, 0x54, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x74, 0x65, 0x12, 0x15, 0x2e, 0x6f, + 0x64, 0x63, 0x2e, 0x54, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x0f, 0x2e, 0x6f, 0x64, 0x63, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, + 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x35, 0x0a, 0x08, 0x53, 0x68, 0x75, 0x74, 0x64, 0x6f, + 0x77, 0x6e, 0x12, 0x14, 0x2e, 0x6f, 0x64, 0x63, 0x2e, 0x53, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, + 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x11, 0x2e, 0x6f, 0x64, 0x63, 0x2e, 0x47, + 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x30, 0x0a, + 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x12, 0x2e, 0x6f, 0x64, 0x63, 0x2e, 0x53, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x10, 0x2e, 0x6f, 0x64, + 0x63, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x42, + 0x41, 0x5a, 0x3f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x41, 0x6c, + 0x69, 0x63, 0x65, 0x4f, 0x32, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x2f, 0x43, 0x6f, 0x6e, 0x74, 0x72, + 0x6f, 0x6c, 0x2f, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x2f, 0x6f, 0x64, 0x63, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x3b, 0x6f, + 0x64, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_protos_odc_proto_rawDescOnce sync.Once + file_protos_odc_proto_rawDescData = file_protos_odc_proto_rawDesc +) + +func file_protos_odc_proto_rawDescGZIP() []byte { + file_protos_odc_proto_rawDescOnce.Do(func() { + file_protos_odc_proto_rawDescData = protoimpl.X.CompressGZIP(file_protos_odc_proto_rawDescData) + }) + return file_protos_odc_proto_rawDescData +} + +var file_protos_odc_proto_enumTypes = make([]protoimpl.EnumInfo, 2) +var file_protos_odc_proto_msgTypes = make([]protoimpl.MessageInfo, 22) +var file_protos_odc_proto_goTypes = []interface{}{ + (ReplyStatus)(0), // 0: odc.ReplyStatus + (SessionStatus)(0), // 1: odc.SessionStatus + (*Error)(nil), // 2: odc.Error + (*GeneralReply)(nil), // 3: odc.GeneralReply + (*Device)(nil), // 4: odc.Device + (*Collection)(nil), // 5: odc.Collection + (*StateRequest)(nil), // 6: odc.StateRequest + (*StateReply)(nil), // 7: odc.StateReply + (*PartitionStatus)(nil), // 8: odc.PartitionStatus + (*StatusReply)(nil), // 9: odc.StatusReply + (*InitializeRequest)(nil), // 10: odc.InitializeRequest + (*SubmitRequest)(nil), // 11: odc.SubmitRequest + (*ActivateRequest)(nil), // 12: odc.ActivateRequest + (*RunRequest)(nil), // 13: odc.RunRequest + (*UpdateRequest)(nil), // 14: odc.UpdateRequest + (*ShutdownRequest)(nil), // 15: odc.ShutdownRequest + (*Property)(nil), // 16: odc.Property + (*SetPropertiesRequest)(nil), // 17: odc.SetPropertiesRequest + (*ConfigureRequest)(nil), // 18: odc.ConfigureRequest + (*StartRequest)(nil), // 19: odc.StartRequest + (*StopRequest)(nil), // 20: odc.StopRequest + (*ResetRequest)(nil), // 21: odc.ResetRequest + (*TerminateRequest)(nil), // 22: odc.TerminateRequest + (*StatusRequest)(nil), // 23: odc.StatusRequest +} +var file_protos_odc_proto_depIdxs = []int32{ + 0, // 0: odc.GeneralReply.status:type_name -> odc.ReplyStatus + 2, // 1: odc.GeneralReply.error:type_name -> odc.Error + 3, // 2: odc.StateReply.reply:type_name -> odc.GeneralReply + 4, // 3: odc.StateReply.devices:type_name -> odc.Device + 5, // 4: odc.StateReply.collections:type_name -> odc.Collection + 1, // 5: odc.PartitionStatus.status:type_name -> odc.SessionStatus + 0, // 6: odc.StatusReply.status:type_name -> odc.ReplyStatus + 2, // 7: odc.StatusReply.error:type_name -> odc.Error + 8, // 8: odc.StatusReply.partitions:type_name -> odc.PartitionStatus + 16, // 9: odc.SetPropertiesRequest.properties:type_name -> odc.Property + 6, // 10: odc.ConfigureRequest.request:type_name -> odc.StateRequest + 6, // 11: odc.StartRequest.request:type_name -> odc.StateRequest + 6, // 12: odc.StopRequest.request:type_name -> odc.StateRequest + 6, // 13: odc.ResetRequest.request:type_name -> odc.StateRequest + 6, // 14: odc.TerminateRequest.request:type_name -> odc.StateRequest + 10, // 15: odc.ODC.Initialize:input_type -> odc.InitializeRequest + 11, // 16: odc.ODC.Submit:input_type -> odc.SubmitRequest + 12, // 17: odc.ODC.Activate:input_type -> odc.ActivateRequest + 13, // 18: odc.ODC.Run:input_type -> odc.RunRequest + 14, // 19: odc.ODC.Update:input_type -> odc.UpdateRequest + 18, // 20: odc.ODC.Configure:input_type -> odc.ConfigureRequest + 17, // 21: odc.ODC.SetProperties:input_type -> odc.SetPropertiesRequest + 6, // 22: odc.ODC.GetState:input_type -> odc.StateRequest + 19, // 23: odc.ODC.Start:input_type -> odc.StartRequest + 20, // 24: odc.ODC.Stop:input_type -> odc.StopRequest + 21, // 25: odc.ODC.Reset:input_type -> odc.ResetRequest + 22, // 26: odc.ODC.Terminate:input_type -> odc.TerminateRequest + 15, // 27: odc.ODC.Shutdown:input_type -> odc.ShutdownRequest + 23, // 28: odc.ODC.Status:input_type -> odc.StatusRequest + 3, // 29: odc.ODC.Initialize:output_type -> odc.GeneralReply + 3, // 30: odc.ODC.Submit:output_type -> odc.GeneralReply + 3, // 31: odc.ODC.Activate:output_type -> odc.GeneralReply + 3, // 32: odc.ODC.Run:output_type -> odc.GeneralReply + 3, // 33: odc.ODC.Update:output_type -> odc.GeneralReply + 7, // 34: odc.ODC.Configure:output_type -> odc.StateReply + 3, // 35: odc.ODC.SetProperties:output_type -> odc.GeneralReply + 7, // 36: odc.ODC.GetState:output_type -> odc.StateReply + 7, // 37: odc.ODC.Start:output_type -> odc.StateReply + 7, // 38: odc.ODC.Stop:output_type -> odc.StateReply + 7, // 39: odc.ODC.Reset:output_type -> odc.StateReply + 7, // 40: odc.ODC.Terminate:output_type -> odc.StateReply + 3, // 41: odc.ODC.Shutdown:output_type -> odc.GeneralReply + 9, // 42: odc.ODC.Status:output_type -> odc.StatusReply + 29, // [29:43] is the sub-list for method output_type + 15, // [15:29] is the sub-list for method input_type + 15, // [15:15] is the sub-list for extension type_name + 15, // [15:15] is the sub-list for extension extendee + 0, // [0:15] is the sub-list for field type_name +} + +func init() { file_protos_odc_proto_init() } +func file_protos_odc_proto_init() { + if File_protos_odc_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_protos_odc_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Error); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_odc_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GeneralReply); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_odc_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Device); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_odc_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Collection); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_odc_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StateRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_odc_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StateReply); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_odc_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PartitionStatus); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_odc_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StatusReply); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_odc_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*InitializeRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_odc_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SubmitRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_odc_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ActivateRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_odc_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RunRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_odc_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UpdateRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_odc_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ShutdownRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_odc_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Property); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_odc_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SetPropertiesRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_odc_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ConfigureRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_odc_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StartRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_odc_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StopRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_odc_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ResetRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_odc_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TerminateRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_odc_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StatusRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_protos_odc_proto_rawDesc, + NumEnums: 2, + NumMessages: 22, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_protos_odc_proto_goTypes, + DependencyIndexes: file_protos_odc_proto_depIdxs, + EnumInfos: file_protos_odc_proto_enumTypes, + MessageInfos: file_protos_odc_proto_msgTypes, + }.Build() + File_protos_odc_proto = out.File + file_protos_odc_proto_rawDesc = nil + file_protos_odc_proto_goTypes = nil + file_protos_odc_proto_depIdxs = nil +} diff --git a/core/integration/odc/protos/odc.proto b/core/integration/odc/protos/odc.proto new file mode 100644 index 000000000..6caa07a76 --- /dev/null +++ b/core/integration/odc/protos/odc.proto @@ -0,0 +1,239 @@ +syntax = "proto3"; + +package odc; +option go_package = "github.com/AliceO2Group/Control/core/integration/odc/protos;odc"; + +// The ODC service definition. +// For details also see https://github.com/FairRootGroup/ODC#command-mapping +service ODC { + // Creates a new DDS session or attaches to an existing DDS session. + rpc Initialize (InitializeRequest) returns (GeneralReply) {} + // Submits DDS agents (deploys a dynamic cluster) according to a specified computing resources. + // Can be called multiple times in order to submit more DDS agents (allocate more resources). + rpc Submit (SubmitRequest) returns (GeneralReply) {} + // Activates a given topology. + rpc Activate (ActivateRequest) returns (GeneralReply) {} + // Run request combines Initialize, Submit and Activate into a single request. + // Run request always creates a new DDS session. + rpc Run (RunRequest) returns (GeneralReply) {} + // Updates a topology (up or down scale number of tasks or any other topology change). + // It consists of 3 commands: Reset, Activate and Configure. + // Can be called multiple times. + rpc Update (UpdateRequest) returns (GeneralReply) {} + // Transitions devices into Ready state. + rpc Configure (ConfigureRequest) returns (StateReply) {} + // Changes devices configuration. + rpc SetProperties (SetPropertiesRequest) returns (GeneralReply) {} + // Get current aggregated state of devices. + rpc GetState (StateRequest) returns (StateReply) {} + // Transition devices into Running state. + rpc Start (StartRequest) returns (StateReply) {} + // Transitions devices into Ready state. + rpc Stop (StopRequest) returns (StateReply) {} + // Transitions devices into Idle state. + rpc Reset (ResetRequest) returns (StateReply) {} + // Shuts devices down via End transition. + rpc Terminate (TerminateRequest) returns (StateReply) {} + // Shutdown DDS session. + rpc Shutdown (ShutdownRequest) returns (GeneralReply) {} + // Status request. + rpc Status (StatusRequest) returns (StatusReply) {} +} + +// Request status +enum ReplyStatus { + UNKNOWN = 0; // Status is unknown + SUCCESS = 1; // Request performed successfully + ERROR = 2; // Failed to perform request +} + +// Session status as defined by DDS +enum SessionStatus { + UNKNOWN_ = 0; // Status is unknown + RUNNING = 1; // DDS session is running + STOPPED = 2; // DDS session is stopped +} + +// General error +message Error { + string msg = 1; // Detailed error message + int32 code = 2; // Error code. + // TODO: Make error codes specific for each concrete request and/or error type. + // TODO: Add link to a documented error codes. +} + +// General reply to requests +message GeneralReply { + string msg = 1; // Detailed reply message + ReplyStatus status = 2; // Request status code (UNKNOWN, SUCCESS, ERROR) + Error error = 3; // If status is ERROR than this field contains error description otherwise it's empty + int32 exectime = 4; // Request execution time in ms + string partitionid = 5; // Partition ID from ECS + string sessionid = 6; // DDS session ID + string state = 7; // If successful and applicable to a request then contains an aggregated FairMQ device state, otherwise UNDEFINED. + uint64 runnr = 8; // Run number from ECS (optional) + repeated string hosts = 9; // Where applicable, provides a list of used hosts (Submit/Run requests) + string rmsjobids = 10; // Where applicable, provides a list of job IDs from the resource management system +} + +// Device information +// Runtime task ID and path are the same as in DDS. +// To get task details use DDS Topology API. +message Device { + uint64 id = 1; // Runtime task ID (same as in DDS) + string state = 2; // FairMQ device state as string + string path = 3; // Runtime task path (same as in DDS) + bool ignored = 4; // Device was stopped and set to be ignored + string host = 5; // Host where the task runs + bool expendable = 6; // Device is expendable + string rmsjobid = 7; // job IDs from the resource management system +} + +//Collection information +message Collection { + uint64 id = 1; // Runtime collection ID (same as in DDS) + string state = 2; // Aggregated state as string + string path = 3; // Path in the topology + string host = 5; // Host where the collection runs +} + +// Device change/get state request +message StateRequest { + string partitionid = 1; // Partition ID from ECS + uint64 runnr = 4; // Run number from ECS + uint32 timeout = 5; // Request timeout in sec. If not set or 0 than default is used. + string path = 2; // Task path in the DDS topology. Can be a regular expression. + bool detailed = 3; // If true then a list of affected devices is populated in the reply. +} + +// Device change/get state reply +message StateReply { + GeneralReply reply = 1; // General reply. See GeneralReply message for details. + repeated Device devices = 2; // If detailed reply is requested then this field contains a list of affected devices otherwise it's empty. + repeated Collection collections = 3; // If detailed reply is requested then this field contains a list of affected collections otherwise it's empty. +} + +// Status of each partition +message PartitionStatus { + string partitionid = 1; // Partition ID from ECS + uint64 runnr = 5; // Run number from ECS + uint32 timeout = 6; // Request timeout in sec. If not set or 0 than default is used. + string sessionid = 2; // DDS session ID + SessionStatus status = 3; // DDS session status + string state = 4; // If successful and applicable to a request then contains an aggregated FairMQ device state, otherwise UNDEFINED. +} + +// ODC status reply +message StatusReply { + string msg = 1; // Detailed reply message + ReplyStatus status = 2; // Request status code (UNKNOWN, SUCCESS, ERROR) + Error error = 3; // If status is ERROR than this field contains error description otherwise it's empty + int32 exectime = 4; // Request execution time in ms + repeated PartitionStatus partitions = 5; // Status of each partition +} + +// Initialize request +message InitializeRequest { + string partitionid = 1; // Partition ID from ECS + uint64 runnr = 3; // Run number from ECS + uint32 timeout = 4; // Request timeout in sec. If not set or 0 than default is used. + string sessionid = 2; // DDS session ID. If session ID is provided that ODC connects to an existing DDS session. If session ID is an empty string that a new DDS session is created. +} + +// Submit request +message SubmitRequest { + string partitionid = 1; // Partition ID from ECS + uint64 runnr = 4; // Run number from ECS + uint32 timeout = 5; // Request timeout in sec. If not set or 0 than default is used. + string plugin = 2; // Name of the resource plugin registered in odc-server + string resources = 3; // Resource description +} + +// Activate request +message ActivateRequest { + string partitionid = 1; // Partition ID from ECS + uint64 runnr = 5; // Run number from ECS + uint32 timeout = 6; // Request timeout in sec. If not set or 0 than default is used. + // Either `topology`, `content` or `script` has to be set. If all or none is set then an error is returned. + string topology = 2; // Filepath to the XML DDS topology file + string content = 3; // Content of the XML DDS topology + string script = 4; // Shell commands to be executed by ODC in order to generate content of the XML DDS topology +} + +// Run request +message RunRequest { + string partitionid = 1; // Partition ID from ECS + uint64 runnr = 7; // Run number from ECS + uint32 timeout = 8; // Request timeout in sec. If not set or 0 than default is used. + // Either `topology`, `content` or `script` has to be set. If all or none is set then an error is returned. + string topology = 2; // Filepath to the XML DDS topology file + string content = 5; // Content of the XML DDS topology + string script = 6; // Shell commands to be executed by ODC in order to generate content of the XML DDS topology + string plugin = 3; // Name of the resource plugin registered in odc-server + string resources = 4; // Resource description + bool extractTopoResources = 9; // extract required resources from the topology file only (plugin & resources fields are ignored) +} + +// Update request +message UpdateRequest { + string partitionid = 1; // Partition ID from ECS + uint64 runnr = 5; // Run number from ECS + uint32 timeout = 6; // Request timeout in sec. If not set or 0 than default is used. + // Either `topology`, `content` or `script` has to be set. If all or none is set then an error is returned. + string topology = 2; // Filepath to the XML DDS topology file + string content = 3; // Content of the XML DDS topology + string script = 4; // Shell commands to be executed by ODC in order to generate content of the XML DDS topology +} + +// Shutdown request +message ShutdownRequest { + string partitionid = 1; // Partition ID from ECS + uint64 runnr = 2; // Run number from ECS + uint32 timeout = 3; // Request timeout in sec. If not set or 0 than default is used. +} + +// Key-Value property +message Property { + string key = 1; // Property key + string value = 2; // Property value +} + +// Set properties request +message SetPropertiesRequest { + string partitionid = 1; // Partition ID from ECS + uint64 runnr = 4; // Run number from ECS + uint32 timeout = 5; // Request timeout in sec. If not set or 0 than default is used. + string path = 2; // Task path in the DDS topology. Can be a regular expression. + repeated Property properties = 3; // List of properties to be set +} + +// Configure request +message ConfigureRequest { + StateRequest request = 1; // State change request. See StateRequest for details. +} + +// Start request +message StartRequest { + StateRequest request = 1; // State change request. See StateRequest for details. +} + +// Stop request +message StopRequest { + StateRequest request = 1; // State change request. See StateRequest for details. +} + +// Reset request +message ResetRequest { + StateRequest request = 1; // State change request. See StateRequest for details. +} + +// Terminate request +message TerminateRequest { + StateRequest request = 1; // State change request. See StateRequest for details. +} + +// Status request +message StatusRequest { + bool running = 1; // Select only running DDS sessions +} + diff --git a/core/integration/odc/protos/odc_grpc.pb.go b/core/integration/odc/protos/odc_grpc.pb.go new file mode 100644 index 000000000..6603ec2a2 --- /dev/null +++ b/core/integration/odc/protos/odc_grpc.pb.go @@ -0,0 +1,624 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.3.0 +// - protoc v5.29.3 +// source: protos/odc.proto + +package odc + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +const ( + ODC_Initialize_FullMethodName = "/odc.ODC/Initialize" + ODC_Submit_FullMethodName = "/odc.ODC/Submit" + ODC_Activate_FullMethodName = "/odc.ODC/Activate" + ODC_Run_FullMethodName = "/odc.ODC/Run" + ODC_Update_FullMethodName = "/odc.ODC/Update" + ODC_Configure_FullMethodName = "/odc.ODC/Configure" + ODC_SetProperties_FullMethodName = "/odc.ODC/SetProperties" + ODC_GetState_FullMethodName = "/odc.ODC/GetState" + ODC_Start_FullMethodName = "/odc.ODC/Start" + ODC_Stop_FullMethodName = "/odc.ODC/Stop" + ODC_Reset_FullMethodName = "/odc.ODC/Reset" + ODC_Terminate_FullMethodName = "/odc.ODC/Terminate" + ODC_Shutdown_FullMethodName = "/odc.ODC/Shutdown" + ODC_Status_FullMethodName = "/odc.ODC/Status" +) + +// ODCClient is the client API for ODC service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type ODCClient interface { + // Creates a new DDS session or attaches to an existing DDS session. + Initialize(ctx context.Context, in *InitializeRequest, opts ...grpc.CallOption) (*GeneralReply, error) + // Submits DDS agents (deploys a dynamic cluster) according to a specified computing resources. + // Can be called multiple times in order to submit more DDS agents (allocate more resources). + Submit(ctx context.Context, in *SubmitRequest, opts ...grpc.CallOption) (*GeneralReply, error) + // Activates a given topology. + Activate(ctx context.Context, in *ActivateRequest, opts ...grpc.CallOption) (*GeneralReply, error) + // Run request combines Initialize, Submit and Activate into a single request. + // Run request always creates a new DDS session. + Run(ctx context.Context, in *RunRequest, opts ...grpc.CallOption) (*GeneralReply, error) + // Updates a topology (up or down scale number of tasks or any other topology change). + // It consists of 3 commands: Reset, Activate and Configure. + // Can be called multiple times. + Update(ctx context.Context, in *UpdateRequest, opts ...grpc.CallOption) (*GeneralReply, error) + // Transitions devices into Ready state. + Configure(ctx context.Context, in *ConfigureRequest, opts ...grpc.CallOption) (*StateReply, error) + // Changes devices configuration. + SetProperties(ctx context.Context, in *SetPropertiesRequest, opts ...grpc.CallOption) (*GeneralReply, error) + // Get current aggregated state of devices. + GetState(ctx context.Context, in *StateRequest, opts ...grpc.CallOption) (*StateReply, error) + // Transition devices into Running state. + Start(ctx context.Context, in *StartRequest, opts ...grpc.CallOption) (*StateReply, error) + // Transitions devices into Ready state. + Stop(ctx context.Context, in *StopRequest, opts ...grpc.CallOption) (*StateReply, error) + // Transitions devices into Idle state. + Reset(ctx context.Context, in *ResetRequest, opts ...grpc.CallOption) (*StateReply, error) + // Shuts devices down via End transition. + Terminate(ctx context.Context, in *TerminateRequest, opts ...grpc.CallOption) (*StateReply, error) + // Shutdown DDS session. + Shutdown(ctx context.Context, in *ShutdownRequest, opts ...grpc.CallOption) (*GeneralReply, error) + // Status request. + Status(ctx context.Context, in *StatusRequest, opts ...grpc.CallOption) (*StatusReply, error) +} + +type oDCClient struct { + cc grpc.ClientConnInterface +} + +func NewODCClient(cc grpc.ClientConnInterface) ODCClient { + return &oDCClient{cc} +} + +func (c *oDCClient) Initialize(ctx context.Context, in *InitializeRequest, opts ...grpc.CallOption) (*GeneralReply, error) { + out := new(GeneralReply) + err := c.cc.Invoke(ctx, ODC_Initialize_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *oDCClient) Submit(ctx context.Context, in *SubmitRequest, opts ...grpc.CallOption) (*GeneralReply, error) { + out := new(GeneralReply) + err := c.cc.Invoke(ctx, ODC_Submit_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *oDCClient) Activate(ctx context.Context, in *ActivateRequest, opts ...grpc.CallOption) (*GeneralReply, error) { + out := new(GeneralReply) + err := c.cc.Invoke(ctx, ODC_Activate_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *oDCClient) Run(ctx context.Context, in *RunRequest, opts ...grpc.CallOption) (*GeneralReply, error) { + out := new(GeneralReply) + err := c.cc.Invoke(ctx, ODC_Run_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *oDCClient) Update(ctx context.Context, in *UpdateRequest, opts ...grpc.CallOption) (*GeneralReply, error) { + out := new(GeneralReply) + err := c.cc.Invoke(ctx, ODC_Update_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *oDCClient) Configure(ctx context.Context, in *ConfigureRequest, opts ...grpc.CallOption) (*StateReply, error) { + out := new(StateReply) + err := c.cc.Invoke(ctx, ODC_Configure_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *oDCClient) SetProperties(ctx context.Context, in *SetPropertiesRequest, opts ...grpc.CallOption) (*GeneralReply, error) { + out := new(GeneralReply) + err := c.cc.Invoke(ctx, ODC_SetProperties_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *oDCClient) GetState(ctx context.Context, in *StateRequest, opts ...grpc.CallOption) (*StateReply, error) { + out := new(StateReply) + err := c.cc.Invoke(ctx, ODC_GetState_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *oDCClient) Start(ctx context.Context, in *StartRequest, opts ...grpc.CallOption) (*StateReply, error) { + out := new(StateReply) + err := c.cc.Invoke(ctx, ODC_Start_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *oDCClient) Stop(ctx context.Context, in *StopRequest, opts ...grpc.CallOption) (*StateReply, error) { + out := new(StateReply) + err := c.cc.Invoke(ctx, ODC_Stop_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *oDCClient) Reset(ctx context.Context, in *ResetRequest, opts ...grpc.CallOption) (*StateReply, error) { + out := new(StateReply) + err := c.cc.Invoke(ctx, ODC_Reset_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *oDCClient) Terminate(ctx context.Context, in *TerminateRequest, opts ...grpc.CallOption) (*StateReply, error) { + out := new(StateReply) + err := c.cc.Invoke(ctx, ODC_Terminate_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *oDCClient) Shutdown(ctx context.Context, in *ShutdownRequest, opts ...grpc.CallOption) (*GeneralReply, error) { + out := new(GeneralReply) + err := c.cc.Invoke(ctx, ODC_Shutdown_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *oDCClient) Status(ctx context.Context, in *StatusRequest, opts ...grpc.CallOption) (*StatusReply, error) { + out := new(StatusReply) + err := c.cc.Invoke(ctx, ODC_Status_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// ODCServer is the server API for ODC service. +// All implementations should embed UnimplementedODCServer +// for forward compatibility +type ODCServer interface { + // Creates a new DDS session or attaches to an existing DDS session. + Initialize(context.Context, *InitializeRequest) (*GeneralReply, error) + // Submits DDS agents (deploys a dynamic cluster) according to a specified computing resources. + // Can be called multiple times in order to submit more DDS agents (allocate more resources). + Submit(context.Context, *SubmitRequest) (*GeneralReply, error) + // Activates a given topology. + Activate(context.Context, *ActivateRequest) (*GeneralReply, error) + // Run request combines Initialize, Submit and Activate into a single request. + // Run request always creates a new DDS session. + Run(context.Context, *RunRequest) (*GeneralReply, error) + // Updates a topology (up or down scale number of tasks or any other topology change). + // It consists of 3 commands: Reset, Activate and Configure. + // Can be called multiple times. + Update(context.Context, *UpdateRequest) (*GeneralReply, error) + // Transitions devices into Ready state. + Configure(context.Context, *ConfigureRequest) (*StateReply, error) + // Changes devices configuration. + SetProperties(context.Context, *SetPropertiesRequest) (*GeneralReply, error) + // Get current aggregated state of devices. + GetState(context.Context, *StateRequest) (*StateReply, error) + // Transition devices into Running state. + Start(context.Context, *StartRequest) (*StateReply, error) + // Transitions devices into Ready state. + Stop(context.Context, *StopRequest) (*StateReply, error) + // Transitions devices into Idle state. + Reset(context.Context, *ResetRequest) (*StateReply, error) + // Shuts devices down via End transition. + Terminate(context.Context, *TerminateRequest) (*StateReply, error) + // Shutdown DDS session. + Shutdown(context.Context, *ShutdownRequest) (*GeneralReply, error) + // Status request. + Status(context.Context, *StatusRequest) (*StatusReply, error) +} + +// UnimplementedODCServer should be embedded to have forward compatible implementations. +type UnimplementedODCServer struct { +} + +func (UnimplementedODCServer) Initialize(context.Context, *InitializeRequest) (*GeneralReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method Initialize not implemented") +} +func (UnimplementedODCServer) Submit(context.Context, *SubmitRequest) (*GeneralReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method Submit not implemented") +} +func (UnimplementedODCServer) Activate(context.Context, *ActivateRequest) (*GeneralReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method Activate not implemented") +} +func (UnimplementedODCServer) Run(context.Context, *RunRequest) (*GeneralReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method Run not implemented") +} +func (UnimplementedODCServer) Update(context.Context, *UpdateRequest) (*GeneralReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method Update not implemented") +} +func (UnimplementedODCServer) Configure(context.Context, *ConfigureRequest) (*StateReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method Configure not implemented") +} +func (UnimplementedODCServer) SetProperties(context.Context, *SetPropertiesRequest) (*GeneralReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method SetProperties not implemented") +} +func (UnimplementedODCServer) GetState(context.Context, *StateRequest) (*StateReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetState not implemented") +} +func (UnimplementedODCServer) Start(context.Context, *StartRequest) (*StateReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method Start not implemented") +} +func (UnimplementedODCServer) Stop(context.Context, *StopRequest) (*StateReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method Stop not implemented") +} +func (UnimplementedODCServer) Reset(context.Context, *ResetRequest) (*StateReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method Reset not implemented") +} +func (UnimplementedODCServer) Terminate(context.Context, *TerminateRequest) (*StateReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method Terminate not implemented") +} +func (UnimplementedODCServer) Shutdown(context.Context, *ShutdownRequest) (*GeneralReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method Shutdown not implemented") +} +func (UnimplementedODCServer) Status(context.Context, *StatusRequest) (*StatusReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method Status not implemented") +} + +// UnsafeODCServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to ODCServer will +// result in compilation errors. +type UnsafeODCServer interface { + mustEmbedUnimplementedODCServer() +} + +func RegisterODCServer(s grpc.ServiceRegistrar, srv ODCServer) { + s.RegisterService(&ODC_ServiceDesc, srv) +} + +func _ODC_Initialize_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(InitializeRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ODCServer).Initialize(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: ODC_Initialize_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ODCServer).Initialize(ctx, req.(*InitializeRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _ODC_Submit_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SubmitRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ODCServer).Submit(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: ODC_Submit_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ODCServer).Submit(ctx, req.(*SubmitRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _ODC_Activate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ActivateRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ODCServer).Activate(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: ODC_Activate_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ODCServer).Activate(ctx, req.(*ActivateRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _ODC_Run_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RunRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ODCServer).Run(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: ODC_Run_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ODCServer).Run(ctx, req.(*RunRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _ODC_Update_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UpdateRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ODCServer).Update(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: ODC_Update_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ODCServer).Update(ctx, req.(*UpdateRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _ODC_Configure_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ConfigureRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ODCServer).Configure(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: ODC_Configure_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ODCServer).Configure(ctx, req.(*ConfigureRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _ODC_SetProperties_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SetPropertiesRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ODCServer).SetProperties(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: ODC_SetProperties_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ODCServer).SetProperties(ctx, req.(*SetPropertiesRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _ODC_GetState_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(StateRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ODCServer).GetState(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: ODC_GetState_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ODCServer).GetState(ctx, req.(*StateRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _ODC_Start_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(StartRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ODCServer).Start(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: ODC_Start_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ODCServer).Start(ctx, req.(*StartRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _ODC_Stop_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(StopRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ODCServer).Stop(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: ODC_Stop_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ODCServer).Stop(ctx, req.(*StopRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _ODC_Reset_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ResetRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ODCServer).Reset(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: ODC_Reset_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ODCServer).Reset(ctx, req.(*ResetRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _ODC_Terminate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(TerminateRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ODCServer).Terminate(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: ODC_Terminate_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ODCServer).Terminate(ctx, req.(*TerminateRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _ODC_Shutdown_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ShutdownRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ODCServer).Shutdown(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: ODC_Shutdown_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ODCServer).Shutdown(ctx, req.(*ShutdownRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _ODC_Status_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(StatusRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ODCServer).Status(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: ODC_Status_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ODCServer).Status(ctx, req.(*StatusRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// ODC_ServiceDesc is the grpc.ServiceDesc for ODC service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var ODC_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "odc.ODC", + HandlerType: (*ODCServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Initialize", + Handler: _ODC_Initialize_Handler, + }, + { + MethodName: "Submit", + Handler: _ODC_Submit_Handler, + }, + { + MethodName: "Activate", + Handler: _ODC_Activate_Handler, + }, + { + MethodName: "Run", + Handler: _ODC_Run_Handler, + }, + { + MethodName: "Update", + Handler: _ODC_Update_Handler, + }, + { + MethodName: "Configure", + Handler: _ODC_Configure_Handler, + }, + { + MethodName: "SetProperties", + Handler: _ODC_SetProperties_Handler, + }, + { + MethodName: "GetState", + Handler: _ODC_GetState_Handler, + }, + { + MethodName: "Start", + Handler: _ODC_Start_Handler, + }, + { + MethodName: "Stop", + Handler: _ODC_Stop_Handler, + }, + { + MethodName: "Reset", + Handler: _ODC_Reset_Handler, + }, + { + MethodName: "Terminate", + Handler: _ODC_Terminate_Handler, + }, + { + MethodName: "Shutdown", + Handler: _ODC_Shutdown_Handler, + }, + { + MethodName: "Status", + Handler: _ODC_Status_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "protos/odc.proto", +} diff --git a/core/integration/plugin.go b/core/integration/plugin.go new file mode 100644 index 000000000..48e87626b --- /dev/null +++ b/core/integration/plugin.go @@ -0,0 +1,249 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2021 CERN and copyright holders of ALICE O². + * Author: Teo Mrnjavac + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +// Package integration provides the plugin system for integrating O² Control +// with external services like DCS, Bookkeeping, ODC, and other ALICE systems. +package integration + +import ( + "context" + "sync" + "time" + + "github.com/AliceO2Group/Control/common/logger" + "github.com/AliceO2Group/Control/common/monitoring" + "github.com/AliceO2Group/Control/common/utils/uid" + "github.com/sirupsen/logrus" + "github.com/spf13/viper" +) + +var log = logger.New(logrus.StandardLogger(), "integration") + +var ( + once sync.Once + instance Plugins + + loaderOnce sync.Once + pluginLoaders map[string]func() Plugin +) + +type Plugins []Plugin + +type Plugin interface { + GetName() string + GetPrettyName() string + GetEndpoint() string + GetConnectionState() string + GetData(argv []any) string + GetEnvironmentsData(envIds []uid.ID) map[uid.ID]string + GetEnvironmentsShortData(envIds []uid.ID) map[uid.ID]string + + Init(instanceId string) error + CallStack(data interface{}) map[string]interface{} // used in hook call context + ObjectStack(varStack map[string]string, baseConfigStack map[string]string) map[string]interface{} // all other ProcessTemplates contexts + Destroy() error +} + +type NewFunc func(endpoint string) Plugin + +func RegisteredPlugins() map[string]func() Plugin { + return pluginLoaders +} + +func RegisterPlugin(pluginName string, endpointArgumentName string, newFunc NewFunc) { + loaderOnce.Do(func() { + pluginLoaders = make(map[string]func() Plugin) + }) + pluginLoaders[pluginName] = func() Plugin { + if viper.IsSet(endpointArgumentName) { + endpoint := viper.GetString(endpointArgumentName) + return newFunc(endpoint) + } + return nil + } +} + +func (p Plugins) InitAll(fid string) { + wg := &sync.WaitGroup{} + wg.Add(len(p)) + for _, plugin := range p { + go func(plugin Plugin) { + defer wg.Done() + initErr := plugin.Init(fid) + if initErr != nil { + log.WithError(initErr). + WithField("plugin", plugin.GetName()). + Error("workflow plugin failed to initialize") + } + }(plugin) + } + wg.Wait() +} + +func (p Plugins) DestroyAll() { + for _, plugin := range p { + err := plugin.Destroy() + if err != nil { + log.WithError(err). + WithField("plugin", plugin.GetName()). + Error("workflow plugin failed to destroy") + } + } +} + +func (p Plugins) CallStack(data interface{}) (stack map[string]interface{}) { + stack = make(map[string]interface{}) + for _, plugin := range p { + s := plugin.CallStack(data) + stack[plugin.GetName()] = s + } + return +} + +func (p Plugins) ObjectStack(varStack map[string]string, baseConfigStack map[string]string) (stack map[string]interface{}) { + stack = make(map[string]interface{}) + + // HACK: this is a dummy object+function to allow odc.GenerateEPNTopologyFullname in the root role + stack["odc"] = map[string]interface{}{ + "GenerateEPNTopologyFullname": func() string { + return "" + }, + } + + for _, plugin := range p { + s := plugin.ObjectStack(varStack, baseConfigStack) + stack[plugin.GetName()] = s + } + return +} + +func (p Plugins) GetData(argv []any) (data map[string]string) { + data = make(map[string]string) + for _, plugin := range p { + data[plugin.GetName()] = plugin.GetData(argv) + } + return +} + +func (p Plugins) GetEnvironmentsData(envIds []uid.ID) (data map[uid.ID]map[string]string) { + data = make(map[uid.ID]map[string]string) + + // First we query each plugin for environment data of all envIds at once + pluginEnvData := make(map[ /*plugin*/ string]map[uid.ID]string) + for _, plugin := range p { + pluginEnvData[plugin.GetName()] = plugin.GetEnvironmentsData(envIds) + } + + // Then we invert the nested map to get a map of envId -> plugin -> data + for _, plugin := range p { + for envId, pluginData := range pluginEnvData[plugin.GetName()] { + if _, ok := data[envId]; !ok { + data[envId] = make(map[string]string) + } + data[envId][plugin.GetName()] = pluginData + } + } + return +} + +func (p Plugins) GetEnvironmentsShortData(envIds []uid.ID) (data map[uid.ID]map[string]string) { + data = make(map[uid.ID]map[string]string) + + // First we query each plugin for environment data of all envIds at once + pluginEnvData := make(map[ /*plugin*/ string]map[uid.ID]string) + for _, plugin := range p { + pluginEnvData[plugin.GetName()] = plugin.GetEnvironmentsShortData(envIds) + } + + // Then we invert the nested map to get a map of envId -> plugin -> data + for _, plugin := range p { + for envId, pluginData := range pluginEnvData[plugin.GetName()] { + if _, ok := data[envId]; !ok { + data[envId] = make(map[string]string) + } + data[envId][plugin.GetName()] = pluginData + } + } + return +} + +func PluginsInstance() Plugins { + once.Do(func() { + instance = Plugins{} + pluginList := viper.GetStringSlice("integrationPlugins") + + for _, pluginName := range pluginList { + if pluginLoaders == nil { + log.WithField("plugin", pluginName). + Error("requested plugin unavailable") + continue + } + pluginLoader, ok := pluginLoaders[pluginName] + if !ok { + log.WithField("plugin", pluginName). + Error("requested plugin unavailable") + continue + } + newPlugin := pluginLoader() + if newPlugin == nil { + log.WithField("plugin", pluginName). + Error("plugin loader failed") + continue + } + instance = append(instance, newPlugin) + } + }) + return instance +} + +// Reset resets the plugin system for testing purposes. +func Reset() { + once = sync.Once{} + instance = Plugins{} + loaderOnce = sync.Once{} + pluginLoaders = make(map[string]func() Plugin) +} + +func ExtractRunTypeOrUndefined(varStack map[string]string) string { + runType, ok := varStack["run_type"] + if !ok { + runType = "undefined" + } + return runType +} + +func NewContext(envId string, varStack map[string]string, timeout time.Duration) (context.Context, context.CancelFunc) { + return context.WithTimeout( + monitoring.AddEnvAndRunType(context.Background(), + envId, + ExtractRunTypeOrUndefined(varStack), + ), + timeout) +} + +func NewContextEmptyEnvIdRunType(timeout time.Duration) (context.Context, context.CancelFunc) { + return context.WithTimeout( + monitoring.AddEnvAndRunType(context.Background(), "none", "none"), + timeout) +} diff --git a/core/integration/plugin_test.go b/core/integration/plugin_test.go new file mode 100644 index 000000000..d5ed637e9 --- /dev/null +++ b/core/integration/plugin_test.go @@ -0,0 +1,229 @@ +package integration_test + +import ( + "github.com/AliceO2Group/Control/common/utils/uid" + "github.com/AliceO2Group/Control/core/integration" + "github.com/AliceO2Group/Control/core/integration/testplugin" + "github.com/AliceO2Group/Control/core/workflow/callable" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "github.com/spf13/viper" + "testing" +) + +var _ = Describe("plugin framework", Ordered, func() { + + BeforeEach(func() { + // Reset the integration package state before each test + integration.Reset() + viper.Reset() + }) + + var _ = Describe("Plugin Registration", Ordered, func() { + It("should register a plugin correctly", func() { + // ... even if we register it multiple times + integration.RegisterPlugin("testplugin", "testPluginEndpoint", testplugin.NewPlugin) + integration.RegisterPlugin("testplugin", "testPluginEndpoint", testplugin.NewPlugin) + + registeredPlugins := integration.RegisteredPlugins() + Expect(registeredPlugins).To(HaveLen(1)) + Expect(registeredPlugins).To(HaveKey("testplugin")) + + pluginLoader := registeredPlugins["testplugin"] + Expect(pluginLoader).ToNot(BeNil()) + }) + }) + + var _ = Describe("Loading Plugins", Ordered, func() { + BeforeEach(func() { + // Reset the integration package state before each test + integration.Reset() + viper.Reset() + }) + It("should load a plugin correctly", func() { + integration.RegisterPlugin("testplugin", "testPluginEndpoint", testplugin.NewPlugin) + viper.Set("testPluginEndpoint", "http://example.com") + viper.Set("integrationPlugins", []string{"testplugin"}) + plugins := integration.PluginsInstance() + Expect(plugins).To(HaveLen(1)) + Expect(plugins[0].GetName()).To(Equal("testplugin")) + }) + It("should fail to load a plugin if endpoint is not set", func() { + integration.RegisterPlugin("testplugin", "testPluginEndpoint", testplugin.NewPlugin) + viper.Set("integrationPlugins", []string{"testplugin"}) + plugins := integration.PluginsInstance() + Expect(plugins).To(BeEmpty()) + }) + It("should not load a plugin which is not registered", func() { + viper.Set("testPluginEndpoint", "http://example.com") + viper.Set("integrationPlugins", []string{"testplugin"}) + plugins := integration.PluginsInstance() + Expect(plugins).To(BeEmpty()) + }) + It("should not load a plugin which is not in the list of integration plugins", func() { + integration.RegisterPlugin("testplugin", "testPluginEndpoint", testplugin.NewPlugin) + viper.Set("testPluginEndpoint", "http://example.com") + viper.Set("integrationPlugins", []string{}) + plugins := integration.PluginsInstance() + Expect(plugins).To(BeEmpty()) + }) + }) + + Describe("Plugin Getters", Ordered, func() { + var pluginInstance integration.Plugin + + BeforeEach(func() { + integration.RegisterPlugin("testplugin", "testPluginEndpoint", testplugin.NewPlugin) + + registeredPlugins := integration.RegisteredPlugins() + pluginLoader := registeredPlugins["testplugin"] + + // Simulate setting the endpoint in Viper + viper.Set("testPluginEndpoint", "http://example.com") + pluginInstance = pluginLoader() + }) + + It("should return the correct name", func() { + Expect(pluginInstance.GetName()).To(Equal("testplugin")) + }) + + It("should return the correct pretty name", func() { + Expect(pluginInstance.GetPrettyName()).To(Equal("Test plugin")) + }) + + It("should return the correct connection state", func() { + Expect(pluginInstance.GetConnectionState()).To(Equal("READY")) + }) + It("should set and get the endpoint correctly", func() { + Expect(pluginInstance.GetEndpoint()).To(Equal("http://example.com")) + }) + + It("should return the correct data", func() { + Expect(pluginInstance.GetData(nil)).To(Equal("test_data")) + }) + + It("should return the correct environments data", func() { + envId, _ := uid.FromString("2oDvieFrVTi") + envData := pluginInstance.GetEnvironmentsData([]uid.ID{envId}) + Expect(envData).To(HaveLen(1)) + Expect(envData).To(HaveKeyWithValue(envId, "test_data_2oDvieFrVTi")) + }) + + It("should return the correct environments short data", func() { + envId, _ := uid.FromString("2oDvieFrVTi") + envData := pluginInstance.GetEnvironmentsShortData([]uid.ID{envId}) + Expect(envData).To(HaveLen(1)) + Expect(envData).To(HaveKeyWithValue(envId, "test_short_data_2oDvieFrVTi")) + }) + }) + + Describe("Global Plugin Getters", Ordered, func() { + var plugins integration.Plugins + + BeforeEach(func() { + integration.Reset() + integration.RegisterPlugin("testplugin", "testPluginEndpoint", testplugin.NewPlugin) + viper.Reset() + viper.Set("integrationPlugins", []string{"testplugin"}) + viper.Set("testPluginEndpoint", "http://example.com") + plugins = integration.PluginsInstance() + }) + It("should return the data returned by the plugin", func() { + data := plugins.GetData(nil) + Expect(data).To(HaveLen(1)) + Expect(data).To(HaveKeyWithValue("testplugin", "test_data")) + }) + + It("should return the env data returned by the plugin", func() { + envId, _ := uid.FromString("2oDvieFrVTi") + envData := plugins.GetEnvironmentsData([]uid.ID{envId}) + Expect(envData).To(HaveLen(1)) + Expect(envData).To(HaveKey(envId)) + envDataTestPlugin := envData[envId] + Expect(envDataTestPlugin).To(HaveLen(1)) + Expect(envDataTestPlugin).To(HaveKeyWithValue("testplugin", "test_data_2oDvieFrVTi")) + }) + + It("should return the short env data returned by the plugin", func() { + envId, _ := uid.FromString("2oDvieFrVTi") + envData := plugins.GetEnvironmentsShortData([]uid.ID{envId}) + Expect(envData).To(HaveLen(1)) + Expect(envData).To(HaveKey(envId)) + envDataTestPlugin := envData[envId] + Expect(envDataTestPlugin).To(HaveLen(1)) + Expect(envDataTestPlugin).To(HaveKeyWithValue("testplugin", "test_short_data_2oDvieFrVTi")) + }) + }) + + Describe("InitAll and DestroyAll", Ordered, func() { + var plugins integration.Plugins + + BeforeEach(func() { + viper.Set("integrationPlugins", []string{"testplugin"}) + viper.Set("testPluginEndpoint", "http://example.com") + integration.RegisterPlugin("testplugin", "testPluginEndpoint", testplugin.NewPlugin) + plugins = integration.PluginsInstance() + }) + + It("should initialize and destroy all plugins correctly", func() { + plugins.InitAll("instance_id") + plugins.DestroyAll() + }) + }) + + Describe("Call Stack", Ordered, func() { + var plugins integration.Plugins + + BeforeEach(func() { + viper.Reset() + viper.Set("integrationPlugins", []string{"testplugin"}) + viper.Set("testPluginEndpoint", "http://example.com") + integration.RegisterPlugin("testplugin", "testPluginEndpoint", testplugin.NewPlugin) + plugins = integration.PluginsInstance() + }) + + It("should create a call stack for all registered plugins", func() { + call := &callable.Call{ + VarStack: map[string]string{ + "environment_id": "2oDvieFrVTi", + }, + } + + callStack := plugins.CallStack(call) + + Expect(callStack).To(HaveLen(1)) + Expect(callStack).To(HaveKey("testplugin")) + + testPluginCallStack := callStack["testplugin"] + Expect(testPluginCallStack).To(HaveKey("Noop")) + Expect(testPluginCallStack).To(HaveKey("Test")) + }) + }) + + Describe("Object Stack", Ordered, func() { + var plugins integration.Plugins + + BeforeEach(func() { + viper.Reset() + viper.Set("integrationPlugins", []string{"testplugin"}) + viper.Set("testPluginEndpoint", "http://example.com") + integration.RegisterPlugin("testplugin", "testPluginEndpoint", testplugin.NewPlugin) + plugins = integration.PluginsInstance() + }) + + It("should create an object stack for all registered plugins", func() { + objectStack := plugins.ObjectStack(nil, nil) + + Expect(objectStack).To(HaveLen(2)) + Expect(objectStack).To(HaveKey("odc")) + Expect(objectStack).To(HaveKey("testplugin")) + testPluginObjectStack := objectStack["testplugin"] + Expect(testPluginObjectStack).To(HaveKeyWithValue("test", "test_data")) + }) + }) +}) + +func TestCoreIntegration(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Core Integration Test Suite") +} diff --git a/core/integration/testplugin/plugin.go b/core/integration/testplugin/plugin.go new file mode 100644 index 000000000..fe545a247 --- /dev/null +++ b/core/integration/testplugin/plugin.go @@ -0,0 +1,250 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2021 CERN and copyright holders of ALICE O². + * Author: Teo Mrnjavac + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +// Package testplugin provides a test integration plugin that serves as an example +// and is used for testing the plugin system functionality. +package testplugin + +import ( + "strconv" + "time" + + "github.com/AliceO2Group/Control/common/logger" + "github.com/AliceO2Group/Control/common/logger/infologger" + "github.com/AliceO2Group/Control/common/utils/uid" + "github.com/AliceO2Group/Control/core/integration" + "github.com/AliceO2Group/Control/core/workflow/callable" + "github.com/sirupsen/logrus" + "github.com/spf13/viper" +) + +var log = logger.New(logrus.StandardLogger(), "testplugin") + +const ( + TESTPLUGIN_GENERAL_OP_TIMEOUT = 3 * time.Second +) + +type Plugin struct { +} + +func NewPlugin(endpoint string) integration.Plugin { + log.WithField("endpoint", endpoint). + Info("initializing test plugin, DO NOT USE IN PRODUCTION") + + return &Plugin{} +} + +func (p *Plugin) GetName() string { + return "testplugin" +} + +func (p *Plugin) GetPrettyName() string { + return "Test plugin" +} + +func (p *Plugin) GetEndpoint() string { + return viper.GetString("testPluginEndpoint") +} + +func (p *Plugin) GetConnectionState() string { + return "READY" +} + +func (p *Plugin) GetData(_ []any) string { + return "test_data" +} + +func (p *Plugin) GetEnvironmentsData(envIds []uid.ID) map[uid.ID]string { + envData := make(map[uid.ID]string) + for _, envId := range envIds { + envData[envId] = "test_data_" + envId.String() + } + return envData +} + +func (p *Plugin) GetEnvironmentsShortData(envIds []uid.ID) map[uid.ID]string { + envData := make(map[uid.ID]string) + for _, envId := range envIds { + envData[envId] = "test_short_data_" + envId.String() + } + return envData +} + +func (p *Plugin) Init(_ string) error { + log.Debug("Test plugin initialized") + return nil +} + +func (p *Plugin) ObjectStack(_ map[string]string, _ map[string]string) (stack map[string]interface{}) { + stack = make(map[string]interface{}) + stack["test"] = "test_data" + return stack +} + +func (p *Plugin) CallStack(data interface{}) (stack map[string]interface{}) { + call, ok := data.(*callable.Call) + if !ok { + return + } + varStack := call.VarStack + envId, ok := varStack["environment_id"] + if !ok { + log.Error("cannot acquire environment ID") + return + } + + message, ok := varStack["testplugin_message"] + if !ok { + message = "running testplugin.Noop" + } + + doFailS, ok := varStack["testplugin_fail"] + if !ok { + doFailS = "false" + } + doFail, convErr := strconv.ParseBool(doFailS) + if convErr != nil { + doFail = false + } + + doHangS, ok := varStack["testplugin_hang"] + if !ok { + doHangS = "false" + } + doHang, convErr := strconv.ParseBool(doHangS) + if convErr != nil { + doHang = false + } + + stack = make(map[string]interface{}) + stack["Noop"] = func() (out string) { // must formally return string even when we return nothing + log.WithField("partition", envId). + WithField("level", infologger.IL_Ops). + WithField("rolepath", call.GetParentRolePath()). + WithField("trigger", call.GetTraits().Trigger). + WithField("await", call.GetTraits().Await). + Infof("executing testplugin.Noop call: %s", message) + + rn := varStack["run_number"] + var ( + runNumber64 int64 + err error + ) + runNumber64, err = strconv.ParseInt(rn, 10, 32) + if err != nil { + runNumber64 = 0 + } + + timeout := callable.AcquireTimeout(TESTPLUGIN_GENERAL_OP_TIMEOUT, varStack, "Noop", envId) + defer log.WithField("partition", envId). + WithField("level", infologger.IL_Ops). + WithField("rolepath", call.GetParentRolePath()). + WithField("trigger", call.GetTraits().Trigger). + WithField("await", call.GetTraits().Await). + WithField("run", runNumber64). + Infof("executed testplugin.Noop call in %s", timeout) + + time.Sleep(timeout) + + return + } + stack["Test"] = func() (out string) { // must formally return string even when we return nothing + log.WithField("partition", envId). + WithField("level", infologger.IL_Ops). + WithField("rolepath", call.GetParentRolePath()). + WithField("trigger", call.GetTraits().Trigger). + WithField("await", call.GetTraits().Await). + Infof("executing testplugin.Test call: %s", message) + + rolePath, ok := varStack["__call_rolepath"] + if !ok { + call.VarStack["__call_error"] = "could not get __call_rolepath" + } + + parentRole, ok := call.GetParentRole().(callable.ParentRole) + if ok { + parentRole.SetGlobalRuntimeVar(rolePath+"_called", "true") + } + + if doFail { + call.VarStack["__call_error"] = "error triggered in testplugin.Test call" + } + + if doHang { + for { + time.Sleep(time.Second) + } + } + + return + } + stack["TimestampObserver"] = func() (out string) { + rolePath, ok := varStack["__call_rolepath"] + if !ok { + call.VarStack["__call_error"] = "could not get __call_rolepath" + } + + parentRole, ok := call.GetParentRole().(callable.ParentRole) + if ok { + parentRole.SetGlobalRuntimeVar(rolePath+"_called", "true") + } else { + call.VarStack["__call_error"] = "could not get parent role" + return + } + + // check presence of the four expected run-related timestamps + for _, key := range []string{"run_start_time_ms", "run_start_completion_time_ms", "run_end_time_ms", "run_end_completion_time_ms"} { + value, ok := varStack[key] + if ok && len(value) > 0 && value != "0" { + parentRole.SetGlobalRuntimeVar(rolePath+"_saw_"+key, "true") + } + } + return + } + stack["CallOrderObserver"] = func() (out string) { + rolePath, ok := varStack["__call_rolepath"] + if !ok { + call.VarStack["__call_error"] = "could not get __call_rolepath" + } + parentRole, ok := call.GetParentRole().(callable.ParentRole) + if !ok { + call.VarStack["__call_error"] = "could not get parent role" + return + } + + callHistory, _ := varStack["call_history"] + if len(callHistory) == 0 { + parentRole.SetGlobalRuntimeVar("call_history", rolePath) + } else { + parentRole.SetGlobalRuntimeVar("call_history", callHistory+","+rolePath) + } + return + } + + return +} + +func (p *Plugin) Destroy() error { + return nil +} diff --git a/core/integration/trg/client.go b/core/integration/trg/client.go new file mode 100644 index 000000000..2ab48a006 --- /dev/null +++ b/core/integration/trg/client.go @@ -0,0 +1,160 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2021 CERN and copyright holders of ALICE O². + * Author: Miltiadis Alexis + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package trg + +import ( + "context" + "time" + + "github.com/AliceO2Group/Control/common/logger" + "github.com/AliceO2Group/Control/common/monitoring" + trgecspb "github.com/AliceO2Group/Control/core/integration/trg/protos" + "github.com/sirupsen/logrus" + "github.com/spf13/viper" + "google.golang.org/grpc" + "google.golang.org/grpc/backoff" + "google.golang.org/grpc/connectivity" + "google.golang.org/grpc/keepalive" +) + +var log = logger.New(logrus.StandardLogger(), "trgclient") + +func convertMethodName(method string) (converted string) { + switch method { + case trgecspb.CTPd_PrepareForRun_FullMethodName: + converted = "PrepareForRun" + case trgecspb.CTPd_RunLoad_FullMethodName: + converted = "RunLoad" + case trgecspb.CTPd_RunUnload_FullMethodName: + converted = "RunUnload" + case trgecspb.CTPd_RunStart_FullMethodName: + converted = "RunStart" + case trgecspb.CTPd_RunStatus_FullMethodName: + converted = "RunStatus" + case trgecspb.CTPd_RunList_FullMethodName: + converted = "RunList" + case trgecspb.CTPd_RunStop_FullMethodName: + converted = "RunStop" + case trgecspb.CTPd_RunConfig_FullMethodName: + converted = "RunConfig" + case trgecspb.CTPd_RunCleanup_FullMethodName: + converted = "RunCleanup" + case trgecspb.CTPd_TPCReset_FullMethodName: + converted = "TPCReset" + default: + converted = "Unknown" + } + return +} + +type RpcClient struct { + trgecspb.CTPdClient + conn *grpc.ClientConn +} + +func NewClient(cxt context.Context, cancel context.CancelFunc, endpoint string) *RpcClient { + log.WithFields(logrus.Fields{ + "endpoint": endpoint, + }).Debug("dialing TRG endpoint") + + dialOptions := []grpc.DialOption{ + grpc.WithInsecure(), + grpc.WithConnectParams(grpc.ConnectParams{ + Backoff: backoff.Config{ + BaseDelay: backoff.DefaultConfig.BaseDelay, + Multiplier: backoff.DefaultConfig.Multiplier, + Jitter: backoff.DefaultConfig.Jitter, + MaxDelay: 20 * time.Second, + }, + MinConnectTimeout: 10 * time.Second, + }), + grpc.WithKeepaliveParams(keepalive.ClientParameters{ + Time: 5 * time.Second, + Timeout: time.Second, + PermitWithoutStream: true, + }), + grpc.WithUnaryInterceptor(monitoring.SetupUnaryClientInterceptor("trg", convertMethodName)), + } + if !viper.GetBool("trgServiceUseSystemProxy") { + dialOptions = append(dialOptions, grpc.WithNoProxy()) + } + conn, err := grpc.DialContext(cxt, + endpoint, + dialOptions..., + ) + if err != nil { + log.WithField("error", err.Error()). + WithField("endpoint", endpoint). + Errorf("cannot dial RPC endpoint") + cancel() + return nil + } + + go func() { + connState := connectivity.Idle + stateChangedNotify := make(chan bool) + notifyFunc := func(st connectivity.State) { + ok := conn.WaitForStateChange(cxt, st) + stateChangedNotify <- ok + } + go notifyFunc(connState) + + for { + select { + case ok := <-stateChangedNotify: + if !ok { + return + } + connState = conn.GetState() + log.Debugf("TRG client %s", connState.String()) + go notifyFunc(connState) + case <-time.After(2 * time.Minute): + if conn.GetState() != connectivity.Ready { + conn.ResetConnectBackoff() + } + case <-cxt.Done(): + return + } + } + }() + + client := &RpcClient{ + CTPdClient: trgecspb.NewCTPdClient(conn), + conn: conn, + } + + return client +} + +func (m *RpcClient) GetConnState() connectivity.State { + if m.conn == nil { + return connectivity.Idle + } + return m.conn.GetState() +} + +func (m *RpcClient) Close() error { + return m.conn.Close() +} diff --git a/core/integration/trg/ctp_status_reader.go b/core/integration/trg/ctp_status_reader.go new file mode 100644 index 000000000..37de809ce --- /dev/null +++ b/core/integration/trg/ctp_status_reader.go @@ -0,0 +1,72 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2026 CERN and copyright holders of ALICE O². + * Author: Piotr Konopka + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package trg + +import ( + "context" + "fmt" + + trgpb "github.com/AliceO2Group/Control/core/integration/trg/protos" + "github.com/segmentio/kafka-go" + "github.com/spf13/viper" + "google.golang.org/protobuf/proto" +) + +// CtpStatusReader reads CTP status messages from Kafka +type CtpStatusReader struct { + *kafka.Reader +} + +// NewCtpStatusReader creates a new reader for CTP status messages +func NewCtpStatusReader(topic string, groupID string) *CtpStatusReader { + cfg := kafka.ReaderConfig{ + Brokers: viper.GetStringSlice("kafkaEndpoints"), + Topic: topic, + GroupID: groupID, + MinBytes: 1, + MaxBytes: 10e7, + } + + return &CtpStatusReader{ + Reader: kafka.NewReader(cfg), + } +} + +// Next reads the next CTP status message from Kafka +func (r *CtpStatusReader) Next(ctx context.Context) (*trgpb.Status, error) { + if r == nil { + return nil, fmt.Errorf("nil reader") + } + msg, err := r.ReadMessage(ctx) + if err != nil { + return nil, err + } + + var status trgpb.Status + if err := proto.Unmarshal(msg.Value, &status); err != nil { + return nil, fmt.Errorf("failed to unmarshal CTP status message: %w", err) + } + return &status, nil +} diff --git a/core/integration/trg/plugin.go b/core/integration/trg/plugin.go new file mode 100644 index 000000000..13eae9444 --- /dev/null +++ b/core/integration/trg/plugin.go @@ -0,0 +1,1588 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2021-2022 CERN and copyright holders of ALICE O². + * Author: Miltiadis Alexis + * Teo Mrnjavac + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +//go:generate protoc --go_out=. --go_opt=paths=source_relative --go-grpc_opt=paths=source_relative --go-grpc_out=require_unimplemented_servers=false:. protos/ctpecs.proto +//go:generate protoc --go_out=. --go_opt=paths=source_relative --go-grpc_opt=paths=source_relative --go-grpc_out=require_unimplemented_servers=false:. protos/ctp_status.proto + +// Package trg provides integration with the ALICE trigger system. +package trg + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "io" + "net/url" + "strconv" + "strings" + "sync" + "time" + + "github.com/AliceO2Group/Control/common/event/topic" + "github.com/AliceO2Group/Control/common/logger/infologger" + "github.com/AliceO2Group/Control/common/monitoring" + pb "github.com/AliceO2Group/Control/common/protos" + "github.com/AliceO2Group/Control/common/utils/uid" + "github.com/AliceO2Group/Control/core/environment" + "github.com/AliceO2Group/Control/core/integration" + trgpb "github.com/AliceO2Group/Control/core/integration/trg/protos" + "github.com/AliceO2Group/Control/core/the" + "github.com/AliceO2Group/Control/core/workflow/callable" + "github.com/spf13/viper" + "google.golang.org/grpc" + "google.golang.org/grpc/connectivity" +) + +const ( + TRG_DIAL_TIMEOUT = 2 * time.Second + TRG_PFR_TIMEOUT = 5 * time.Second + TRG_LOAD_TIMEOUT = 5 * time.Second + TRG_START_TIMEOUT = 5 * time.Second + TRG_STOP_TIMEOUT = 5 * time.Second + TRG_UNLOAD_TIMEOUT = 5 * time.Second + TRG_CLEANUP_TIMEOUT = 5 * time.Second + TOPIC = topic.IntegratedService + topic.Separator + "trg" +) + +var ctpStatusTopic topic.Topic = "ctp.status" + +type Plugin struct { + trgHost string + trgPort int + + trgClient *RpcClient + trgClientCancel context.CancelFunc + + kafkaReader *CtpStatusReader + kafkaReaderCtx context.Context + kafkaReaderCancel context.CancelFunc + + pendingRunStops map[string] /*envId*/ int64 + pendingRunUnloads map[string] /*envId*/ int64 + + cachedStatus *TrgStatus + cachedStatusMu sync.RWMutex +} + +type TrgStatus struct { + // Fields from TRG RunList queries + RunCount int `json:"runCount,omitempty"` + Lines []string `json:"lines,omitempty"` + Structured Runs `json:"structured,omitempty"` + EnvMap map[uid.ID]Run `json:"envMap,omitempty"` + + // Fields from CTP status Kafka messages + Clock string `json:"clock,omitempty"` + DetectorsInHoldover []string `json:"detectorsInHoldover,omitempty"` + ClockTransitionExpectedTime int64 `json:"clockTransitionExpectedTime,omitempty"` // nanoseconds since epoch, 0 if not expected +} + +func NewPlugin(endpoint string) integration.Plugin { + u, err := url.Parse(endpoint) + if err != nil { + log.WithField("endpoint", endpoint). + WithError(err). + Error("bad service endpoint") + return nil + } + + portNumber, _ := strconv.Atoi(u.Port()) + + return &Plugin{ + trgHost: u.Hostname(), + trgPort: portNumber, + trgClient: nil, + pendingRunStops: make(map[string]int64), + pendingRunUnloads: make(map[string]int64), + cachedStatus: &TrgStatus{}, + } +} + +func (p *Plugin) GetName() string { + return "trg" +} + +func (p *Plugin) GetPrettyName() string { + return "Trigger System" +} + +func (p *Plugin) GetEndpoint() string { + return viper.GetString("trgServiceEndpoint") +} + +func (p *Plugin) GetConnectionState() string { + if p == nil || p.trgClient == nil { + return "UNKNOWN" + } + return p.trgClient.conn.GetState().String() +} + +func (p *Plugin) queryRunList() { + ctx, cancel := integration.NewContextEmptyEnvIdRunType(viper.GetDuration("trgPollingTimeout")) + defer cancel() + + runReply, err := p.trgClient.RunList(ctx, &trgpb.Empty{}, grpc.EmptyCallOption{}) + if err != nil { + err = fmt.Errorf("error querying TRG service at %s: %w", viper.GetString("trgServiceEndpoint"), err) + log.WithError(err). + WithField("level", infologger.IL_Devel). + WithField("endpoint", viper.GetString("trgServiceEndpoint")). + WithField("call", "RunList"). + Error("TRG error") + } + if runReply == nil { + log.WithField("level", infologger.IL_Support). + WithField("endpoint", viper.GetString("trgServiceEndpoint")). + WithField("call", "RunList"). + WithError(fmt.Errorf("TRG RunList response is nil")). + Error("TRG error") + runReply = &trgpb.RunReply{} + } + + structured, errSlice := parseRunList(int(runReply.Rc), runReply.Msg) + for _, err := range errSlice { + if err != nil { + err = fmt.Errorf("error parsing response from TRG service at %s: %w", viper.GetString("trgServiceEndpoint"), err) + log.WithError(err). + WithField("level", infologger.IL_Devel). + WithField("endpoint", viper.GetString("trgServiceEndpoint")). + WithField("call", "RunList"). + Error("TRG error") + } + } + if structured == nil { + structured = make(Runs, 0) + } + + envMap := make(map[uid.ID]Run) + for envIdS, rn := range p.pendingRunStops { + var envId uid.ID + envId, err = uid.FromString(envIdS) + if err != nil { + continue + } + + for _, run := range structured { + if run.RunNumber == uint32(rn) { + envMap[envId] = run + } + } + } + for envIdS, rn := range p.pendingRunUnloads { + var envId uid.ID + envId, err = uid.FromString(envIdS) + if err != nil { + continue + } + + for _, run := range structured { + if run.RunNumber == uint32(rn) { + envMap[envId] = run + } + } + } + + p.cachedStatusMu.Lock() + p.cachedStatus.RunCount = int(runReply.Rc) + p.cachedStatus.Lines = strings.Split(runReply.Msg, "\n") + p.cachedStatus.Structured = structured + p.cachedStatus.EnvMap = envMap + p.cachedStatusMu.Unlock() +} + +func (p *Plugin) GetData(_ []any) string { + if p == nil || p.trgClient == nil { + return "" + } + + p.cachedStatusMu.RLock() + r := p.cachedStatus + if r == nil { + p.cachedStatusMu.RUnlock() + return "" + } + + out, err := json.Marshal(r) + p.cachedStatusMu.RUnlock() + + if err != nil { + return "" + } + return string(out[:]) +} + +func (p *Plugin) GetEnvironmentsData(envIds []uid.ID) map[uid.ID]string { + if p == nil || p.trgClient == nil { + return nil + } + + p.cachedStatusMu.RLock() + defer p.cachedStatusMu.RUnlock() + + out := make(map[uid.ID]string) + envMap := p.cachedStatus.EnvMap + for _, envId := range envIds { + if run, ok := envMap[envId]; !ok { + runOut, err := json.Marshal(run) + if err != nil { + continue + } + out[envId] = string(runOut[:]) + } + } + + return out +} + +func (p *Plugin) GetEnvironmentsShortData(envIds []uid.ID) map[uid.ID]string { + return p.GetEnvironmentsData(envIds) +} + +func (p *Plugin) Init(instanceId string) error { + if p.trgClient == nil { + callTimeout := TRG_DIAL_TIMEOUT + cxt, cancel := context.WithTimeout(context.Background(), callTimeout) + p.trgClient = NewClient(cxt, cancel, viper.GetString("trgServiceEndpoint")) + if p.trgClient == nil { + return fmt.Errorf("failed to connect to TRG service on %s", viper.GetString("trgServiceEndpoint")) + } + } + if p.trgClient == nil { + return fmt.Errorf("failed to start TRG client on %s", viper.GetString("trgServiceEndpoint")) + } + + var ctx context.Context + ctx, p.trgClientCancel = context.WithCancel(context.Background()) + + trgPollingInterval := viper.GetDuration("trgPollingInterval") + + // polling + go func() { + for { + select { + case <-ctx.Done(): + return + case <-time.After(trgPollingInterval): + p.queryRunList() + p.reconcile() + } + } + }() + + // Initialize CTP status reader + p.kafkaReaderCtx, p.kafkaReaderCancel = context.WithCancel(context.Background()) + p.kafkaReader = NewCtpStatusReader(string(ctpStatusTopic), "o2-aliecs-core.trg") + if p.kafkaReader != nil { + log.WithField("level", infologger.IL_Devel).Info("TRG plugin: draining CTP status backlog") + p.drainCtpStatusBacklog(2 * time.Second) + + // Start reading CTP status updates + go p.readCtpStatusUpdates() + } else { + log.WithField("level", infologger.IL_Support). + Warn("could not create CTP status reader, CTP status monitoring disabled") + } + + return nil +} + +func (p *Plugin) reconcile() { + envMan := environment.ManagerInstance() + envIds := envMan.Ids() + activeEcsRuns := make(map[uint32]struct{}) + for _, envId := range envIds { + env, err := envMan.Environment(envId) + if err != nil || env == nil { + continue + } + rn := env.GetCurrentRunNumber() + if rn != 0 { + activeEcsRuns[rn] = struct{}{} + } + } + + p.cachedStatusMu.RLock() + if p.cachedStatus != nil && len(p.cachedStatus.Structured) > 0 { + for _, trgRun := range p.cachedStatus.Structured { + if _, contains := activeEcsRuns[trgRun.RunNumber]; contains { // if activeEcsRuns contains a RN known to TRG + continue + } else { // found TRG run unknown to AliECS + if p.trgClient == nil { + continue + } + in := trgpb.RunStopRequest{ // applies to both RunStop and RunUnload + Runn: trgRun.RunNumber, + Detector: "", + } + + if trgRun.State == CTP_RUNNING { // both STANDALONE and GLOBAL + + // Setting the detector here results in a failed RunStop call + // Returns with RC:12 "Unknown ltud [DETNAME]" + /*if trgRun.Cardinality == CTP_STANDALONE { // if global run, we send no detector + if len(trgRun.Detectors) == 1 { + in.Detector = trgRun.Detectors[0].String() + } + }*/ + + ctx, cancel := integration.NewContextEmptyEnvIdRunType(viper.GetDuration("trgReconciliationTimeout")) + _, err := p.trgClient.RunStop(ctx, &in, grpc.EmptyCallOption{}) + cancel() + + // TODO: Response's RC should also be checked here + if err != nil { + err = fmt.Errorf("TRG reconciliation failure: %w", err) + log.WithError(err). + WithField("level", infologger.IL_Support). + WithField("endpoint", viper.GetString("trgServiceEndpoint")). + WithField("run", trgRun.RunNumber). + WithField("call", "RunStop"). + Error("TRG error") + } else { + if trgRun.Cardinality == CTP_GLOBAL { + trgRun.State = CTP_LOADED // must be loaded if GLOBAL && RunStop successful + } else { + log.WithField("level", infologger.IL_Support). + WithField("endpoint", viper.GetString("trgServiceEndpoint")). + WithField("run", trgRun.RunNumber). + Info("TRG STANDALONE reconciliation complete") + } + } + } + if trgRun.State == CTP_LOADED && trgRun.Cardinality == CTP_GLOBAL { + ctx, cancel := integration.NewContextEmptyEnvIdRunType(viper.GetDuration("trgReconciliationTimeout")) + monitoring.AddEnvAndRunType(ctx, "none", "none") + _, err := p.trgClient.RunUnload(ctx, &in, grpc.EmptyCallOption{}) + cancel() + if err != nil { + err = fmt.Errorf("TRG reconciliation failure: %w", err) + log.WithError(err). + WithField("level", infologger.IL_Support). + WithField("endpoint", viper.GetString("trgServiceEndpoint")). + WithField("run", trgRun.RunNumber). + WithField("call", "RunUnload"). + Error("TRG error") + } else { + log.WithField("level", infologger.IL_Support). + WithField("endpoint", viper.GetString("trgServiceEndpoint")). + WithField("run", trgRun.RunNumber). + Info("TRG GLOBAL reconciliation complete") + } + } + } + } + } + p.cachedStatusMu.RUnlock() +} + +func (p *Plugin) ObjectStack(_ map[string]string, _ map[string]string) (stack map[string]interface{}) { + stack = make(map[string]interface{}) + return stack +} + +func (p *Plugin) CallStack(data interface{}) (stack map[string]interface{}) { + call, ok := data.(*callable.Call) + if !ok { + return + } + varStack := call.VarStack + envId, ok := varStack["environment_id"] + if !ok { + log.Error("cannot acquire environment ID") + return + } + + stack = make(map[string]interface{}) + stack["PrepareForRun"] = func() (out string) { // must formally return string even when we return nothing + log.WithField("partition", envId). + WithField("level", infologger.IL_Ops). + Info("ALIECS SOR operation : performing TRG PrepareForRun Request") + + runType, ok := varStack["run_type"] + if !ok { + log.WithField("partition", envId). + Debug("no run type set") + runType = "NONE" + } + + trgDetectorsParam, ok := varStack["trg_detectors"] + if !ok { + // "" -all required must be ready + log.WithField("partition", envId). + Debug("empty TRG detectors list provided") + trgDetectorsParam = "" + } + + callFailedStr := "TRG PrepareForRun call failed" + + detectors, err := p.parseDetectors(trgDetectorsParam) + if err != nil { + log.WithError(err). + WithField("level", infologger.IL_Support). + WithField("partition", envId). + WithField("call", "PrepareForRun"). + Error("TRG error") + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + + return + } + + in := trgpb.RunPrepareRequest{ + Runtype: runType, + Detectors: detectors, + } + if p.trgClient == nil { + err = fmt.Errorf("TRG plugin not initialized, PrepareForRun impossible") + + log.WithError(err). + WithField("level", infologger.IL_Support). + WithField("endpoint", viper.GetString("trgServiceEndpoint")). + WithField("partition", envId). + WithField("call", "PrepareForRun"). + Error("TRG error") + + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + + return + } + + if p.trgClient.GetConnState() == connectivity.Shutdown { + err = fmt.Errorf("TRG client connection not available, PrepareForRun impossible") + + log.WithError(err). + WithField("level", infologger.IL_Support). + WithField("endpoint", viper.GetString("trgServiceEndpoint")). + WithField("partition", envId). + WithField("call", "PrepareForRun"). + Error("TRG error") + + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + + return + } + + timeout := callable.AcquireTimeout(TRG_PFR_TIMEOUT, varStack, "PrepareForRun", envId) + ctx, cancel := integration.NewContext(envId, varStack, timeout) + defer cancel() + + payload := map[string]interface{}{ + "trgRequest": &in, + } + payloadJson, _ := json.Marshal(payload) + + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_STARTED, + OperationStep: "perform TRG call: PrepareForRun", + OperationStepStatus: pb.OpStatus_STARTED, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + }) + + var response *trgpb.RunReply + response, err = p.trgClient.PrepareForRun(ctx, &in, grpc.EmptyCallOption{}) + if err != nil { + log.WithError(err). + WithField("level", infologger.IL_Support). + WithField("endpoint", viper.GetString("trgServiceEndpoint")). + WithField("partition", envId). + WithField("call", "PrepareForRun"). + Error("TRG error") + + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_ERROR, + OperationStep: "perform TRG call: PrepareForRun", + OperationStepStatus: pb.OpStatus_DONE_ERROR, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + Error: err.Error(), + }) + + return + } + + if response != nil { + if response.Rc != 0 { + err = fmt.Errorf("response code %d from TRG: %s", response.Rc, response.Msg) + + log.WithError(err). + WithField("level", infologger.IL_Support). + WithField("endpoint", viper.GetString("trgServiceEndpoint")). + WithField("partition", envId). + WithField("call", "PrepareForRun"). + Error("TRG error") + + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + + payload["trgResponse"] = &response + payloadJson, _ = json.Marshal(payload) + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_ERROR, + OperationStep: "perform TRG call: PrepareForRun", + OperationStepStatus: pb.OpStatus_DONE_ERROR, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + Error: err.Error(), + }) + + return + } + } + + payload["trgResponse"] = &response + payloadJson, _ = json.Marshal(payload) + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_OK, + OperationStep: "perform TRG call: PrepareForRun", + OperationStepStatus: pb.OpStatus_DONE_OK, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + }) + + log.WithField("partition", envId). + Info("ALIECS SOR Operation : TRG PrepareForRun success") + + return + } + // global runs only + stack["RunLoad"] = func() (out string) { // must formally return string even when we return nothing + rn := varStack["run_number"] + var runNumber64 int64 + runNumber64, err := strconv.ParseInt(rn, 10, 32) + if err != nil { + log.WithField("partition", envId). + WithError(err). + Error("cannot acquire run number for Run Load") + } + log := log.WithField("run", runNumber64).WithField("partition", envId) + + log.WithField("level", infologger.IL_Ops). + Info("ALIECS SOR operation : performing TRG Run Load Request") + + parentRole, ok := call.GetParentRole().(callable.ParentRole) + if ok { + parentRole.DeleteGlobalRuntimeVar("trg_start_time_ms") + parentRole.DeleteGlobalRuntimeVar("trg_end_time_ms") + } + + globalConfig, ok := varStack["trg_global_config"] + log.WithField("globalConfig", globalConfig). + Debug("not a TRG Global Run, continuing with TRG Run Start") + if !ok { + log.Debug("no TRG Global config set") + globalConfig = "" + } + // TODO (malexis): pass consul key to TRG if avail + + trgDetectorsParam, ok := varStack["trg_detectors"] + if !ok { + // "" -all required must be ready + log.Debug("empty TRG detectors list provided") + trgDetectorsParam = "" + } + + callFailedStr := "TRG RunLoad call failed" + + detectors, err := p.parseDetectors(trgDetectorsParam) + if err != nil { + log.WithError(err). + WithField("level", infologger.IL_Support). + WithField("call", "RunLoad"). + Error("TRG error") + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + + return + } + + // standalone run + if len(strings.Split(detectors, " ")) < 2 && varStack["trg_global_run_enabled"] == "false" { + // we do not load any run cause it is standalone + log.Debug("not a TRG Global Run, continuing with TRG Run Start") + + return + } + + ctpReadoutIncluded := false + ctpReadoutIncludedStr, ok := varStack["ctp_readout_enabled"] + if ok { + ctpReadoutIncluded, err = strconv.ParseBool(ctpReadoutIncludedStr) + if err != nil { + log.WithError(err). + WithField("level", infologger.IL_Support). + WithField("call", "RunLoad"). + Warn("could not parse ctp_readout_enabled value") + } + } + + in := trgpb.RunLoadRequest{ + Runn: uint32(runNumber64), + Detectors: detectors, + Config: globalConfig, + CtpReadoutIncluded: ctpReadoutIncluded, + } + if p.trgClient == nil { + err = fmt.Errorf("TRG plugin not initialized, RunLoad impossible") + + log.WithError(err). + WithField("level", infologger.IL_Support). + WithField("endpoint", viper.GetString("trgServiceEndpoint")). + WithField("call", "RunLoad"). + Error("TRG error") + + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + + return + } + + if p.trgClient.GetConnState() == connectivity.Shutdown { + err = fmt.Errorf("TRG client connection not available, RunLoad impossible") + + log.WithError(err). + WithField("level", infologger.IL_Support). + WithField("endpoint", viper.GetString("trgServiceEndpoint")). + WithField("call", "RunLoad"). + Error("TRG error") + + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + + return + } + + timeout := callable.AcquireTimeout(TRG_LOAD_TIMEOUT, varStack, "RunLoad", envId) + ctx, cancel := integration.NewContext(envId, varStack, timeout) + defer cancel() + + payload := map[string]interface{}{ + "trgRequest": &in, + } + payloadJson, _ := json.Marshal(payload) + + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_STARTED, + OperationStep: "perform TRG call: RunLoad", + OperationStepStatus: pb.OpStatus_STARTED, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + }) + + var response *trgpb.RunReply + response, err = p.trgClient.RunLoad(ctx, &in, grpc.EmptyCallOption{}) + if err != nil { + log.WithError(err). + WithField("level", infologger.IL_Support). + WithField("endpoint", viper.GetString("trgServiceEndpoint")). + WithField("call", "RunLoad"). + Error("TRG error") + + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_ERROR, + OperationStep: "perform TRG call: RunLoad", + OperationStepStatus: pb.OpStatus_DONE_ERROR, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + Error: err.Error(), + }) + + return + } + + if response != nil { + if response.Rc != 0 { + err = fmt.Errorf("response code %d from TRG: %s", response.Rc, response.Msg) + + log.WithError(err). + WithField("level", infologger.IL_Support). + WithField("endpoint", viper.GetString("trgServiceEndpoint")). + WithField("call", "RunLoad"). + Error("TRG error") + + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + + payload["trgResponse"] = &response + payloadJson, _ = json.Marshal(payload) + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_ERROR, + OperationStep: "perform TRG call: RunLoad", + OperationStepStatus: pb.OpStatus_DONE_ERROR, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + Error: err.Error(), + }) + + return + } + } + + payload["trgResponse"] = &response + payloadJson, _ = json.Marshal(payload) + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_OK, + OperationStep: "perform TRG call: RunLoad", + OperationStepStatus: pb.OpStatus_DONE_OK, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + }) + + // runLoad successful, we cache the run number for eventual cleanup + p.pendingRunUnloads[envId] = runNumber64 + log.WithField("partition", envId). + WithField("run", runNumber64). + Info("ALIECS SOR Operation : TRG RunLoad success") + + return + } + stack["RunStart"] = func() (out string) { // must formally return string even when we return nothing + rn := varStack["run_number"] + var runNumber64 int64 + runNumber64, err := strconv.ParseInt(rn, 10, 32) + if err != nil { + log.WithError(err). + WithField("partition", envId). + Error("cannot acquire run number for Run Start") + } + log := log.WithField("run", runNumber64).WithField("partition", envId) + + log.Info("ALIECS SOR operation : performing TRG Run Start") + + runtimeConfig, ok := varStack["trg_runtime_config"] + if !ok { + log.Debug("no TRG config set, using default configuration") + runtimeConfig = "" + } + + trgDetectorsParam, ok := varStack["trg_detectors"] + if !ok { + // "" it is a global run + log.Debug("Detector for host is not available, starting global run") + trgDetectorsParam = "" + } + + callFailedStr := "TRG RunStart call failed" + + detectors, err := p.parseDetectors(trgDetectorsParam) + if err != nil { + log.WithError(err). + WithField("level", infologger.IL_Support). + WithField("endpoint", viper.GetString("trgServiceEndpoint")). + WithField("call", "RunStart"). + Error("TRG error") + + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + + return + } + + // if global run then start with empty string in detectors + if len(strings.Split(detectors, " ")) >= 2 || varStack["trg_global_run_enabled"] == "true" { + // global run detectors "" + detectors = "" + } + + in := trgpb.RunStartRequest{ + Runn: uint32(runNumber64), + Detector: detectors, + Config: runtimeConfig, + } + + if p.trgClient == nil { + err = fmt.Errorf("TRG plugin not initialized, RunStart impossible") + + log.WithError(err). + WithField("level", infologger.IL_Support). + WithField("endpoint", viper.GetString("trgServiceEndpoint")). + WithField("call", "RunStart"). + Error("TRG error") + + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + + return + } + if p.trgClient.GetConnState() == connectivity.Shutdown { + err = fmt.Errorf("TRG client connection not available, RunStart impossible") + + log.WithError(err). + WithField("level", infologger.IL_Support). + WithField("endpoint", viper.GetString("trgServiceEndpoint")). + WithField("call", "RunStart"). + Error("TRG error") + + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + + return + } + + timeout := callable.AcquireTimeout(TRG_START_TIMEOUT, varStack, "RunStart", envId) + ctx, cancel := integration.NewContext(envId, varStack, timeout) + defer cancel() + + payload := map[string]interface{}{ + "trgRequest": &in, + } + payloadJson, _ := json.Marshal(payload) + + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_STARTED, + OperationStep: "perform TRG call: RunStart", + OperationStepStatus: pb.OpStatus_STARTED, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + }) + + var response *trgpb.RunReply + response, err = p.trgClient.RunStart(ctx, &in, grpc.EmptyCallOption{}) + if err != nil { + log.WithError(err). + WithField("level", infologger.IL_Support). + WithField("endpoint", viper.GetString("trgServiceEndpoint")). + WithField("call", "RunStart"). + Error("TRG error") + + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_ERROR, + OperationStep: "perform TRG call: RunStart", + OperationStepStatus: pb.OpStatus_DONE_ERROR, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + Error: err.Error(), + }) + + return + } + if response != nil { + if response.Rc != 0 { + err = fmt.Errorf("response code %d from TRG: %s", response.Rc, response.Msg) + + log.WithError(err). + WithField("level", infologger.IL_Support). + WithField("endpoint", viper.GetString("trgServiceEndpoint")). + WithField("call", "RunStart"). + Error("TRG error") + + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + + payload["trgResponse"] = &response + payloadJson, _ = json.Marshal(payload) + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_ERROR, + OperationStep: "perform TRG call: RunStart", + OperationStepStatus: pb.OpStatus_DONE_ERROR, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + Error: err.Error(), + }) + + return + } + } + + trgStartTime := time.Now() + + payload["trgResponse"] = &response + payloadJson, _ = json.Marshal(payload) + the.EventWriterWithTopic(TOPIC).WriteEventWithTimestamp(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_OK, + OperationStep: "perform TRG call: RunStart", + OperationStepStatus: pb.OpStatus_DONE_OK, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + }, trgStartTime) + + // runStart successful, we cache the run number for eventual cleanup + p.pendingRunStops[envId] = runNumber64 + log.Info("TRG RunStart success") + + trgStartTimeS := strconv.FormatInt(trgStartTime.UnixMilli(), 10) + parentRole, ok := call.GetParentRole().(callable.ParentRole) + if ok { + parentRole.SetGlobalRuntimeVar("trg_start_time_ms", trgStartTimeS) + parentRole.DeleteGlobalRuntimeVar("trg_end_time_ms") + } + + return + } + runStopFunc := func(ctx context.Context, runNumber64 int64) (out string) { + log := log.WithField("run", runNumber64).WithField("partition", envId) + + trgDetectorsParam, ok := varStack["trg_detectors"] + if !ok { + // "" it is a global run + log.Debug("Detector for host is not available, stoping global run") + trgDetectorsParam = "" + } + + detectors, err := p.parseDetectors(trgDetectorsParam) + if err != nil { + return + } + + // if global run then start with empty + if len(strings.Split(detectors, " ")) >= 2 || varStack["trg_global_run_enabled"] == "true" { + // global run detectors "" + detectors = "" + } + + in := trgpb.RunStopRequest{ + Runn: uint32(runNumber64), + Detector: detectors, + } + + callFailedStr := "TRG RunStop call failed" + + if p.trgClient == nil { + err = fmt.Errorf("TRG plugin not initialized, RunStop impossible") + + log.WithError(err). + WithField("level", infologger.IL_Support). + WithField("endpoint", viper.GetString("trgServiceEndpoint")). + WithField("call", "RunStop"). + Error("TRG error") + + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + + return + } + if p.trgClient.GetConnState() == connectivity.Shutdown { + err = fmt.Errorf("TRG client connection not available, RunStop impossible") + + log.WithError(err). + WithField("level", infologger.IL_Support). + WithField("endpoint", viper.GetString("trgServiceEndpoint")). + WithField("call", "RunStop"). + Error("TRG error") + + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + + return + } + + payload := map[string]interface{}{ + "trgRequest": &in, + } + payloadJson, _ := json.Marshal(payload) + + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_STARTED, + OperationStep: "perform TRG call: RunStop", + OperationStepStatus: pb.OpStatus_STARTED, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + }) + + var response *trgpb.RunReply + response, err = p.trgClient.RunStop(ctx, &in, grpc.EmptyCallOption{}) + if err != nil { + log.WithError(err). + WithField("level", infologger.IL_Support). + WithField("endpoint", viper.GetString("trgServiceEndpoint")). + WithField("call", "RunStop"). + Error("TRG error") + + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_ERROR, + OperationStep: "perform TRG call: RunStop", + OperationStepStatus: pb.OpStatus_DONE_ERROR, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + Error: err.Error(), + }) + + return + } + if response != nil { + if response.Rc != 0 { + err = fmt.Errorf("response code %d from TRG: %s", response.Rc, response.Msg) + + log.WithError(err). + WithField("level", infologger.IL_Support). + WithField("endpoint", viper.GetString("trgServiceEndpoint")). + WithField("call", "RunStop"). + Error("TRG error") + + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + + payload["trgResponse"] = &response + payloadJson, _ = json.Marshal(payload) + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_ERROR, + OperationStep: "perform TRG call: RunStop", + OperationStepStatus: pb.OpStatus_DONE_ERROR, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + Error: err.Error(), + }) + + return + } + } + + trgEndTime := time.Now() + + payload["trgResponse"] = &response + payloadJson, _ = json.Marshal(payload) + the.EventWriterWithTopic(TOPIC).WriteEventWithTimestamp(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_OK, + OperationStep: "perform TRG call: RunStop", + OperationStepStatus: pb.OpStatus_DONE_OK, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + }, trgEndTime) + + // RunStop successful, we pop the run number from the cache + delete(p.pendingRunStops, envId) + log.Info("TRG RunStop success") + + trgEndTimeS := strconv.FormatInt(trgEndTime.UnixMilli(), 10) + parentRole, ok := call.GetParentRole().(callable.ParentRole) + if ok { + parentRole.SetGlobalRuntimeVar("trg_end_time_ms", trgEndTimeS) + } else { + log.WithField("trgEndTime", trgEndTimeS). + Debug("could not get parentRole and set TRG end time") + } + + return + } + runUnloadFunc := func(ctx context.Context, runNumber64 int64) (out string) { + log := log.WithField("run", runNumber64).WithField("partition", envId) + + trgDetectorsParam, ok := varStack["trg_detectors"] + if !ok { + trgDetectorsParam = "" + } + + detectors, err := p.parseDetectors(trgDetectorsParam) + if err != nil { + return + } + + // if global run then unload + if len(strings.Split(detectors, " ")) < 2 && varStack["trg_global_run_enabled"] == "false" { + log.Debug("not a TRG Global Run, skipping TRG Run Unload") + return + } + + in := trgpb.RunStopRequest{ + Runn: uint32(runNumber64), + // "" when unloading global run + Detector: "", + } + + callFailedStr := "TRG RunUnload call failed" + + if p.trgClient == nil { + err = fmt.Errorf("TRG plugin not initialized, RunUnload impossible") + + log.WithError(err). + WithField("level", infologger.IL_Support). + WithField("endpoint", viper.GetString("trgServiceEndpoint")). + WithField("call", "RunUnload"). + Error("TRG error") + + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + + return + } + if p.trgClient.GetConnState() == connectivity.Shutdown { + err = fmt.Errorf("TRG client connection not available, RunUnload impossible") + + log.WithError(err). + WithField("level", infologger.IL_Support). + WithField("endpoint", viper.GetString("trgServiceEndpoint")). + WithField("call", "RunUnload"). + Error("TRG error") + + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + + return + } + + payload := map[string]interface{}{ + "trgRequest": &in, + } + payloadJson, _ := json.Marshal(payload) + + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_STARTED, + OperationStep: "perform TRG call: RunUnload", + OperationStepStatus: pb.OpStatus_STARTED, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + }) + + var response *trgpb.RunReply + response, err = p.trgClient.RunUnload(ctx, &in, grpc.EmptyCallOption{}) + if err != nil { + log.WithError(err). + WithField("level", infologger.IL_Support). + WithField("endpoint", viper.GetString("trgServiceEndpoint")). + WithField("call", "RunUnload"). + Error("TRG error") + + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_ERROR, + OperationStep: "perform TRG call: RunUnload", + OperationStepStatus: pb.OpStatus_DONE_ERROR, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + Error: err.Error(), + }) + + return + } + if response != nil { + if response.Rc != 0 { + err = fmt.Errorf("response code %d from TRG: %s", response.Rc, response.Msg) + + log.WithError(err). + WithField("level", infologger.IL_Support). + WithField("endpoint", viper.GetString("trgServiceEndpoint")). + WithField("call", "RunUnload"). + Error("TRG error") + + call.VarStack["__call_error_reason"] = err.Error() + call.VarStack["__call_error"] = callFailedStr + + payload["trgResponse"] = &response + payloadJson, _ = json.Marshal(payload) + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_ERROR, + OperationStep: "perform TRG call: RunUnload", + OperationStepStatus: pb.OpStatus_DONE_ERROR, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + Error: err.Error(), + }) + + return + } + } + + payload["trgResponse"] = &response + payloadJson, _ = json.Marshal(payload) + the.EventWriterWithTopic(TOPIC).WriteEvent(&pb.Ev_IntegratedServiceEvent{ + Name: call.GetName(), + OperationName: call.Func, + OperationStatus: pb.OpStatus_DONE_OK, + OperationStep: "perform TRG call: RunUnload", + OperationStepStatus: pb.OpStatus_DONE_OK, + EnvironmentId: envId, + Payload: string(payloadJson[:]), + }) + + // RunUnload successful, we pop the run number from the cache + delete(p.pendingRunUnloads, envId) + log.Info("ALICECS EOR operation : TRG RunUnload success") + + return + } + stack["RunStop"] = func() (out string) { + rn := varStack["run_number"] + var runNumber64 int64 + var err error + runNumber64, err = strconv.ParseInt(rn, 10, 32) + if err != nil { + log.WithError(err). + WithField("partition", envId). + Error("cannot acquire run number for TRG Run Stop") + } + + log.WithField("partition", envId). + WithField("run", runNumber64). + Info("ALIECS EOR operation : performing TRG Run Stop ") + + timeout := callable.AcquireTimeout(TRG_STOP_TIMEOUT, varStack, "RunStop", envId) + ctx, cancel := integration.NewContext(envId, varStack, timeout) + defer cancel() + + return runStopFunc(ctx, runNumber64) + } + stack["RunUnload"] = func() (out string) { + rn := varStack["run_number"] + var runNumber64 int64 + var err error + runNumber64, err = strconv.ParseInt(rn, 10, 32) + if err != nil { + log.WithError(err). + WithField("partition", envId). + Error("cannot acquire run number for TRG Run Unload") + } + + log.WithField("partition", envId). + WithField("run", runNumber64). + Info("ALIECS EOR operation : performing TRG Run Unload ") + + timeout := callable.AcquireTimeout(TRG_UNLOAD_TIMEOUT, varStack, "RunUnload", envId) + ctx, cancel := integration.NewContext(envId, varStack, timeout) + defer cancel() + + return runUnloadFunc(ctx, runNumber64) + } + stack["Cleanup"] = func() (out string) { + // obsolete, EnsureRunStop and EnsureRunUnload should be used instead one after another. + envId, ok := varStack["environment_id"] + if !ok { + log.WithField("partition", envId). + WithField("level", infologger.IL_Devel). + Warn("no environment_id found for TRG cleanup") + return + } + + timeout := callable.AcquireTimeout(TRG_CLEANUP_TIMEOUT, varStack, "Cleanup", envId) + ctx, cancel := integration.NewContext(envId, varStack, timeout) + defer cancel() + + // runStop if found pending + runNumberStop, ok := p.pendingRunStops[envId] + if ok { + log.WithField("run", runNumberStop). + WithField("partition", envId). + WithField("level", infologger.IL_Devel). + Info("pending TRG Stop found, performing cleanup") + + delete(p.pendingRunStops, envId) + _ = runStopFunc(ctx, runNumberStop) + + trgEndTime := strconv.FormatInt(time.Now().UnixMilli(), 10) + parentRole, ok := call.GetParentRole().(callable.ParentRole) + if ok { + parentRole.SetGlobalRuntimeVar("trg_end_time_ms", trgEndTime) + } else { + log.WithField("partition", envId). + WithField("run", runNumberStop). + WithField("trgEndTime", trgEndTime). + Debug("could not get parentRole and set TRG end time") + } + } else { + log.WithField("partition", envId). + WithField("level", infologger.IL_Devel). + Debug("TRG cleanup: Stop not needed") + } + + // runUnload if found pending + runNumberUnload, ok := p.pendingRunUnloads[envId] + if ok { + log.WithField("run", runNumberUnload). + WithField("partition", envId). + WithField("level", infologger.IL_Devel). + Info("pending TRG Unload found, performing cleanup") + + delete(p.pendingRunUnloads, envId) + _ = runUnloadFunc(ctx, runNumberUnload) + } else { + log.WithField("partition", envId). + WithField("level", infologger.IL_Devel). + Debug("TRG cleanup: Unload not needed") + } + return + } + stack["EnsureRunStop"] = func() (out string) { + // if there is a run to stop, it is stopped, otherwise we do nothing (no errors, no events) + envId, ok := varStack["environment_id"] + if !ok { + log.WithField("partition", envId). + WithField("level", infologger.IL_Devel). + Warn("no environment_id found for TRG EnsureRunStop") + return + } + + timeout := callable.AcquireTimeout(TRG_STOP_TIMEOUT, varStack, "EnsureRunStop", envId) + ctx, cancel := integration.NewContext(envId, varStack, timeout) + defer cancel() + + // runStop if found pending + runNumberStop, ok := p.pendingRunStops[envId] + if ok { + log.WithField("run", runNumberStop). + WithField("partition", envId). + WithField("level", infologger.IL_Devel). + Info("pending TRG Stop found, performing cleanup") + + delete(p.pendingRunStops, envId) + _ = runStopFunc(ctx, runNumberStop) + + trgEndTime := strconv.FormatInt(time.Now().UnixMilli(), 10) + parentRole, ok := call.GetParentRole().(callable.ParentRole) + if ok { + parentRole.SetGlobalRuntimeVar("trg_end_time_ms", trgEndTime) + } else { + log.WithField("partition", envId). + WithField("run", runNumberStop). + WithField("trgEndTime", trgEndTime). + Debug("could not get parentRole and set TRG end time") + } + } else { + log.WithField("partition", envId). + WithField("level", infologger.IL_Devel). + Debug("TRG cleanup: Stop not needed") + } + return + } + stack["EnsureRunUnload"] = func() (out string) { + // if there is a run to unload, it is unloaded, otherwise we do nothing (no errors, no events) + envId, ok := varStack["environment_id"] + if !ok { + log.WithField("partition", envId). + WithField("level", infologger.IL_Devel). + Warn("no environment_id found for TRG EnsureRunUnload") + return + } + + timeout := callable.AcquireTimeout(TRG_STOP_TIMEOUT, varStack, "EnsureRunUnload", envId) + ctx, cancel := integration.NewContext(envId, varStack, timeout) + defer cancel() + + // runUnload if found pending + runNumberUnload, ok := p.pendingRunUnloads[envId] + if ok { + log.WithField("run", runNumberUnload). + WithField("partition", envId). + WithField("level", infologger.IL_Devel). + Info("pending TRG Unload found, performing cleanup") + + delete(p.pendingRunUnloads, envId) + _ = runUnloadFunc(ctx, runNumberUnload) + } else { + log.WithField("partition", envId). + WithField("level", infologger.IL_Devel). + Debug("TRG cleanup: Unload not needed") + } + return + } + + return +} + +func (p *Plugin) parseDetectors(ctsDetectorsParam string) (detectors string, err error) { + detectorsSlice := make([]string, 0) + bytes := []byte(ctsDetectorsParam) + err = json.Unmarshal(bytes, &detectorsSlice) + if err != nil { + log.WithError(err).Error("error processing TRG detectors list") + return + } + + detectors = strings.ToLower(strings.Join(detectorsSlice, " ")) + return +} + +func (p *Plugin) drainCtpStatusBacklog(timeout time.Duration) { + drainCtx, cancel := context.WithTimeout(p.kafkaReaderCtx, timeout) + defer cancel() + for { + ctpStatus, err := p.kafkaReader.Next(drainCtx) + if err != nil { + if errors.Is(err, context.DeadlineExceeded) || errors.Is(err, io.EOF) || errors.Is(err, context.Canceled) { + break + } + // transient error: small sleep and continue until timeout + time.Sleep(50 * time.Millisecond) + continue + } + if ctpStatus == nil { + continue + } + + detectorsInHoldover := extractDetectorsInHoldover(ctpStatus) + clockTransitionTimeExpected := extractClockTransitionExpected(ctpStatus) + currentClock := ctpStatus.Clock.String() + + p.cachedStatusMu.Lock() + p.cachedStatus.Clock = currentClock + p.cachedStatus.DetectorsInHoldover = detectorsInHoldover + p.cachedStatus.ClockTransitionExpectedTime = clockTransitionTimeExpected + p.cachedStatusMu.Unlock() + } +} + +func extractClockTransitionExpected(ctpStatus *trgpb.Status) int64 { + var clockTransitionTimeExpected int64 + if ctpStatus.ClockTransitionExpected > 0 { + clockTransitionTimeExpected = int64(ctpStatus.TimestampNano) + int64(ctpStatus.ClockTransitionExpected)*1e9 + } + return clockTransitionTimeExpected +} + +func extractDetectorsInHoldover(ctpStatus *trgpb.Status) []string { + var detectorsInHoldover []string + for _, detInfo := range ctpStatus.DetectorInfo { + if detInfo.HoldoverOngoing { + detectorsInHoldover = append(detectorsInHoldover, detInfo.Detector.String()) + } + } + return detectorsInHoldover +} + +func (p *Plugin) readCtpStatusUpdates() { + for { + ctpStatus, err := p.kafkaReader.Next(p.kafkaReaderCtx) + if errors.Is(err, io.EOF) { + log.WithField(infologger.Level, infologger.IL_Support). + Debug("received EOF from CTP status reader, likely reading was cancelled, exiting") + break + } + if err != nil { + log.WithField(infologger.Level, infologger.IL_Support). + WithError(err). + Error("error while reading CTP status from Kafka") + time.Sleep(time.Second * 1) // throttle to avoid spamming infologger + continue + } + if ctpStatus == nil { + continue + } + + detectorsInHoldover := extractDetectorsInHoldover(ctpStatus) + clockTransitionExpectedTime := extractClockTransitionExpected(ctpStatus) + currentClock := ctpStatus.Clock.String() + + p.cachedStatusMu.Lock() + previousExpectedClockTransitionTime := p.cachedStatus.ClockTransitionExpectedTime + previousDetectorsInHoldover := p.cachedStatus.DetectorsInHoldover + + p.cachedStatus.Clock = currentClock + p.cachedStatus.DetectorsInHoldover = detectorsInHoldover + p.cachedStatus.ClockTransitionExpectedTime = clockTransitionExpectedTime + p.cachedStatusMu.Unlock() + + p.reportCtpStatus(ctpStatus.ClockTransitionExpected, previousExpectedClockTransitionTime, detectorsInHoldover, previousDetectorsInHoldover) + } +} + +func (p *Plugin) reportCtpStatus(clockTransitionExpected uint32, previousExpectedClockTransitionTime int64, detectorsInHoldover []string, previousDetectorsInHoldover []string) { + + if clockTransitionExpected == 0 && previousExpectedClockTransitionTime == 0 && len(detectorsInHoldover) == 0 && len(previousDetectorsInHoldover) == 0 { + // nothing interesting happening in CTP, don't report anything + return + } + + msg := "" + if clockTransitionExpected > 0 { + msg += fmt.Sprintf("Clock transition expected in less than %ds. ", clockTransitionExpected) + } else if clockTransitionExpected == 0 && previousExpectedClockTransitionTime > 0 { + msg += "Clock transition has just been performed. " + } else { + // clock transition was not and is not expected, noop + } + + if len(detectorsInHoldover) > 0 { + msg += fmt.Sprintf("Detectors in Holdover: %s. ", strings.Join(detectorsInHoldover, ", ")) + } else { + msg += "No detectors in Holdover. " + } + + if clockTransitionExpected > 0 { + msg += "Starting a trigger run with ANY detector may cause instabilities." + log.WithField(infologger.Level, infologger.IL_Ops).Warn(msg) + } else if len(detectorsInHoldover) > 0 { + msg += "Starting a trigger run with these detectors may cause instabilities." + log.WithField(infologger.Level, infologger.IL_Ops).Warn(msg) + } else { + msg += "It is safe to start trigger runs." + log.WithField(infologger.Level, infologger.IL_Ops).Info(msg) + } +} + +func (p *Plugin) Destroy() error { + p.trgClientCancel() + + if p.kafkaReaderCancel != nil { + p.kafkaReaderCancel() + } + if p.kafkaReader != nil { + _ = p.kafkaReader.Close() + } + + return p.trgClient.Close() +} diff --git a/core/integration/trg/protos/ctp_status.pb.go b/core/integration/trg/protos/ctp_status.pb.go new file mode 100644 index 000000000..39bd8c3d8 --- /dev/null +++ b/core/integration/trg/protos/ctp_status.pb.go @@ -0,0 +1,387 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.34.1 +// protoc v5.29.3 +// source: protos/ctp_status.proto + +package ctpecs + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type Detector int32 + +const ( + Detector_NULL_DETECTOR Detector = 0 + Detector_ITS Detector = 6 + Detector_MCH Detector = 8 + Detector_MFT Detector = 9 + Detector_TPC Detector = 13 +) + +// Enum value maps for Detector. +var ( + Detector_name = map[int32]string{ + 0: "NULL_DETECTOR", + 6: "ITS", + 8: "MCH", + 9: "MFT", + 13: "TPC", + } + Detector_value = map[string]int32{ + "NULL_DETECTOR": 0, + "ITS": 6, + "MCH": 8, + "MFT": 9, + "TPC": 13, + } +) + +func (x Detector) Enum() *Detector { + p := new(Detector) + *p = x + return p +} + +func (x Detector) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (Detector) Descriptor() protoreflect.EnumDescriptor { + return file_protos_ctp_status_proto_enumTypes[0].Descriptor() +} + +func (Detector) Type() protoreflect.EnumType { + return &file_protos_ctp_status_proto_enumTypes[0] +} + +func (x Detector) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use Detector.Descriptor instead. +func (Detector) EnumDescriptor() ([]byte, []int) { + return file_protos_ctp_status_proto_rawDescGZIP(), []int{0} +} + +type Clock int32 + +const ( + Clock_NULL Clock = 0 + Clock_LOCAL Clock = 1 + Clock_BEAM1 Clock = 2 +) + +// Enum value maps for Clock. +var ( + Clock_name = map[int32]string{ + 0: "NULL", + 1: "LOCAL", + 2: "BEAM1", + } + Clock_value = map[string]int32{ + "NULL": 0, + "LOCAL": 1, + "BEAM1": 2, + } +) + +func (x Clock) Enum() *Clock { + p := new(Clock) + *p = x + return p +} + +func (x Clock) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (Clock) Descriptor() protoreflect.EnumDescriptor { + return file_protos_ctp_status_proto_enumTypes[1].Descriptor() +} + +func (Clock) Type() protoreflect.EnumType { + return &file_protos_ctp_status_proto_enumTypes[1] +} + +func (x Clock) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use Clock.Descriptor instead. +func (Clock) EnumDescriptor() ([]byte, []int) { + return file_protos_ctp_status_proto_rawDescGZIP(), []int{1} +} + +type DetectorInfo struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Detector Detector `protobuf:"varint,1,opt,name=detector,proto3,enum=ctp_status.Detector" json:"detector,omitempty"` + HoldoverOngoing bool `protobuf:"varint,2,opt,name=holdover_ongoing,json=holdoverOngoing,proto3" json:"holdover_ongoing,omitempty"` // detector:holdover, holdover=True if holdover ON or holdover OFF not finished, holdover=False if holdover procedure finished +} + +func (x *DetectorInfo) Reset() { + *x = DetectorInfo{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_ctp_status_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DetectorInfo) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DetectorInfo) ProtoMessage() {} + +func (x *DetectorInfo) ProtoReflect() protoreflect.Message { + mi := &file_protos_ctp_status_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DetectorInfo.ProtoReflect.Descriptor instead. +func (*DetectorInfo) Descriptor() ([]byte, []int) { + return file_protos_ctp_status_proto_rawDescGZIP(), []int{0} +} + +func (x *DetectorInfo) GetDetector() Detector { + if x != nil { + return x.Detector + } + return Detector_NULL_DETECTOR +} + +func (x *DetectorInfo) GetHoldoverOngoing() bool { + if x != nil { + return x.HoldoverOngoing + } + return false +} + +type Status struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + TimestampNano uint64 `protobuf:"varint,1,opt,name=timestamp_nano,json=timestampNano,proto3" json:"timestamp_nano,omitempty"` // nanoseconds since epoch or whatever precision you can offer + DetectorInfo []*DetectorInfo `protobuf:"bytes,2,rep,name=detector_info,json=detectorInfo,proto3" json:"detector_info,omitempty"` + Clock Clock `protobuf:"varint,3,opt,name=clock,proto3,enum=ctp_status.Clock" json:"clock,omitempty"` + ClockTransitionExpected uint32 `protobuf:"varint,4,opt,name=clock_transition_expected,json=clockTransitionExpected,proto3" json:"clock_transition_expected,omitempty"` // Integer, values only 90,60,30,0 + Ready bool `protobuf:"varint,5,opt,name=ready,proto3" json:"ready,omitempty"` // True if no holdover for any detector and clocktransition=0, False otherwise +} + +func (x *Status) Reset() { + *x = Status{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_ctp_status_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Status) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Status) ProtoMessage() {} + +func (x *Status) ProtoReflect() protoreflect.Message { + mi := &file_protos_ctp_status_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Status.ProtoReflect.Descriptor instead. +func (*Status) Descriptor() ([]byte, []int) { + return file_protos_ctp_status_proto_rawDescGZIP(), []int{1} +} + +func (x *Status) GetTimestampNano() uint64 { + if x != nil { + return x.TimestampNano + } + return 0 +} + +func (x *Status) GetDetectorInfo() []*DetectorInfo { + if x != nil { + return x.DetectorInfo + } + return nil +} + +func (x *Status) GetClock() Clock { + if x != nil { + return x.Clock + } + return Clock_NULL +} + +func (x *Status) GetClockTransitionExpected() uint32 { + if x != nil { + return x.ClockTransitionExpected + } + return 0 +} + +func (x *Status) GetReady() bool { + if x != nil { + return x.Ready + } + return false +} + +var File_protos_ctp_status_proto protoreflect.FileDescriptor + +var file_protos_ctp_status_proto_rawDesc = []byte{ + 0x0a, 0x17, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x63, 0x74, 0x70, 0x5f, 0x73, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0a, 0x63, 0x74, 0x70, 0x5f, 0x73, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x6b, 0x0a, 0x0c, 0x44, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, + 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x30, 0x0a, 0x08, 0x64, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, + 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x14, 0x2e, 0x63, 0x74, 0x70, 0x5f, 0x73, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x2e, 0x44, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, 0x08, 0x64, + 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x29, 0x0a, 0x10, 0x68, 0x6f, 0x6c, 0x64, 0x6f, + 0x76, 0x65, 0x72, 0x5f, 0x6f, 0x6e, 0x67, 0x6f, 0x69, 0x6e, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x0f, 0x68, 0x6f, 0x6c, 0x64, 0x6f, 0x76, 0x65, 0x72, 0x4f, 0x6e, 0x67, 0x6f, 0x69, + 0x6e, 0x67, 0x22, 0xe9, 0x01, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x25, 0x0a, + 0x0e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x5f, 0x6e, 0x61, 0x6e, 0x6f, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, + 0x4e, 0x61, 0x6e, 0x6f, 0x12, 0x3d, 0x0a, 0x0d, 0x64, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, + 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x63, 0x74, + 0x70, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2e, 0x44, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, + 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0c, 0x64, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x49, + 0x6e, 0x66, 0x6f, 0x12, 0x27, 0x0a, 0x05, 0x63, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x0e, 0x32, 0x11, 0x2e, 0x63, 0x74, 0x70, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2e, + 0x43, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x05, 0x63, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x3a, 0x0a, 0x19, + 0x63, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, + 0x5f, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x17, 0x63, 0x6c, 0x6f, 0x63, 0x6b, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, + 0x45, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x72, 0x65, 0x61, 0x64, + 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x72, 0x65, 0x61, 0x64, 0x79, 0x2a, 0x83, + 0x01, 0x0a, 0x08, 0x44, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x11, 0x0a, 0x0d, 0x4e, + 0x55, 0x4c, 0x4c, 0x5f, 0x44, 0x45, 0x54, 0x45, 0x43, 0x54, 0x4f, 0x52, 0x10, 0x00, 0x12, 0x07, + 0x0a, 0x03, 0x49, 0x54, 0x53, 0x10, 0x06, 0x12, 0x07, 0x0a, 0x03, 0x4d, 0x43, 0x48, 0x10, 0x08, + 0x12, 0x07, 0x0a, 0x03, 0x4d, 0x46, 0x54, 0x10, 0x09, 0x12, 0x07, 0x0a, 0x03, 0x54, 0x50, 0x43, + 0x10, 0x0d, 0x22, 0x04, 0x08, 0x01, 0x10, 0x01, 0x22, 0x04, 0x08, 0x02, 0x10, 0x02, 0x22, 0x04, + 0x08, 0x03, 0x10, 0x03, 0x22, 0x04, 0x08, 0x04, 0x10, 0x04, 0x22, 0x04, 0x08, 0x05, 0x10, 0x05, + 0x22, 0x04, 0x08, 0x07, 0x10, 0x07, 0x22, 0x04, 0x08, 0x0a, 0x10, 0x0a, 0x22, 0x04, 0x08, 0x0b, + 0x10, 0x0b, 0x22, 0x04, 0x08, 0x0c, 0x10, 0x0c, 0x22, 0x04, 0x08, 0x0e, 0x10, 0x0e, 0x22, 0x04, + 0x08, 0x0f, 0x10, 0x0f, 0x2a, 0x27, 0x0a, 0x05, 0x43, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x08, 0x0a, + 0x04, 0x4e, 0x55, 0x4c, 0x4c, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x4c, 0x4f, 0x43, 0x41, 0x4c, + 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x42, 0x45, 0x41, 0x4d, 0x31, 0x10, 0x02, 0x42, 0x44, 0x5a, + 0x42, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x41, 0x6c, 0x69, 0x63, + 0x65, 0x4f, 0x32, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x2f, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, + 0x2f, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x2f, 0x74, 0x72, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x3b, 0x63, 0x74, 0x70, + 0x65, 0x63, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_protos_ctp_status_proto_rawDescOnce sync.Once + file_protos_ctp_status_proto_rawDescData = file_protos_ctp_status_proto_rawDesc +) + +func file_protos_ctp_status_proto_rawDescGZIP() []byte { + file_protos_ctp_status_proto_rawDescOnce.Do(func() { + file_protos_ctp_status_proto_rawDescData = protoimpl.X.CompressGZIP(file_protos_ctp_status_proto_rawDescData) + }) + return file_protos_ctp_status_proto_rawDescData +} + +var file_protos_ctp_status_proto_enumTypes = make([]protoimpl.EnumInfo, 2) +var file_protos_ctp_status_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_protos_ctp_status_proto_goTypes = []interface{}{ + (Detector)(0), // 0: ctp_status.Detector + (Clock)(0), // 1: ctp_status.Clock + (*DetectorInfo)(nil), // 2: ctp_status.DetectorInfo + (*Status)(nil), // 3: ctp_status.Status +} +var file_protos_ctp_status_proto_depIdxs = []int32{ + 0, // 0: ctp_status.DetectorInfo.detector:type_name -> ctp_status.Detector + 2, // 1: ctp_status.Status.detector_info:type_name -> ctp_status.DetectorInfo + 1, // 2: ctp_status.Status.clock:type_name -> ctp_status.Clock + 3, // [3:3] is the sub-list for method output_type + 3, // [3:3] is the sub-list for method input_type + 3, // [3:3] is the sub-list for extension type_name + 3, // [3:3] is the sub-list for extension extendee + 0, // [0:3] is the sub-list for field type_name +} + +func init() { file_protos_ctp_status_proto_init() } +func file_protos_ctp_status_proto_init() { + if File_protos_ctp_status_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_protos_ctp_status_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DetectorInfo); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_ctp_status_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Status); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_protos_ctp_status_proto_rawDesc, + NumEnums: 2, + NumMessages: 2, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_protos_ctp_status_proto_goTypes, + DependencyIndexes: file_protos_ctp_status_proto_depIdxs, + EnumInfos: file_protos_ctp_status_proto_enumTypes, + MessageInfos: file_protos_ctp_status_proto_msgTypes, + }.Build() + File_protos_ctp_status_proto = out.File + file_protos_ctp_status_proto_rawDesc = nil + file_protos_ctp_status_proto_goTypes = nil + file_protos_ctp_status_proto_depIdxs = nil +} diff --git a/core/integration/trg/protos/ctp_status.proto b/core/integration/trg/protos/ctp_status.proto new file mode 100644 index 000000000..78b153d3b --- /dev/null +++ b/core/integration/trg/protos/ctp_status.proto @@ -0,0 +1,46 @@ +syntax="proto3"; + +package ctp_status; +option go_package = "github.com/AliceO2Group/Control/core/integration/trg/protos;ctpecs"; + + +enum Detector { +NULL_DETECTOR = 0; +reserved 1; // CPV was removed during YETS 2024-2025 +reserved 2; // EMC not using holdover +reserved 3; // FDD not using holdover +reserved 4; // FT0 not using holdover +reserved 5; // FV0 not using holdover +ITS = 6; +reserved 7; // HMP not using holdover +MCH = 8; +MFT = 9; +reserved 10; // MID not using holdover +reserved 11; // PHS was removed during YETS 2024-2025 +reserved 12; // TOF not using holdover +TPC = 13; +reserved 14; // TRD not using holdover +reserved 15; // ZDC not using holdover +} + + +message DetectorInfo { +Detector detector = 1; +bool holdover_ongoing = 2; // detector:holdover, holdover=True if holdover ON or holdover OFF not finished, holdover=False if holdover procedure finished +} + + +enum Clock { +NULL = 0; +LOCAL = 1; +BEAM1 = 2; +} + +message Status { // General status message for kafka + +uint64 timestamp_nano = 1; // nanoseconds since epoch or whatever precision you can offer +repeated DetectorInfo detector_info = 2; +Clock clock = 3; +uint32 clock_transition_expected = 4; // Integer, values only 90,60,30,0 +bool ready = 5; // True if no holdover for any detector and clocktransition=0, False otherwise +} \ No newline at end of file diff --git a/core/integration/trg/protos/ctpecs.pb.go b/core/integration/trg/protos/ctpecs.pb.go new file mode 100644 index 000000000..cb61c9a0c --- /dev/null +++ b/core/integration/trg/protos/ctpecs.pb.go @@ -0,0 +1,742 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.34.1 +// protoc v5.29.3 +// source: protos/ctpecs.proto + +package ctpecs + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// -config, e.g. "trig\nPH_bc 100 inf" or +// -key in case the config is available in Consul[key] +// -"" use default configuration +// +// Optional parameters follows, overriding parameters in config string +// for the begining let's agree on three parameters : +type RunStartRequest_Mode int32 + +const ( + RunStartRequest_NULL RunStartRequest_Mode = 0 // use default (from config) + RunStartRequest_TRIG RunStartRequest_Mode = 1 + RunStartRequest_CONT RunStartRequest_Mode = 2 +) + +// Enum value maps for RunStartRequest_Mode. +var ( + RunStartRequest_Mode_name = map[int32]string{ + 0: "NULL", + 1: "TRIG", + 2: "CONT", + } + RunStartRequest_Mode_value = map[string]int32{ + "NULL": 0, + "TRIG": 1, + "CONT": 2, + } +) + +func (x RunStartRequest_Mode) Enum() *RunStartRequest_Mode { + p := new(RunStartRequest_Mode) + *p = x + return p +} + +func (x RunStartRequest_Mode) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (RunStartRequest_Mode) Descriptor() protoreflect.EnumDescriptor { + return file_protos_ctpecs_proto_enumTypes[0].Descriptor() +} + +func (RunStartRequest_Mode) Type() protoreflect.EnumType { + return &file_protos_ctpecs_proto_enumTypes[0] +} + +func (x RunStartRequest_Mode) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use RunStartRequest_Mode.Descriptor instead. +func (RunStartRequest_Mode) EnumDescriptor() ([]byte, []int) { + return file_protos_ctpecs_proto_rawDescGZIP(), []int{2, 0} +} + +// import "ctpecs_m.proto"; +// global runs only: +type RunPrepareRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Runtype string `protobuf:"bytes,1,opt,name=runtype,proto3" json:"runtype,omitempty"` + Detectors string `protobuf:"bytes,2,opt,name=detectors,proto3" json:"detectors,omitempty"` // as in RunLoadRequest +} + +func (x *RunPrepareRequest) Reset() { + *x = RunPrepareRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_ctpecs_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RunPrepareRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RunPrepareRequest) ProtoMessage() {} + +func (x *RunPrepareRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_ctpecs_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RunPrepareRequest.ProtoReflect.Descriptor instead. +func (*RunPrepareRequest) Descriptor() ([]byte, []int) { + return file_protos_ctpecs_proto_rawDescGZIP(), []int{0} +} + +func (x *RunPrepareRequest) GetRuntype() string { + if x != nil { + return x.Runtype + } + return "" +} + +func (x *RunPrepareRequest) GetDetectors() string { + if x != nil { + return x.Detectors + } + return "" +} + +type RunLoadRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Runn uint32 `protobuf:"varint,1,opt,name=runn,proto3" json:"runn,omitempty"` // run number + Config string `protobuf:"bytes,2,opt,name=config,proto3" json:"config,omitempty"` + // -config, i.e. the content of partname.pd + // -partname -the content is in ctp config dir partname.pd + // -key in case the config is available in Consul[key] + Detectors string `protobuf:"bytes,3,opt,name=detectors,proto3" json:"detectors,omitempty"` // available detectors, "" -all required must be ready + CtpReadoutIncluded bool `protobuf:"varint,4,opt,name=ctp_readout_included,json=ctpReadoutIncluded,proto3" json:"ctp_readout_included,omitempty"` // true: ctp readout included in the run +} + +func (x *RunLoadRequest) Reset() { + *x = RunLoadRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_ctpecs_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RunLoadRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RunLoadRequest) ProtoMessage() {} + +func (x *RunLoadRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_ctpecs_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RunLoadRequest.ProtoReflect.Descriptor instead. +func (*RunLoadRequest) Descriptor() ([]byte, []int) { + return file_protos_ctpecs_proto_rawDescGZIP(), []int{1} +} + +func (x *RunLoadRequest) GetRunn() uint32 { + if x != nil { + return x.Runn + } + return 0 +} + +func (x *RunLoadRequest) GetConfig() string { + if x != nil { + return x.Config + } + return "" +} + +func (x *RunLoadRequest) GetDetectors() string { + if x != nil { + return x.Detectors + } + return "" +} + +func (x *RunLoadRequest) GetCtpReadoutIncluded() bool { + if x != nil { + return x.CtpReadoutIncluded + } + return false +} + +// global + stdalone runs (common for ctpd/ltud): +type RunStartRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Runn uint32 `protobuf:"varint,1,opt,name=runn,proto3" json:"runn,omitempty"` // run number + Config string `protobuf:"bytes,2,opt,name=config,proto3" json:"config,omitempty"` + Mode RunStartRequest_Mode `protobuf:"varint,3,opt,name=mode,proto3,enum=ctpd.RunStartRequest_Mode" json:"mode,omitempty"` + PhBc string `protobuf:"bytes,4,opt,name=ph_bc,json=phBc,proto3" json:"ph_bc,omitempty"` // BC-downscaled rate of physics triggers + PhRnd string `protobuf:"bytes,5,opt,name=ph_rnd,json=phRnd,proto3" json:"ph_rnd,omitempty"` // rate of physics triggers generated randomly + // ph_ ...: "": use default (from config) + // + // N : number of BCs between 2 triggers + // 2.3khz rate of triggers: 2.3 khz + Detector string `protobuf:"bytes,6,opt,name=detector,proto3" json:"detector,omitempty"` // detector has to be given for stdalone runs, "" for global run +} + +func (x *RunStartRequest) Reset() { + *x = RunStartRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_ctpecs_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RunStartRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RunStartRequest) ProtoMessage() {} + +func (x *RunStartRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_ctpecs_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RunStartRequest.ProtoReflect.Descriptor instead. +func (*RunStartRequest) Descriptor() ([]byte, []int) { + return file_protos_ctpecs_proto_rawDescGZIP(), []int{2} +} + +func (x *RunStartRequest) GetRunn() uint32 { + if x != nil { + return x.Runn + } + return 0 +} + +func (x *RunStartRequest) GetConfig() string { + if x != nil { + return x.Config + } + return "" +} + +func (x *RunStartRequest) GetMode() RunStartRequest_Mode { + if x != nil { + return x.Mode + } + return RunStartRequest_NULL +} + +func (x *RunStartRequest) GetPhBc() string { + if x != nil { + return x.PhBc + } + return "" +} + +func (x *RunStartRequest) GetPhRnd() string { + if x != nil { + return x.PhRnd + } + return "" +} + +func (x *RunStartRequest) GetDetector() string { + if x != nil { + return x.Detector + } + return "" +} + +type RunStatusRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Runn uint32 `protobuf:"varint,1,opt,name=runn,proto3" json:"runn,omitempty"` +} + +func (x *RunStatusRequest) Reset() { + *x = RunStatusRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_ctpecs_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RunStatusRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RunStatusRequest) ProtoMessage() {} + +func (x *RunStatusRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_ctpecs_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RunStatusRequest.ProtoReflect.Descriptor instead. +func (*RunStatusRequest) Descriptor() ([]byte, []int) { + return file_protos_ctpecs_proto_rawDescGZIP(), []int{3} +} + +func (x *RunStatusRequest) GetRunn() uint32 { + if x != nil { + return x.Runn + } + return 0 +} + +type Empty struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *Empty) Reset() { + *x = Empty{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_ctpecs_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Empty) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Empty) ProtoMessage() {} + +func (x *Empty) ProtoReflect() protoreflect.Message { + mi := &file_protos_ctpecs_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Empty.ProtoReflect.Descriptor instead. +func (*Empty) Descriptor() ([]byte, []int) { + return file_protos_ctpecs_proto_rawDescGZIP(), []int{4} +} + +type RunStopRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Runn uint32 `protobuf:"varint,1,opt,name=runn,proto3" json:"runn,omitempty"` + Detector string `protobuf:"bytes,2,opt,name=detector,proto3" json:"detector,omitempty"` // only for stdalone runs (forced stop) +} + +func (x *RunStopRequest) Reset() { + *x = RunStopRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_ctpecs_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RunStopRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RunStopRequest) ProtoMessage() {} + +func (x *RunStopRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_ctpecs_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RunStopRequest.ProtoReflect.Descriptor instead. +func (*RunStopRequest) Descriptor() ([]byte, []int) { + return file_protos_ctpecs_proto_rawDescGZIP(), []int{5} +} + +func (x *RunStopRequest) GetRunn() uint32 { + if x != nil { + return x.Runn + } + return 0 +} + +func (x *RunStopRequest) GetDetector() string { + if x != nil { + return x.Detector + } + return "" +} + +type RunReply struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Rc int32 `protobuf:"varint,1,opt,name=rc,proto3" json:"rc,omitempty"` // 0: ok + // RunStatus rc: 0:active/running 1:paused 2:loaded 3:does not exist + Msg string `protobuf:"bytes,2,opt,name=msg,proto3" json:"msg,omitempty"` // response (usually "" when rc is 0) +} + +func (x *RunReply) Reset() { + *x = RunReply{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_ctpecs_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RunReply) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RunReply) ProtoMessage() {} + +func (x *RunReply) ProtoReflect() protoreflect.Message { + mi := &file_protos_ctpecs_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RunReply.ProtoReflect.Descriptor instead. +func (*RunReply) Descriptor() ([]byte, []int) { + return file_protos_ctpecs_proto_rawDescGZIP(), []int{6} +} + +func (x *RunReply) GetRc() int32 { + if x != nil { + return x.Rc + } + return 0 +} + +func (x *RunReply) GetMsg() string { + if x != nil { + return x.Msg + } + return "" +} + +var File_protos_ctpecs_proto protoreflect.FileDescriptor + +var file_protos_ctpecs_proto_rawDesc = []byte{ + 0x0a, 0x13, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x63, 0x74, 0x70, 0x65, 0x63, 0x73, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x04, 0x63, 0x74, 0x70, 0x64, 0x22, 0x4b, 0x0a, 0x11, 0x52, + 0x75, 0x6e, 0x50, 0x72, 0x65, 0x70, 0x61, 0x72, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x18, 0x0a, 0x07, 0x72, 0x75, 0x6e, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x07, 0x72, 0x75, 0x6e, 0x74, 0x79, 0x70, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x64, 0x65, + 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x64, + 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x22, 0x8c, 0x01, 0x0a, 0x0e, 0x52, 0x75, 0x6e, + 0x4c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x72, + 0x75, 0x6e, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x72, 0x75, 0x6e, 0x6e, 0x12, + 0x16, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1c, 0x0a, 0x09, 0x64, 0x65, 0x74, 0x65, 0x63, + 0x74, 0x6f, 0x72, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x64, 0x65, 0x74, 0x65, + 0x63, 0x74, 0x6f, 0x72, 0x73, 0x12, 0x30, 0x0a, 0x14, 0x63, 0x74, 0x70, 0x5f, 0x72, 0x65, 0x61, + 0x64, 0x6f, 0x75, 0x74, 0x5f, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x64, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x12, 0x63, 0x74, 0x70, 0x52, 0x65, 0x61, 0x64, 0x6f, 0x75, 0x74, 0x49, + 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x64, 0x22, 0xdb, 0x01, 0x0a, 0x0f, 0x52, 0x75, 0x6e, 0x53, + 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x72, + 0x75, 0x6e, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x72, 0x75, 0x6e, 0x6e, 0x12, + 0x16, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x2e, 0x0a, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x63, 0x74, 0x70, 0x64, 0x2e, 0x52, 0x75, 0x6e, + 0x53, 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x4d, 0x6f, 0x64, + 0x65, 0x52, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x12, 0x13, 0x0a, 0x05, 0x70, 0x68, 0x5f, 0x62, 0x63, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x68, 0x42, 0x63, 0x12, 0x15, 0x0a, 0x06, + 0x70, 0x68, 0x5f, 0x72, 0x6e, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x70, 0x68, + 0x52, 0x6e, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x64, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, + 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x22, + 0x24, 0x0a, 0x04, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x08, 0x0a, 0x04, 0x4e, 0x55, 0x4c, 0x4c, 0x10, + 0x00, 0x12, 0x08, 0x0a, 0x04, 0x54, 0x52, 0x49, 0x47, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x43, + 0x4f, 0x4e, 0x54, 0x10, 0x02, 0x22, 0x26, 0x0a, 0x10, 0x52, 0x75, 0x6e, 0x53, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x72, 0x75, 0x6e, + 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x72, 0x75, 0x6e, 0x6e, 0x22, 0x07, 0x0a, + 0x05, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x40, 0x0a, 0x0e, 0x52, 0x75, 0x6e, 0x53, 0x74, 0x6f, + 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x72, 0x75, 0x6e, 0x6e, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x72, 0x75, 0x6e, 0x6e, 0x12, 0x1a, 0x0a, 0x08, + 0x64, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, + 0x64, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x22, 0x2c, 0x0a, 0x08, 0x52, 0x75, 0x6e, 0x52, + 0x65, 0x70, 0x6c, 0x79, 0x12, 0x0e, 0x0a, 0x02, 0x72, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, + 0x52, 0x02, 0x72, 0x63, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x73, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x03, 0x6d, 0x73, 0x67, 0x32, 0x80, 0x04, 0x0a, 0x04, 0x43, 0x54, 0x50, 0x64, 0x12, + 0x3a, 0x0a, 0x0d, 0x50, 0x72, 0x65, 0x70, 0x61, 0x72, 0x65, 0x46, 0x6f, 0x72, 0x52, 0x75, 0x6e, + 0x12, 0x17, 0x2e, 0x63, 0x74, 0x70, 0x64, 0x2e, 0x52, 0x75, 0x6e, 0x50, 0x72, 0x65, 0x70, 0x61, + 0x72, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0e, 0x2e, 0x63, 0x74, 0x70, 0x64, + 0x2e, 0x52, 0x75, 0x6e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x31, 0x0a, 0x07, 0x52, + 0x75, 0x6e, 0x4c, 0x6f, 0x61, 0x64, 0x12, 0x14, 0x2e, 0x63, 0x74, 0x70, 0x64, 0x2e, 0x52, 0x75, + 0x6e, 0x4c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0e, 0x2e, 0x63, + 0x74, 0x70, 0x64, 0x2e, 0x52, 0x75, 0x6e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x33, + 0x0a, 0x09, 0x52, 0x75, 0x6e, 0x55, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x14, 0x2e, 0x63, 0x74, + 0x70, 0x64, 0x2e, 0x52, 0x75, 0x6e, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x0e, 0x2e, 0x63, 0x74, 0x70, 0x64, 0x2e, 0x52, 0x75, 0x6e, 0x52, 0x65, 0x70, 0x6c, + 0x79, 0x22, 0x00, 0x12, 0x33, 0x0a, 0x08, 0x52, 0x75, 0x6e, 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, + 0x15, 0x2e, 0x63, 0x74, 0x70, 0x64, 0x2e, 0x52, 0x75, 0x6e, 0x53, 0x74, 0x61, 0x72, 0x74, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0e, 0x2e, 0x63, 0x74, 0x70, 0x64, 0x2e, 0x52, 0x75, + 0x6e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x35, 0x0a, 0x09, 0x52, 0x75, 0x6e, 0x53, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x16, 0x2e, 0x63, 0x74, 0x70, 0x64, 0x2e, 0x52, 0x75, 0x6e, + 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0e, 0x2e, + 0x63, 0x74, 0x70, 0x64, 0x2e, 0x52, 0x75, 0x6e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, + 0x28, 0x0a, 0x07, 0x52, 0x75, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x0b, 0x2e, 0x63, 0x74, 0x70, + 0x64, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x0e, 0x2e, 0x63, 0x74, 0x70, 0x64, 0x2e, 0x52, + 0x75, 0x6e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x31, 0x0a, 0x07, 0x52, 0x75, 0x6e, + 0x53, 0x74, 0x6f, 0x70, 0x12, 0x14, 0x2e, 0x63, 0x74, 0x70, 0x64, 0x2e, 0x52, 0x75, 0x6e, 0x53, + 0x74, 0x6f, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0e, 0x2e, 0x63, 0x74, 0x70, + 0x64, 0x2e, 0x52, 0x75, 0x6e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x33, 0x0a, 0x09, + 0x52, 0x75, 0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x14, 0x2e, 0x63, 0x74, 0x70, 0x64, + 0x2e, 0x52, 0x75, 0x6e, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x0e, 0x2e, 0x63, 0x74, 0x70, 0x64, 0x2e, 0x52, 0x75, 0x6e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, + 0x00, 0x12, 0x2b, 0x0a, 0x0a, 0x52, 0x75, 0x6e, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x75, 0x70, 0x12, + 0x0b, 0x2e, 0x63, 0x74, 0x70, 0x64, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x0e, 0x2e, 0x63, + 0x74, 0x70, 0x64, 0x2e, 0x52, 0x75, 0x6e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x29, + 0x0a, 0x08, 0x54, 0x50, 0x43, 0x52, 0x65, 0x73, 0x65, 0x74, 0x12, 0x0b, 0x2e, 0x63, 0x74, 0x70, + 0x64, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x0e, 0x2e, 0x63, 0x74, 0x70, 0x64, 0x2e, 0x52, + 0x75, 0x6e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x42, 0x44, 0x5a, 0x42, 0x67, 0x69, 0x74, + 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x41, 0x6c, 0x69, 0x63, 0x65, 0x4f, 0x32, 0x47, + 0x72, 0x6f, 0x75, 0x70, 0x2f, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2f, 0x63, 0x6f, 0x72, + 0x65, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x74, 0x72, + 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x3b, 0x63, 0x74, 0x70, 0x65, 0x63, 0x73, 0x62, + 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_protos_ctpecs_proto_rawDescOnce sync.Once + file_protos_ctpecs_proto_rawDescData = file_protos_ctpecs_proto_rawDesc +) + +func file_protos_ctpecs_proto_rawDescGZIP() []byte { + file_protos_ctpecs_proto_rawDescOnce.Do(func() { + file_protos_ctpecs_proto_rawDescData = protoimpl.X.CompressGZIP(file_protos_ctpecs_proto_rawDescData) + }) + return file_protos_ctpecs_proto_rawDescData +} + +var file_protos_ctpecs_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_protos_ctpecs_proto_msgTypes = make([]protoimpl.MessageInfo, 7) +var file_protos_ctpecs_proto_goTypes = []interface{}{ + (RunStartRequest_Mode)(0), // 0: ctpd.RunStartRequest.Mode + (*RunPrepareRequest)(nil), // 1: ctpd.RunPrepareRequest + (*RunLoadRequest)(nil), // 2: ctpd.RunLoadRequest + (*RunStartRequest)(nil), // 3: ctpd.RunStartRequest + (*RunStatusRequest)(nil), // 4: ctpd.RunStatusRequest + (*Empty)(nil), // 5: ctpd.Empty + (*RunStopRequest)(nil), // 6: ctpd.RunStopRequest + (*RunReply)(nil), // 7: ctpd.RunReply +} +var file_protos_ctpecs_proto_depIdxs = []int32{ + 0, // 0: ctpd.RunStartRequest.mode:type_name -> ctpd.RunStartRequest.Mode + 1, // 1: ctpd.CTPd.PrepareForRun:input_type -> ctpd.RunPrepareRequest + 2, // 2: ctpd.CTPd.RunLoad:input_type -> ctpd.RunLoadRequest + 6, // 3: ctpd.CTPd.RunUnload:input_type -> ctpd.RunStopRequest + 3, // 4: ctpd.CTPd.RunStart:input_type -> ctpd.RunStartRequest + 4, // 5: ctpd.CTPd.RunStatus:input_type -> ctpd.RunStatusRequest + 5, // 6: ctpd.CTPd.RunList:input_type -> ctpd.Empty + 6, // 7: ctpd.CTPd.RunStop:input_type -> ctpd.RunStopRequest + 6, // 8: ctpd.CTPd.RunConfig:input_type -> ctpd.RunStopRequest + 5, // 9: ctpd.CTPd.RunCleanup:input_type -> ctpd.Empty + 5, // 10: ctpd.CTPd.TPCReset:input_type -> ctpd.Empty + 7, // 11: ctpd.CTPd.PrepareForRun:output_type -> ctpd.RunReply + 7, // 12: ctpd.CTPd.RunLoad:output_type -> ctpd.RunReply + 7, // 13: ctpd.CTPd.RunUnload:output_type -> ctpd.RunReply + 7, // 14: ctpd.CTPd.RunStart:output_type -> ctpd.RunReply + 7, // 15: ctpd.CTPd.RunStatus:output_type -> ctpd.RunReply + 7, // 16: ctpd.CTPd.RunList:output_type -> ctpd.RunReply + 7, // 17: ctpd.CTPd.RunStop:output_type -> ctpd.RunReply + 7, // 18: ctpd.CTPd.RunConfig:output_type -> ctpd.RunReply + 7, // 19: ctpd.CTPd.RunCleanup:output_type -> ctpd.RunReply + 7, // 20: ctpd.CTPd.TPCReset:output_type -> ctpd.RunReply + 11, // [11:21] is the sub-list for method output_type + 1, // [1:11] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name +} + +func init() { file_protos_ctpecs_proto_init() } +func file_protos_ctpecs_proto_init() { + if File_protos_ctpecs_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_protos_ctpecs_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RunPrepareRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_ctpecs_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RunLoadRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_ctpecs_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RunStartRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_ctpecs_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RunStatusRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_ctpecs_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Empty); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_ctpecs_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RunStopRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_ctpecs_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RunReply); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_protos_ctpecs_proto_rawDesc, + NumEnums: 1, + NumMessages: 7, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_protos_ctpecs_proto_goTypes, + DependencyIndexes: file_protos_ctpecs_proto_depIdxs, + EnumInfos: file_protos_ctpecs_proto_enumTypes, + MessageInfos: file_protos_ctpecs_proto_msgTypes, + }.Build() + File_protos_ctpecs_proto = out.File + file_protos_ctpecs_proto_rawDesc = nil + file_protos_ctpecs_proto_goTypes = nil + file_protos_ctpecs_proto_depIdxs = nil +} diff --git a/core/integration/trg/protos/ctpecs.proto b/core/integration/trg/protos/ctpecs.proto new file mode 100644 index 000000000..90a65c7a8 --- /dev/null +++ b/core/integration/trg/protos/ctpecs.proto @@ -0,0 +1,71 @@ +syntax = "proto3"; +package ctpd; +option go_package = "github.com/AliceO2Group/Control/core/integration/trg/protos;ctpecs"; + +// ctpd interface for ECS +service CTPd { + // global run: + rpc PrepareForRun (RunPrepareRequest) returns (RunReply) {} + rpc RunLoad (RunLoadRequest) returns (RunReply) {} + rpc RunUnload (RunStopRequest) returns (RunReply) {} + // stdalone and global runs: + rpc RunStart (RunStartRequest) returns (RunReply) {} + rpc RunStatus (RunStatusRequest) returns (RunReply) {} + rpc RunList (Empty) returns (RunReply) {} // runs <1000 not shown + rpc RunStop (RunStopRequest) returns (RunReply) {} + rpc RunConfig (RunStopRequest) returns (RunReply) {} + rpc RunCleanup (Empty) returns (RunReply) {} + rpc TPCReset (Empty) returns (RunReply) {} +} +//import "ctpecs_m.proto"; +// global runs only: +message RunPrepareRequest { + string runtype = 1; + string detectors= 2; // as in RunLoadRequest +} +message RunLoadRequest { + uint32 runn = 1; // run number + string config = 2; + // -config, i.e. the content of partname.pd + // -partname -the content is in ctp config dir partname.pd + // -key in case the config is available in Consul[key] + string detectors= 3; // available detectors, "" -all required must be ready + bool ctp_readout_included = 4; // true: ctp readout included in the run +} +// global + stdalone runs (common for ctpd/ltud): +message RunStartRequest { + uint32 runn = 1; // run number + string config = 2; + // -config, e.g. "trig\nPH_bc 100 inf" or + // -key in case the config is available in Consul[key] + // -"" use default configuration + // + // Optional parameters follows, overriding parameters in config string + // for the begining let's agree on three parameters : + enum Mode { + NULL = 0; // use default (from config) + TRIG = 1; + CONT = 2; + } + Mode mode = 3; + string ph_bc = 4; // BC-downscaled rate of physics triggers + string ph_rnd = 5; // rate of physics triggers generated randomly + // ph_ ...: "": use default (from config) + // N : number of BCs between 2 triggers + // 2.3khz rate of triggers: 2.3 khz + string detector = 6; // detector has to be given for stdalone runs, "" for global run +} +message RunStatusRequest { + uint32 runn = 1; +} +message Empty {} +message RunStopRequest { + uint32 runn = 1; + string detector = 2; // only for stdalone runs (forced stop) +} +message RunReply { + int32 rc = 1; // 0: ok + // RunStatus rc: 0:active/running 1:paused 2:loaded 3:does not exist + string msg = 2; // response (usually "" when rc is 0) +} + diff --git a/core/integration/trg/protos/ctpecs_grpc.pb.go b/core/integration/trg/protos/ctpecs_grpc.pb.go new file mode 100644 index 000000000..abde79d52 --- /dev/null +++ b/core/integration/trg/protos/ctpecs_grpc.pb.go @@ -0,0 +1,444 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.3.0 +// - protoc v5.29.3 +// source: protos/ctpecs.proto + +package ctpecs + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +const ( + CTPd_PrepareForRun_FullMethodName = "/ctpd.CTPd/PrepareForRun" + CTPd_RunLoad_FullMethodName = "/ctpd.CTPd/RunLoad" + CTPd_RunUnload_FullMethodName = "/ctpd.CTPd/RunUnload" + CTPd_RunStart_FullMethodName = "/ctpd.CTPd/RunStart" + CTPd_RunStatus_FullMethodName = "/ctpd.CTPd/RunStatus" + CTPd_RunList_FullMethodName = "/ctpd.CTPd/RunList" + CTPd_RunStop_FullMethodName = "/ctpd.CTPd/RunStop" + CTPd_RunConfig_FullMethodName = "/ctpd.CTPd/RunConfig" + CTPd_RunCleanup_FullMethodName = "/ctpd.CTPd/RunCleanup" + CTPd_TPCReset_FullMethodName = "/ctpd.CTPd/TPCReset" +) + +// CTPdClient is the client API for CTPd service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type CTPdClient interface { + // global run: + PrepareForRun(ctx context.Context, in *RunPrepareRequest, opts ...grpc.CallOption) (*RunReply, error) + RunLoad(ctx context.Context, in *RunLoadRequest, opts ...grpc.CallOption) (*RunReply, error) + RunUnload(ctx context.Context, in *RunStopRequest, opts ...grpc.CallOption) (*RunReply, error) + // stdalone and global runs: + RunStart(ctx context.Context, in *RunStartRequest, opts ...grpc.CallOption) (*RunReply, error) + RunStatus(ctx context.Context, in *RunStatusRequest, opts ...grpc.CallOption) (*RunReply, error) + RunList(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*RunReply, error) + RunStop(ctx context.Context, in *RunStopRequest, opts ...grpc.CallOption) (*RunReply, error) + RunConfig(ctx context.Context, in *RunStopRequest, opts ...grpc.CallOption) (*RunReply, error) + RunCleanup(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*RunReply, error) + TPCReset(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*RunReply, error) +} + +type cTPdClient struct { + cc grpc.ClientConnInterface +} + +func NewCTPdClient(cc grpc.ClientConnInterface) CTPdClient { + return &cTPdClient{cc} +} + +func (c *cTPdClient) PrepareForRun(ctx context.Context, in *RunPrepareRequest, opts ...grpc.CallOption) (*RunReply, error) { + out := new(RunReply) + err := c.cc.Invoke(ctx, CTPd_PrepareForRun_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *cTPdClient) RunLoad(ctx context.Context, in *RunLoadRequest, opts ...grpc.CallOption) (*RunReply, error) { + out := new(RunReply) + err := c.cc.Invoke(ctx, CTPd_RunLoad_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *cTPdClient) RunUnload(ctx context.Context, in *RunStopRequest, opts ...grpc.CallOption) (*RunReply, error) { + out := new(RunReply) + err := c.cc.Invoke(ctx, CTPd_RunUnload_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *cTPdClient) RunStart(ctx context.Context, in *RunStartRequest, opts ...grpc.CallOption) (*RunReply, error) { + out := new(RunReply) + err := c.cc.Invoke(ctx, CTPd_RunStart_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *cTPdClient) RunStatus(ctx context.Context, in *RunStatusRequest, opts ...grpc.CallOption) (*RunReply, error) { + out := new(RunReply) + err := c.cc.Invoke(ctx, CTPd_RunStatus_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *cTPdClient) RunList(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*RunReply, error) { + out := new(RunReply) + err := c.cc.Invoke(ctx, CTPd_RunList_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *cTPdClient) RunStop(ctx context.Context, in *RunStopRequest, opts ...grpc.CallOption) (*RunReply, error) { + out := new(RunReply) + err := c.cc.Invoke(ctx, CTPd_RunStop_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *cTPdClient) RunConfig(ctx context.Context, in *RunStopRequest, opts ...grpc.CallOption) (*RunReply, error) { + out := new(RunReply) + err := c.cc.Invoke(ctx, CTPd_RunConfig_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *cTPdClient) RunCleanup(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*RunReply, error) { + out := new(RunReply) + err := c.cc.Invoke(ctx, CTPd_RunCleanup_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *cTPdClient) TPCReset(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*RunReply, error) { + out := new(RunReply) + err := c.cc.Invoke(ctx, CTPd_TPCReset_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// CTPdServer is the server API for CTPd service. +// All implementations should embed UnimplementedCTPdServer +// for forward compatibility +type CTPdServer interface { + // global run: + PrepareForRun(context.Context, *RunPrepareRequest) (*RunReply, error) + RunLoad(context.Context, *RunLoadRequest) (*RunReply, error) + RunUnload(context.Context, *RunStopRequest) (*RunReply, error) + // stdalone and global runs: + RunStart(context.Context, *RunStartRequest) (*RunReply, error) + RunStatus(context.Context, *RunStatusRequest) (*RunReply, error) + RunList(context.Context, *Empty) (*RunReply, error) + RunStop(context.Context, *RunStopRequest) (*RunReply, error) + RunConfig(context.Context, *RunStopRequest) (*RunReply, error) + RunCleanup(context.Context, *Empty) (*RunReply, error) + TPCReset(context.Context, *Empty) (*RunReply, error) +} + +// UnimplementedCTPdServer should be embedded to have forward compatible implementations. +type UnimplementedCTPdServer struct { +} + +func (UnimplementedCTPdServer) PrepareForRun(context.Context, *RunPrepareRequest) (*RunReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method PrepareForRun not implemented") +} +func (UnimplementedCTPdServer) RunLoad(context.Context, *RunLoadRequest) (*RunReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method RunLoad not implemented") +} +func (UnimplementedCTPdServer) RunUnload(context.Context, *RunStopRequest) (*RunReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method RunUnload not implemented") +} +func (UnimplementedCTPdServer) RunStart(context.Context, *RunStartRequest) (*RunReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method RunStart not implemented") +} +func (UnimplementedCTPdServer) RunStatus(context.Context, *RunStatusRequest) (*RunReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method RunStatus not implemented") +} +func (UnimplementedCTPdServer) RunList(context.Context, *Empty) (*RunReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method RunList not implemented") +} +func (UnimplementedCTPdServer) RunStop(context.Context, *RunStopRequest) (*RunReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method RunStop not implemented") +} +func (UnimplementedCTPdServer) RunConfig(context.Context, *RunStopRequest) (*RunReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method RunConfig not implemented") +} +func (UnimplementedCTPdServer) RunCleanup(context.Context, *Empty) (*RunReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method RunCleanup not implemented") +} +func (UnimplementedCTPdServer) TPCReset(context.Context, *Empty) (*RunReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method TPCReset not implemented") +} + +// UnsafeCTPdServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to CTPdServer will +// result in compilation errors. +type UnsafeCTPdServer interface { + mustEmbedUnimplementedCTPdServer() +} + +func RegisterCTPdServer(s grpc.ServiceRegistrar, srv CTPdServer) { + s.RegisterService(&CTPd_ServiceDesc, srv) +} + +func _CTPd_PrepareForRun_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RunPrepareRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CTPdServer).PrepareForRun(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: CTPd_PrepareForRun_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CTPdServer).PrepareForRun(ctx, req.(*RunPrepareRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _CTPd_RunLoad_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RunLoadRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CTPdServer).RunLoad(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: CTPd_RunLoad_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CTPdServer).RunLoad(ctx, req.(*RunLoadRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _CTPd_RunUnload_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RunStopRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CTPdServer).RunUnload(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: CTPd_RunUnload_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CTPdServer).RunUnload(ctx, req.(*RunStopRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _CTPd_RunStart_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RunStartRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CTPdServer).RunStart(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: CTPd_RunStart_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CTPdServer).RunStart(ctx, req.(*RunStartRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _CTPd_RunStatus_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RunStatusRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CTPdServer).RunStatus(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: CTPd_RunStatus_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CTPdServer).RunStatus(ctx, req.(*RunStatusRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _CTPd_RunList_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(Empty) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CTPdServer).RunList(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: CTPd_RunList_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CTPdServer).RunList(ctx, req.(*Empty)) + } + return interceptor(ctx, in, info, handler) +} + +func _CTPd_RunStop_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RunStopRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CTPdServer).RunStop(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: CTPd_RunStop_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CTPdServer).RunStop(ctx, req.(*RunStopRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _CTPd_RunConfig_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RunStopRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CTPdServer).RunConfig(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: CTPd_RunConfig_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CTPdServer).RunConfig(ctx, req.(*RunStopRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _CTPd_RunCleanup_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(Empty) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CTPdServer).RunCleanup(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: CTPd_RunCleanup_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CTPdServer).RunCleanup(ctx, req.(*Empty)) + } + return interceptor(ctx, in, info, handler) +} + +func _CTPd_TPCReset_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(Empty) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CTPdServer).TPCReset(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: CTPd_TPCReset_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CTPdServer).TPCReset(ctx, req.(*Empty)) + } + return interceptor(ctx, in, info, handler) +} + +// CTPd_ServiceDesc is the grpc.ServiceDesc for CTPd service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var CTPd_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "ctpd.CTPd", + HandlerType: (*CTPdServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "PrepareForRun", + Handler: _CTPd_PrepareForRun_Handler, + }, + { + MethodName: "RunLoad", + Handler: _CTPd_RunLoad_Handler, + }, + { + MethodName: "RunUnload", + Handler: _CTPd_RunUnload_Handler, + }, + { + MethodName: "RunStart", + Handler: _CTPd_RunStart_Handler, + }, + { + MethodName: "RunStatus", + Handler: _CTPd_RunStatus_Handler, + }, + { + MethodName: "RunList", + Handler: _CTPd_RunList_Handler, + }, + { + MethodName: "RunStop", + Handler: _CTPd_RunStop_Handler, + }, + { + MethodName: "RunConfig", + Handler: _CTPd_RunConfig_Handler, + }, + { + MethodName: "RunCleanup", + Handler: _CTPd_RunCleanup_Handler, + }, + { + MethodName: "TPCReset", + Handler: _CTPd_TPCReset_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "protos/ctpecs.proto", +} diff --git a/core/integration/trg/trgutil.go b/core/integration/trg/trgutil.go new file mode 100644 index 000000000..c1a705d0b --- /dev/null +++ b/core/integration/trg/trgutil.go @@ -0,0 +1,164 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2022 CERN and copyright holders of ALICE O². + * Author: Teo Mrnjavac + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +// Package trg provides integration with the ALICE trigger system. +package trg + +import ( + "fmt" + "strconv" + "strings" + + "github.com/AliceO2Group/Control/common/system" +) + +type Run struct { + Cardinality RunCardinality + RunNumber uint32 + State State + Detectors []system.ID + Skip bool +} + +type Runs []Run + +type RunCardinality int +type State int + +const ( + __RunCardinality_NIL RunCardinality = iota + CTP_STANDALONE + CTP_GLOBAL + __RunCardinality_MAX +) + +const ( + __State_NIL State = iota + CTP_LOADED + CTP_RUNNING + __State_MAX +) + +// respose examples: +//ecsc>rlist +//rc:4 +//G 511707 R tpc run511707 +//G 511713 R its, mft, mid, mch, tof, trd, emc, cpv run511713 +//G 511714 R phs run511714 +//G 511715 R zdc run511715 +// +//ecsc>rlist +//rc:3 +//S 2222 R hmp +//S 2223 R fv0 +//G 2224 L its, tpc run2224 + +func parseRunList(runCount int, payload string) (runs Runs, err []error) { + cleanPayload := strings.TrimSpace(payload) + if len(cleanPayload) == 0 { + // nothing to do + return + } + lines := strings.Split(strings.TrimSpace(cleanPayload), "\n") + if len(lines) != runCount { + err = append(err, fmt.Errorf("cannot parse run count mismatch in payload: %s", payload)) + return + } + for i, line := range lines { + run, parseErr := parseRunLine(line) + if parseErr != nil { + err = append(err, fmt.Errorf("cannot parse line %d: %s", i, parseErr.Error())) + continue + } + if !run.Skip { + runs = append(runs, run) + } + } + + return +} + +func parseRunLine(line string) (run Run, err error) { + // lst= lst+"S {:6} R {:3}\n".format(runn, self.sruns[runn]) + // lst= lst+"G {:6} {:1} {} {}\n".format(runn, st, ", ".join(self.gruns[runn].getDets()), self.gruns[runn].pname) + cols := strings.Fields(line) + // col 0 -> TRG TYPE + // col 1 -> RUN NUMBER + // col 2 -> TRG STATUS + // col 3 -> comma-seperated list of DETECTORS + + if len(cols) < 4 { + err = fmt.Errorf("cannot parse run state from line with less than 4 columns: %s", line) + return + } + + if cols[0] == "S" { + run.Cardinality = CTP_STANDALONE + run.State = CTP_RUNNING + } else if cols[0] == "G" { + run.Cardinality = CTP_GLOBAL + if cols[2] == "R" { + run.State = CTP_RUNNING + } else if cols[2] == "L" { + run.State = CTP_LOADED + } else { + run.State = __State_NIL + err = fmt.Errorf("cannot parse run state from line: %s", line) + return + } + } else { + run.Cardinality = __RunCardinality_NIL + err = fmt.Errorf("cannot parse run cardinality from line: %s", line) + return + } + + rn64, err := strconv.ParseUint(cols[1], 10, 32) + run.RunNumber = uint32(rn64) + if err != nil { + err = fmt.Errorf("cannot parse run number from line: %s", line) + return + } + + run.Detectors = make([]system.ID, 0) + + detectorsSSlice := strings.Split(cols[3], ",") + for _, item := range detectorsSSlice { + // don't interfere with daq + // also daq not in known system ids -> results in error + if strings.TrimSpace(item) == "daq" { + run.Skip = true + continue + } + + var det system.ID + det, err = system.IDString(strings.TrimSpace(item)) + if err != nil { + err = fmt.Errorf("cannot parse detector %s from line: %s", item, line) + return + } + run.Detectors = append(run.Detectors, det) + } + + return +} diff --git a/core/protos/o2control.pb.go b/core/protos/o2control.pb.go index db73e9e78..4124ea0a0 100644 --- a/core/protos/o2control.pb.go +++ b/core/protos/o2control.pb.go @@ -1,58 +1,85 @@ -// Code generated by protoc-gen-gogo. DO NOT EDIT. +// +// === This file is part of ALICE O² === +// +// Copyright 2018 CERN and copyright holders of ALICE O². +// Author: Teo Mrnjavac +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// In applying this license CERN does not waive the privileges and +// immunities granted to it by virtue of its status as an +// Intergovernmental Organization or submit itself to any jurisdiction. + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.34.1 +// protoc v5.29.3 // source: protos/o2control.proto package pb import ( - context "context" - fmt "fmt" - proto "github.com/golang/protobuf/proto" - grpc "google.golang.org/grpc" - io "io" - math "math" + protos "github.com/AliceO2Group/Control/common/protos" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" ) -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package - -type StatusUpdate_Level int32 - const ( - StatusUpdate_DEBUG StatusUpdate_Level = 0 - StatusUpdate_INFO StatusUpdate_Level = 1 - StatusUpdate_WARNING StatusUpdate_Level = 2 - StatusUpdate_ERROR StatusUpdate_Level = 3 + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) -var StatusUpdate_Level_name = map[int32]string{ - 0: "DEBUG", - 1: "INFO", - 2: "WARNING", - 3: "ERROR", -} - -var StatusUpdate_Level_value = map[string]int32{ - "DEBUG": 0, - "INFO": 1, - "WARNING": 2, - "ERROR": 3, -} - -func (x StatusUpdate_Level) String() string { - return proto.EnumName(StatusUpdate_Level_name, int32(x)) -} - -func (StatusUpdate_Level) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{3, 0} -} +// Symbols defined in public import of protos/events.proto. + +type OpStatus = protos.OpStatus + +const OpStatus_NULL = protos.OpStatus_NULL +const OpStatus_STARTED = protos.OpStatus_STARTED +const OpStatus_ONGOING = protos.OpStatus_ONGOING +const OpStatus_DONE_OK = protos.OpStatus_DONE_OK +const OpStatus_DONE_ERROR = protos.OpStatus_DONE_ERROR +const OpStatus_DONE_TIMEOUT = protos.OpStatus_DONE_TIMEOUT + +var OpStatus_name = protos.OpStatus_name +var OpStatus_value = protos.OpStatus_value + +type Ev_MetaEvent_MesosHeartbeat = protos.Ev_MetaEvent_MesosHeartbeat +type Ev_MetaEvent_CoreStart = protos.Ev_MetaEvent_CoreStart +type Ev_MetaEvent_FrameworkEvent = protos.Ev_MetaEvent_FrameworkEvent +type Ev_EnvironmentEvent = protos.Ev_EnvironmentEvent +type Traits = protos.Traits +type Ev_TaskEvent = protos.Ev_TaskEvent +type Ev_CallEvent = protos.Ev_CallEvent +type Ev_RoleEvent = protos.Ev_RoleEvent +type Ev_IntegratedServiceEvent = protos.Ev_IntegratedServiceEvent +type Ev_RunEvent = protos.Ev_RunEvent +type Ev_BeamModeEvent = protos.Ev_BeamModeEvent +type Event = protos.Event +type Event_EnvironmentEvent = protos.Event_EnvironmentEvent +type Event_TaskEvent = protos.Event_TaskEvent +type Event_RoleEvent = protos.Event_RoleEvent +type Event_CallEvent = protos.Event_CallEvent +type Event_IntegratedServiceEvent = protos.Event_IntegratedServiceEvent +type Event_RunEvent = protos.Event_RunEvent +type Event_FrameworkEvent = protos.Event_FrameworkEvent +type Event_MesosHeartbeatEvent = protos.Event_MesosHeartbeatEvent +type Event_CoreStartEvent = protos.Event_CoreStartEvent +type Event_BeamModeEvent = protos.Event_BeamModeEvent type ControlEnvironmentRequest_Optype int32 @@ -63,32 +90,56 @@ const ( ControlEnvironmentRequest_CONFIGURE ControlEnvironmentRequest_Optype = 3 ControlEnvironmentRequest_RESET ControlEnvironmentRequest_Optype = 4 ControlEnvironmentRequest_GO_ERROR ControlEnvironmentRequest_Optype = 5 + ControlEnvironmentRequest_DEPLOY ControlEnvironmentRequest_Optype = 6 ) -var ControlEnvironmentRequest_Optype_name = map[int32]string{ - 0: "NOOP", - 1: "START_ACTIVITY", - 2: "STOP_ACTIVITY", - 3: "CONFIGURE", - 4: "RESET", - 5: "GO_ERROR", -} +// Enum value maps for ControlEnvironmentRequest_Optype. +var ( + ControlEnvironmentRequest_Optype_name = map[int32]string{ + 0: "NOOP", + 1: "START_ACTIVITY", + 2: "STOP_ACTIVITY", + 3: "CONFIGURE", + 4: "RESET", + 5: "GO_ERROR", + 6: "DEPLOY", + } + ControlEnvironmentRequest_Optype_value = map[string]int32{ + "NOOP": 0, + "START_ACTIVITY": 1, + "STOP_ACTIVITY": 2, + "CONFIGURE": 3, + "RESET": 4, + "GO_ERROR": 5, + "DEPLOY": 6, + } +) -var ControlEnvironmentRequest_Optype_value = map[string]int32{ - "NOOP": 0, - "START_ACTIVITY": 1, - "STOP_ACTIVITY": 2, - "CONFIGURE": 3, - "RESET": 4, - "GO_ERROR": 5, +func (x ControlEnvironmentRequest_Optype) Enum() *ControlEnvironmentRequest_Optype { + p := new(ControlEnvironmentRequest_Optype) + *p = x + return p } func (x ControlEnvironmentRequest_Optype) String() string { - return proto.EnumName(ControlEnvironmentRequest_Optype_name, int32(x)) + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (ControlEnvironmentRequest_Optype) Descriptor() protoreflect.EnumDescriptor { + return file_protos_o2control_proto_enumTypes[0].Descriptor() +} + +func (ControlEnvironmentRequest_Optype) Type() protoreflect.EnumType { + return &file_protos_o2control_proto_enumTypes[0] } +func (x ControlEnvironmentRequest_Optype) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use ControlEnvironmentRequest_Optype.Descriptor instead. func (ControlEnvironmentRequest_Optype) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{16, 0} + return file_protos_o2control_proto_rawDescGZIP(), []int{15, 0} } type EnvironmentOperation_Optype int32 @@ -99,12971 +150,5960 @@ const ( EnvironmentOperation_ADD_ROLE EnvironmentOperation_Optype = 4 ) -var EnvironmentOperation_Optype_name = map[int32]string{ - 0: "NOOP", - 3: "REMOVE_ROLE", - 4: "ADD_ROLE", -} +// Enum value maps for EnvironmentOperation_Optype. +var ( + EnvironmentOperation_Optype_name = map[int32]string{ + 0: "NOOP", + 3: "REMOVE_ROLE", + 4: "ADD_ROLE", + } + EnvironmentOperation_Optype_value = map[string]int32{ + "NOOP": 0, + "REMOVE_ROLE": 3, + "ADD_ROLE": 4, + } +) -var EnvironmentOperation_Optype_value = map[string]int32{ - "NOOP": 0, - "REMOVE_ROLE": 3, - "ADD_ROLE": 4, +func (x EnvironmentOperation_Optype) Enum() *EnvironmentOperation_Optype { + p := new(EnvironmentOperation_Optype) + *p = x + return p } func (x EnvironmentOperation_Optype) String() string { - return proto.EnumName(EnvironmentOperation_Optype_name, int32(x)) + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } -func (EnvironmentOperation_Optype) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{19, 0} +func (EnvironmentOperation_Optype) Descriptor() protoreflect.EnumDescriptor { + return file_protos_o2control_proto_enumTypes[1].Descriptor() } -type Event_MesosHeartbeat struct { - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` +func (EnvironmentOperation_Optype) Type() protoreflect.EnumType { + return &file_protos_o2control_proto_enumTypes[1] } -func (m *Event_MesosHeartbeat) Reset() { *m = Event_MesosHeartbeat{} } -func (m *Event_MesosHeartbeat) String() string { return proto.CompactTextString(m) } -func (*Event_MesosHeartbeat) ProtoMessage() {} -func (*Event_MesosHeartbeat) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{0} +func (x EnvironmentOperation_Optype) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) } -func (m *Event_MesosHeartbeat) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *Event_MesosHeartbeat) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_Event_MesosHeartbeat.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err - } - return b[:n], nil - } -} -func (m *Event_MesosHeartbeat) XXX_Merge(src proto.Message) { - xxx_messageInfo_Event_MesosHeartbeat.Merge(m, src) -} -func (m *Event_MesosHeartbeat) XXX_Size() int { - return m.Size() -} -func (m *Event_MesosHeartbeat) XXX_DiscardUnknown() { - xxx_messageInfo_Event_MesosHeartbeat.DiscardUnknown(m) + +// Deprecated: Use EnvironmentOperation_Optype.Descriptor instead. +func (EnvironmentOperation_Optype) EnumDescriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{18, 0} } -var xxx_messageInfo_Event_MesosHeartbeat proto.InternalMessageInfo +type VarSpecMessage_UiWidget int32 -//////////////////////////////////////// -// Global status -//////////////////////////////////////// -type StatusRequest struct { - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} +const ( + VarSpecMessage_editBox VarSpecMessage_UiWidget = 0 // plain string input line, can accept types number (like a spinBox) and string + VarSpecMessage_slider VarSpecMessage_UiWidget = 1 // input widget exclusively for numbers, range allowedValues[0]-[1] + VarSpecMessage_listBox VarSpecMessage_UiWidget = 2 // displays a list of items, can accept types number, string or list; if number/string ==> single selection, otherwise multiple selection allowed + VarSpecMessage_dropDownBox VarSpecMessage_UiWidget = 3 + VarSpecMessage_comboBox VarSpecMessage_UiWidget = 4 + VarSpecMessage_radioButtonBox VarSpecMessage_UiWidget = 5 + VarSpecMessage_checkBox VarSpecMessage_UiWidget = 6 +) -func (m *StatusRequest) Reset() { *m = StatusRequest{} } -func (m *StatusRequest) String() string { return proto.CompactTextString(m) } -func (*StatusRequest) ProtoMessage() {} -func (*StatusRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{1} -} -func (m *StatusRequest) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *StatusRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_StatusRequest.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err - } - return b[:n], nil +// Enum value maps for VarSpecMessage_UiWidget. +var ( + VarSpecMessage_UiWidget_name = map[int32]string{ + 0: "editBox", + 1: "slider", + 2: "listBox", + 3: "dropDownBox", + 4: "comboBox", + 5: "radioButtonBox", + 6: "checkBox", + } + VarSpecMessage_UiWidget_value = map[string]int32{ + "editBox": 0, + "slider": 1, + "listBox": 2, + "dropDownBox": 3, + "comboBox": 4, + "radioButtonBox": 5, + "checkBox": 6, } -} -func (m *StatusRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_StatusRequest.Merge(m, src) -} -func (m *StatusRequest) XXX_Size() int { - return m.Size() -} -func (m *StatusRequest) XXX_DiscardUnknown() { - xxx_messageInfo_StatusRequest.DiscardUnknown(m) -} - -var xxx_messageInfo_StatusRequest proto.InternalMessageInfo +) -type StatusReply struct { - State string `protobuf:"bytes,1,opt,name=state,proto3" json:"state,omitempty"` - StatusUpdates []*StatusUpdate `protobuf:"bytes,2,rep,name=statusUpdates,proto3" json:"statusUpdates,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` +func (x VarSpecMessage_UiWidget) Enum() *VarSpecMessage_UiWidget { + p := new(VarSpecMessage_UiWidget) + *p = x + return p } -func (m *StatusReply) Reset() { *m = StatusReply{} } -func (m *StatusReply) String() string { return proto.CompactTextString(m) } -func (*StatusReply) ProtoMessage() {} -func (*StatusReply) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{2} -} -func (m *StatusReply) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) +func (x VarSpecMessage_UiWidget) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } -func (m *StatusReply) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_StatusReply.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err - } - return b[:n], nil - } + +func (VarSpecMessage_UiWidget) Descriptor() protoreflect.EnumDescriptor { + return file_protos_o2control_proto_enumTypes[2].Descriptor() } -func (m *StatusReply) XXX_Merge(src proto.Message) { - xxx_messageInfo_StatusReply.Merge(m, src) + +func (VarSpecMessage_UiWidget) Type() protoreflect.EnumType { + return &file_protos_o2control_proto_enumTypes[2] } -func (m *StatusReply) XXX_Size() int { - return m.Size() + +func (x VarSpecMessage_UiWidget) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) } -func (m *StatusReply) XXX_DiscardUnknown() { - xxx_messageInfo_StatusReply.DiscardUnknown(m) + +// Deprecated: Use VarSpecMessage_UiWidget.Descriptor instead. +func (VarSpecMessage_UiWidget) EnumDescriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{43, 0} } -var xxx_messageInfo_StatusReply proto.InternalMessageInfo +type VarSpecMessage_Type int32 -func (m *StatusReply) GetState() string { - if m != nil { - return m.State - } - return "" -} +const ( + VarSpecMessage_string VarSpecMessage_Type = 0 + VarSpecMessage_number VarSpecMessage_Type = 1 + VarSpecMessage_bool VarSpecMessage_Type = 2 + VarSpecMessage_list VarSpecMessage_Type = 3 + VarSpecMessage_map VarSpecMessage_Type = 4 +) -func (m *StatusReply) GetStatusUpdates() []*StatusUpdate { - if m != nil { - return m.StatusUpdates +// Enum value maps for VarSpecMessage_Type. +var ( + VarSpecMessage_Type_name = map[int32]string{ + 0: "string", + 1: "number", + 2: "bool", + 3: "list", + 4: "map", + } + VarSpecMessage_Type_value = map[string]int32{ + "string": 0, + "number": 1, + "bool": 2, + "list": 3, + "map": 4, } - return nil -} +) -type StatusUpdate struct { - Level StatusUpdate_Level `protobuf:"varint,1,opt,name=level,proto3,enum=o2control.StatusUpdate_Level" json:"level,omitempty"` - // Types that are valid to be assigned to Event: - // *StatusUpdate_MesosHeartbeat - Event isStatusUpdate_Event `protobuf_oneof:"Event"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *StatusUpdate) Reset() { *m = StatusUpdate{} } -func (m *StatusUpdate) String() string { return proto.CompactTextString(m) } -func (*StatusUpdate) ProtoMessage() {} -func (*StatusUpdate) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{3} -} -func (m *StatusUpdate) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *StatusUpdate) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_StatusUpdate.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err - } - return b[:n], nil - } -} -func (m *StatusUpdate) XXX_Merge(src proto.Message) { - xxx_messageInfo_StatusUpdate.Merge(m, src) +func (x VarSpecMessage_Type) Enum() *VarSpecMessage_Type { + p := new(VarSpecMessage_Type) + *p = x + return p } -func (m *StatusUpdate) XXX_Size() int { - return m.Size() + +func (x VarSpecMessage_Type) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } -func (m *StatusUpdate) XXX_DiscardUnknown() { - xxx_messageInfo_StatusUpdate.DiscardUnknown(m) + +func (VarSpecMessage_Type) Descriptor() protoreflect.EnumDescriptor { + return file_protos_o2control_proto_enumTypes[3].Descriptor() } -var xxx_messageInfo_StatusUpdate proto.InternalMessageInfo +func (VarSpecMessage_Type) Type() protoreflect.EnumType { + return &file_protos_o2control_proto_enumTypes[3] +} -type isStatusUpdate_Event interface { - isStatusUpdate_Event() - MarshalTo([]byte) (int, error) - Size() int +func (x VarSpecMessage_Type) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) } -type StatusUpdate_MesosHeartbeat struct { - MesosHeartbeat *Event_MesosHeartbeat `protobuf:"bytes,2,opt,name=mesosHeartbeat,proto3,oneof"` +// Deprecated: Use VarSpecMessage_Type.Descriptor instead. +func (VarSpecMessage_Type) EnumDescriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{43, 1} } -func (*StatusUpdate_MesosHeartbeat) isStatusUpdate_Event() {} +type SubscribeRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields -func (m *StatusUpdate) GetEvent() isStatusUpdate_Event { - if m != nil { - return m.Event - } - return nil + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` } -func (m *StatusUpdate) GetLevel() StatusUpdate_Level { - if m != nil { - return m.Level +func (x *SubscribeRequest) Reset() { + *x = SubscribeRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return StatusUpdate_DEBUG } -func (m *StatusUpdate) GetMesosHeartbeat() *Event_MesosHeartbeat { - if x, ok := m.GetEvent().(*StatusUpdate_MesosHeartbeat); ok { - return x.MesosHeartbeat - } - return nil +func (x *SubscribeRequest) String() string { + return protoimpl.X.MessageStringOf(x) } -// XXX_OneofFuncs is for the internal use of the proto package. -func (*StatusUpdate) XXX_OneofFuncs() (func(msg proto.Message, b *proto.Buffer) error, func(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error), func(msg proto.Message) (n int), []interface{}) { - return _StatusUpdate_OneofMarshaler, _StatusUpdate_OneofUnmarshaler, _StatusUpdate_OneofSizer, []interface{}{ - (*StatusUpdate_MesosHeartbeat)(nil), - } -} +func (*SubscribeRequest) ProtoMessage() {} -func _StatusUpdate_OneofMarshaler(msg proto.Message, b *proto.Buffer) error { - m := msg.(*StatusUpdate) - // Event - switch x := m.Event.(type) { - case *StatusUpdate_MesosHeartbeat: - _ = b.EncodeVarint(2<<3 | proto.WireBytes) - if err := b.EncodeMessage(x.MesosHeartbeat); err != nil { - return err +func (x *SubscribeRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) } - case nil: - default: - return fmt.Errorf("StatusUpdate.Event has unexpected type %T", x) + return ms } - return nil + return mi.MessageOf(x) } -func _StatusUpdate_OneofUnmarshaler(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error) { - m := msg.(*StatusUpdate) - switch tag { - case 2: // Event.mesosHeartbeat - if wire != proto.WireBytes { - return true, proto.ErrInternalBadWireType - } - msg := new(Event_MesosHeartbeat) - err := b.DecodeMessage(msg) - m.Event = &StatusUpdate_MesosHeartbeat{msg} - return true, err - default: - return false, nil - } +// Deprecated: Use SubscribeRequest.ProtoReflect.Descriptor instead. +func (*SubscribeRequest) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{0} } -func _StatusUpdate_OneofSizer(msg proto.Message) (n int) { - m := msg.(*StatusUpdate) - // Event - switch x := m.Event.(type) { - case *StatusUpdate_MesosHeartbeat: - s := proto.Size(x.MesosHeartbeat) - n += 1 // tag and wire - n += proto.SizeVarint(uint64(s)) - n += s - case nil: - default: - panic(fmt.Sprintf("proto: unexpected type %T in oneof", x)) +func (x *SubscribeRequest) GetId() string { + if x != nil { + return x.Id } - return n + return "" } -//////////////////////////////////////// +// ////////////////////////////////////// // Framework -//////////////////////////////////////// +// ////////////////////////////////////// type GetFrameworkInfoRequest struct { - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields } -func (m *GetFrameworkInfoRequest) Reset() { *m = GetFrameworkInfoRequest{} } -func (m *GetFrameworkInfoRequest) String() string { return proto.CompactTextString(m) } -func (*GetFrameworkInfoRequest) ProtoMessage() {} -func (*GetFrameworkInfoRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{4} -} -func (m *GetFrameworkInfoRequest) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *GetFrameworkInfoRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_GetFrameworkInfoRequest.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err - } - return b[:n], nil +func (x *GetFrameworkInfoRequest) Reset() { + *x = GetFrameworkInfoRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } } -func (m *GetFrameworkInfoRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_GetFrameworkInfoRequest.Merge(m, src) -} -func (m *GetFrameworkInfoRequest) XXX_Size() int { - return m.Size() -} -func (m *GetFrameworkInfoRequest) XXX_DiscardUnknown() { - xxx_messageInfo_GetFrameworkInfoRequest.DiscardUnknown(m) + +func (x *GetFrameworkInfoRequest) String() string { + return protoimpl.X.MessageStringOf(x) } -var xxx_messageInfo_GetFrameworkInfoRequest proto.InternalMessageInfo +func (*GetFrameworkInfoRequest) ProtoMessage() {} -type Version struct { - Major int32 `protobuf:"varint,1,opt,name=major,proto3" json:"major,omitempty"` - Minor int32 `protobuf:"varint,2,opt,name=minor,proto3" json:"minor,omitempty"` - Patch int32 `protobuf:"varint,3,opt,name=patch,proto3" json:"patch,omitempty"` - Build string `protobuf:"bytes,4,opt,name=build,proto3" json:"build,omitempty"` - ProductName string `protobuf:"bytes,5,opt,name=productName,proto3" json:"productName,omitempty"` - VersionStr string `protobuf:"bytes,6,opt,name=versionStr,proto3" json:"versionStr,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *Version) Reset() { *m = Version{} } -func (m *Version) String() string { return proto.CompactTextString(m) } -func (*Version) ProtoMessage() {} -func (*Version) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{5} -} -func (m *Version) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *Version) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_Version.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err +func (x *GetFrameworkInfoRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) } - return b[:n], nil + return ms } + return mi.MessageOf(x) +} + +// Deprecated: Use GetFrameworkInfoRequest.ProtoReflect.Descriptor instead. +func (*GetFrameworkInfoRequest) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{1} +} + +type Version struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Major int32 `protobuf:"varint,1,opt,name=major,proto3" json:"major,omitempty"` + Minor int32 `protobuf:"varint,2,opt,name=minor,proto3" json:"minor,omitempty"` + Patch int32 `protobuf:"varint,3,opt,name=patch,proto3" json:"patch,omitempty"` + Build string `protobuf:"bytes,4,opt,name=build,proto3" json:"build,omitempty"` + ProductName string `protobuf:"bytes,5,opt,name=productName,proto3" json:"productName,omitempty"` + VersionStr string `protobuf:"bytes,6,opt,name=versionStr,proto3" json:"versionStr,omitempty"` } -func (m *Version) XXX_Merge(src proto.Message) { - xxx_messageInfo_Version.Merge(m, src) + +func (x *Version) Reset() { + *x = Version{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *Version) XXX_Size() int { - return m.Size() + +func (x *Version) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *Version) XXX_DiscardUnknown() { - xxx_messageInfo_Version.DiscardUnknown(m) + +func (*Version) ProtoMessage() {} + +func (x *Version) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_Version proto.InternalMessageInfo +// Deprecated: Use Version.ProtoReflect.Descriptor instead. +func (*Version) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{2} +} -func (m *Version) GetMajor() int32 { - if m != nil { - return m.Major +func (x *Version) GetMajor() int32 { + if x != nil { + return x.Major } return 0 } -func (m *Version) GetMinor() int32 { - if m != nil { - return m.Minor +func (x *Version) GetMinor() int32 { + if x != nil { + return x.Minor } return 0 } -func (m *Version) GetPatch() int32 { - if m != nil { - return m.Patch +func (x *Version) GetPatch() int32 { + if x != nil { + return x.Patch } return 0 } -func (m *Version) GetBuild() string { - if m != nil { - return m.Build +func (x *Version) GetBuild() string { + if x != nil { + return x.Build } return "" } -func (m *Version) GetProductName() string { - if m != nil { - return m.ProductName +func (x *Version) GetProductName() string { + if x != nil { + return x.ProductName } return "" } -func (m *Version) GetVersionStr() string { - if m != nil { - return m.VersionStr +func (x *Version) GetVersionStr() string { + if x != nil { + return x.VersionStr } return "" } type GetFrameworkInfoReply struct { - FrameworkId string `protobuf:"bytes,1,opt,name=frameworkId,proto3" json:"frameworkId,omitempty"` - EnvironmentsCount int32 `protobuf:"varint,2,opt,name=environmentsCount,proto3" json:"environmentsCount,omitempty"` - TasksCount int32 `protobuf:"varint,3,opt,name=tasksCount,proto3" json:"tasksCount,omitempty"` - State string `protobuf:"bytes,4,opt,name=state,proto3" json:"state,omitempty"` - HostsCount int32 `protobuf:"varint,5,opt,name=hostsCount,proto3" json:"hostsCount,omitempty"` - InstanceName string `protobuf:"bytes,6,opt,name=instanceName,proto3" json:"instanceName,omitempty"` - Version *Version `protobuf:"bytes,7,opt,name=version,proto3" json:"version,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *GetFrameworkInfoReply) Reset() { *m = GetFrameworkInfoReply{} } -func (m *GetFrameworkInfoReply) String() string { return proto.CompactTextString(m) } -func (*GetFrameworkInfoReply) ProtoMessage() {} -func (*GetFrameworkInfoReply) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{6} -} -func (m *GetFrameworkInfoReply) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *GetFrameworkInfoReply) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_GetFrameworkInfoReply.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err - } - return b[:n], nil - } + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + FrameworkId string `protobuf:"bytes,1,opt,name=frameworkId,proto3" json:"frameworkId,omitempty"` + EnvironmentsCount int32 `protobuf:"varint,2,opt,name=environmentsCount,proto3" json:"environmentsCount,omitempty"` + TasksCount int32 `protobuf:"varint,3,opt,name=tasksCount,proto3" json:"tasksCount,omitempty"` + State string `protobuf:"bytes,4,opt,name=state,proto3" json:"state,omitempty"` + HostsCount int32 `protobuf:"varint,5,opt,name=hostsCount,proto3" json:"hostsCount,omitempty"` + InstanceName string `protobuf:"bytes,6,opt,name=instanceName,proto3" json:"instanceName,omitempty"` + Version *Version `protobuf:"bytes,7,opt,name=version,proto3" json:"version,omitempty"` + ConfigurationEndpoint string `protobuf:"bytes,8,opt,name=configurationEndpoint,proto3" json:"configurationEndpoint,omitempty"` + DetectorsInInstance []string `protobuf:"bytes,9,rep,name=detectorsInInstance,proto3" json:"detectorsInInstance,omitempty"` + ActiveDetectors []string `protobuf:"bytes,10,rep,name=activeDetectors,proto3" json:"activeDetectors,omitempty"` + AvailableDetectors []string `protobuf:"bytes,11,rep,name=availableDetectors,proto3" json:"availableDetectors,omitempty"` + Timestamp int64 `protobuf:"varint,12,opt,name=timestamp,proto3" json:"timestamp,omitempty"` // timestamp of when this object was sent in unix milliseconds } -func (m *GetFrameworkInfoReply) XXX_Merge(src proto.Message) { - xxx_messageInfo_GetFrameworkInfoReply.Merge(m, src) + +func (x *GetFrameworkInfoReply) Reset() { + *x = GetFrameworkInfoReply{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *GetFrameworkInfoReply) XXX_Size() int { - return m.Size() + +func (x *GetFrameworkInfoReply) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *GetFrameworkInfoReply) XXX_DiscardUnknown() { - xxx_messageInfo_GetFrameworkInfoReply.DiscardUnknown(m) + +func (*GetFrameworkInfoReply) ProtoMessage() {} + +func (x *GetFrameworkInfoReply) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_GetFrameworkInfoReply proto.InternalMessageInfo +// Deprecated: Use GetFrameworkInfoReply.ProtoReflect.Descriptor instead. +func (*GetFrameworkInfoReply) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{3} +} -func (m *GetFrameworkInfoReply) GetFrameworkId() string { - if m != nil { - return m.FrameworkId +func (x *GetFrameworkInfoReply) GetFrameworkId() string { + if x != nil { + return x.FrameworkId } return "" } -func (m *GetFrameworkInfoReply) GetEnvironmentsCount() int32 { - if m != nil { - return m.EnvironmentsCount +func (x *GetFrameworkInfoReply) GetEnvironmentsCount() int32 { + if x != nil { + return x.EnvironmentsCount } return 0 } -func (m *GetFrameworkInfoReply) GetTasksCount() int32 { - if m != nil { - return m.TasksCount +func (x *GetFrameworkInfoReply) GetTasksCount() int32 { + if x != nil { + return x.TasksCount } return 0 } -func (m *GetFrameworkInfoReply) GetState() string { - if m != nil { - return m.State +func (x *GetFrameworkInfoReply) GetState() string { + if x != nil { + return x.State } return "" } -func (m *GetFrameworkInfoReply) GetHostsCount() int32 { - if m != nil { - return m.HostsCount +func (x *GetFrameworkInfoReply) GetHostsCount() int32 { + if x != nil { + return x.HostsCount } return 0 } -func (m *GetFrameworkInfoReply) GetInstanceName() string { - if m != nil { - return m.InstanceName +func (x *GetFrameworkInfoReply) GetInstanceName() string { + if x != nil { + return x.InstanceName } return "" } -func (m *GetFrameworkInfoReply) GetVersion() *Version { - if m != nil { - return m.Version +func (x *GetFrameworkInfoReply) GetVersion() *Version { + if x != nil { + return x.Version } return nil } -// Not implemented yet -type TeardownRequest struct { - Reason string `protobuf:"bytes,1,opt,name=reason,proto3" json:"reason,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` +func (x *GetFrameworkInfoReply) GetConfigurationEndpoint() string { + if x != nil { + return x.ConfigurationEndpoint + } + return "" } -func (m *TeardownRequest) Reset() { *m = TeardownRequest{} } -func (m *TeardownRequest) String() string { return proto.CompactTextString(m) } -func (*TeardownRequest) ProtoMessage() {} -func (*TeardownRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{7} -} -func (m *TeardownRequest) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *TeardownRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_TeardownRequest.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err - } - return b[:n], nil +func (x *GetFrameworkInfoReply) GetDetectorsInInstance() []string { + if x != nil { + return x.DetectorsInInstance } + return nil } -func (m *TeardownRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_TeardownRequest.Merge(m, src) + +func (x *GetFrameworkInfoReply) GetActiveDetectors() []string { + if x != nil { + return x.ActiveDetectors + } + return nil } -func (m *TeardownRequest) XXX_Size() int { - return m.Size() + +func (x *GetFrameworkInfoReply) GetAvailableDetectors() []string { + if x != nil { + return x.AvailableDetectors + } + return nil } -func (m *TeardownRequest) XXX_DiscardUnknown() { - xxx_messageInfo_TeardownRequest.DiscardUnknown(m) + +func (x *GetFrameworkInfoReply) GetTimestamp() int64 { + if x != nil { + return x.Timestamp + } + return 0 } -var xxx_messageInfo_TeardownRequest proto.InternalMessageInfo +// Not implemented yet +type TeardownRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Reason string `protobuf:"bytes,1,opt,name=reason,proto3" json:"reason,omitempty"` +} -func (m *TeardownRequest) GetReason() string { - if m != nil { - return m.Reason +func (x *TeardownRequest) Reset() { + *x = TeardownRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return "" } -type TeardownReply struct { - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` +func (x *TeardownRequest) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *TeardownReply) Reset() { *m = TeardownReply{} } -func (m *TeardownReply) String() string { return proto.CompactTextString(m) } -func (*TeardownReply) ProtoMessage() {} -func (*TeardownReply) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{8} -} -func (m *TeardownReply) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *TeardownReply) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_TeardownReply.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err +func (*TeardownRequest) ProtoMessage() {} + +func (x *TeardownRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) } - return b[:n], nil + return ms } + return mi.MessageOf(x) } -func (m *TeardownReply) XXX_Merge(src proto.Message) { - xxx_messageInfo_TeardownReply.Merge(m, src) + +// Deprecated: Use TeardownRequest.ProtoReflect.Descriptor instead. +func (*TeardownRequest) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{4} } -func (m *TeardownReply) XXX_Size() int { - return m.Size() + +func (x *TeardownRequest) GetReason() string { + if x != nil { + return x.Reason + } + return "" } -func (m *TeardownReply) XXX_DiscardUnknown() { - xxx_messageInfo_TeardownReply.DiscardUnknown(m) + +type TeardownReply struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields } -var xxx_messageInfo_TeardownReply proto.InternalMessageInfo +func (x *TeardownReply) Reset() { + *x = TeardownReply{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} -//////////////////////////////////////// -// Environment -//////////////////////////////////////// -type GetEnvironmentsRequest struct { - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` +func (x *TeardownReply) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *GetEnvironmentsRequest) Reset() { *m = GetEnvironmentsRequest{} } -func (m *GetEnvironmentsRequest) String() string { return proto.CompactTextString(m) } -func (*GetEnvironmentsRequest) ProtoMessage() {} -func (*GetEnvironmentsRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{9} -} -func (m *GetEnvironmentsRequest) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *GetEnvironmentsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_GetEnvironmentsRequest.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err +func (*TeardownReply) ProtoMessage() {} + +func (x *TeardownReply) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) } - return b[:n], nil + return ms } + return mi.MessageOf(x) } -func (m *GetEnvironmentsRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_GetEnvironmentsRequest.Merge(m, src) -} -func (m *GetEnvironmentsRequest) XXX_Size() int { - return m.Size() -} -func (m *GetEnvironmentsRequest) XXX_DiscardUnknown() { - xxx_messageInfo_GetEnvironmentsRequest.DiscardUnknown(m) + +// Deprecated: Use TeardownReply.ProtoReflect.Descriptor instead. +func (*TeardownReply) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{5} } -var xxx_messageInfo_GetEnvironmentsRequest proto.InternalMessageInfo +// ////////////////////////////////////// +// Environment +// ////////////////////////////////////// +type GetEnvironmentsRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields -type GetEnvironmentsReply struct { - FrameworkId string `protobuf:"bytes,1,opt,name=frameworkId,proto3" json:"frameworkId,omitempty"` - Environments []*EnvironmentInfo `protobuf:"bytes,2,rep,name=environments,proto3" json:"environments,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + ShowAll bool `protobuf:"varint,1,opt,name=showAll,proto3" json:"showAll,omitempty"` + ShowTaskInfos bool `protobuf:"varint,2,opt,name=showTaskInfos,proto3" json:"showTaskInfos,omitempty"` + ShowDetailedIntegratedServices bool `protobuf:"varint,3,opt,name=showDetailedIntegratedServices,proto3" json:"showDetailedIntegratedServices,omitempty"` // integratedServices are returned everytime, setting this flag gives detailed report } -func (m *GetEnvironmentsReply) Reset() { *m = GetEnvironmentsReply{} } -func (m *GetEnvironmentsReply) String() string { return proto.CompactTextString(m) } -func (*GetEnvironmentsReply) ProtoMessage() {} -func (*GetEnvironmentsReply) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{10} -} -func (m *GetEnvironmentsReply) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *GetEnvironmentsReply) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_GetEnvironmentsReply.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err - } - return b[:n], nil +func (x *GetEnvironmentsRequest) Reset() { + *x = GetEnvironmentsRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } } -func (m *GetEnvironmentsReply) XXX_Merge(src proto.Message) { - xxx_messageInfo_GetEnvironmentsReply.Merge(m, src) -} -func (m *GetEnvironmentsReply) XXX_Size() int { - return m.Size() + +func (x *GetEnvironmentsRequest) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *GetEnvironmentsReply) XXX_DiscardUnknown() { - xxx_messageInfo_GetEnvironmentsReply.DiscardUnknown(m) + +func (*GetEnvironmentsRequest) ProtoMessage() {} + +func (x *GetEnvironmentsRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_GetEnvironmentsReply proto.InternalMessageInfo +// Deprecated: Use GetEnvironmentsRequest.ProtoReflect.Descriptor instead. +func (*GetEnvironmentsRequest) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{6} +} -func (m *GetEnvironmentsReply) GetFrameworkId() string { - if m != nil { - return m.FrameworkId +func (x *GetEnvironmentsRequest) GetShowAll() bool { + if x != nil { + return x.ShowAll } - return "" + return false } -func (m *GetEnvironmentsReply) GetEnvironments() []*EnvironmentInfo { - if m != nil { - return m.Environments +func (x *GetEnvironmentsRequest) GetShowTaskInfos() bool { + if x != nil { + return x.ShowTaskInfos } - return nil + return false } -type EnvironmentInfo struct { - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - CreatedWhen string `protobuf:"bytes,2,opt,name=createdWhen,proto3" json:"createdWhen,omitempty"` - State string `protobuf:"bytes,3,opt,name=state,proto3" json:"state,omitempty"` - Tasks []*ShortTaskInfo `protobuf:"bytes,4,rep,name=tasks,proto3" json:"tasks,omitempty"` - RootRole string `protobuf:"bytes,5,opt,name=rootRole,proto3" json:"rootRole,omitempty"` - CurrentRunNumber uint32 `protobuf:"varint,6,opt,name=currentRunNumber,proto3" json:"currentRunNumber,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *EnvironmentInfo) Reset() { *m = EnvironmentInfo{} } -func (m *EnvironmentInfo) String() string { return proto.CompactTextString(m) } -func (*EnvironmentInfo) ProtoMessage() {} -func (*EnvironmentInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{11} -} -func (m *EnvironmentInfo) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *EnvironmentInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_EnvironmentInfo.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err - } - return b[:n], nil +func (x *GetEnvironmentsRequest) GetShowDetailedIntegratedServices() bool { + if x != nil { + return x.ShowDetailedIntegratedServices } + return false } -func (m *EnvironmentInfo) XXX_Merge(src proto.Message) { - xxx_messageInfo_EnvironmentInfo.Merge(m, src) + +type GetEnvironmentsReply struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + FrameworkId string `protobuf:"bytes,1,opt,name=frameworkId,proto3" json:"frameworkId,omitempty"` + Environments []*EnvironmentInfo `protobuf:"bytes,2,rep,name=environments,proto3" json:"environments,omitempty"` + Timestamp int64 `protobuf:"varint,3,opt,name=timestamp,proto3" json:"timestamp,omitempty"` // timestamp of when this object was sent in unix milliseconds } -func (m *EnvironmentInfo) XXX_Size() int { - return m.Size() + +func (x *GetEnvironmentsReply) Reset() { + *x = GetEnvironmentsReply{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *EnvironmentInfo) XXX_DiscardUnknown() { - xxx_messageInfo_EnvironmentInfo.DiscardUnknown(m) + +func (x *GetEnvironmentsReply) String() string { + return protoimpl.X.MessageStringOf(x) } -var xxx_messageInfo_EnvironmentInfo proto.InternalMessageInfo +func (*GetEnvironmentsReply) ProtoMessage() {} -func (m *EnvironmentInfo) GetId() string { - if m != nil { - return m.Id +func (x *GetEnvironmentsReply) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms } - return "" + return mi.MessageOf(x) } -func (m *EnvironmentInfo) GetCreatedWhen() string { - if m != nil { - return m.CreatedWhen - } - return "" +// Deprecated: Use GetEnvironmentsReply.ProtoReflect.Descriptor instead. +func (*GetEnvironmentsReply) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{7} } -func (m *EnvironmentInfo) GetState() string { - if m != nil { - return m.State +func (x *GetEnvironmentsReply) GetFrameworkId() string { + if x != nil { + return x.FrameworkId } return "" } -func (m *EnvironmentInfo) GetTasks() []*ShortTaskInfo { - if m != nil { - return m.Tasks +func (x *GetEnvironmentsReply) GetEnvironments() []*EnvironmentInfo { + if x != nil { + return x.Environments } return nil } -func (m *EnvironmentInfo) GetRootRole() string { - if m != nil { - return m.RootRole - } - return "" -} - -func (m *EnvironmentInfo) GetCurrentRunNumber() uint32 { - if m != nil { - return m.CurrentRunNumber +func (x *GetEnvironmentsReply) GetTimestamp() int64 { + if x != nil { + return x.Timestamp } return 0 } -type NewEnvironmentRequest struct { - WorkflowTemplate string `protobuf:"bytes,1,opt,name=workflowTemplate,proto3" json:"workflowTemplate,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` +type EnvironmentInfo struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + CreatedWhen int64 `protobuf:"varint,2,opt,name=createdWhen,proto3" json:"createdWhen,omitempty"` // msec + State string `protobuf:"bytes,3,opt,name=state,proto3" json:"state,omitempty"` + Tasks []*ShortTaskInfo `protobuf:"bytes,4,rep,name=tasks,proto3" json:"tasks,omitempty"` + RootRole string `protobuf:"bytes,5,opt,name=rootRole,proto3" json:"rootRole,omitempty"` + CurrentRunNumber uint32 `protobuf:"varint,6,opt,name=currentRunNumber,proto3" json:"currentRunNumber,omitempty"` + Defaults map[string]string `protobuf:"bytes,7,rep,name=defaults,proto3" json:"defaults,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Vars map[string]string `protobuf:"bytes,8,rep,name=vars,proto3" json:"vars,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + UserVars map[string]string `protobuf:"bytes,9,rep,name=userVars,proto3" json:"userVars,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + NumberOfFlps int32 `protobuf:"varint,10,opt,name=numberOfFlps,proto3" json:"numberOfFlps,omitempty"` + IncludedDetectors []string `protobuf:"bytes,11,rep,name=includedDetectors,proto3" json:"includedDetectors,omitempty"` + Description string `protobuf:"bytes,12,opt,name=description,proto3" json:"description,omitempty"` + NumberOfHosts int32 `protobuf:"varint,13,opt,name=numberOfHosts,proto3" json:"numberOfHosts,omitempty"` + IntegratedServicesData map[string]string `protobuf:"bytes,14,rep,name=integratedServicesData,proto3" json:"integratedServicesData,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + NumberOfTasks int32 `protobuf:"varint,15,opt,name=numberOfTasks,proto3" json:"numberOfTasks,omitempty"` + CurrentTransition string `protobuf:"bytes,16,opt,name=currentTransition,proto3" json:"currentTransition,omitempty"` + NumberOfActiveTasks int32 `protobuf:"varint,17,opt,name=numberOfActiveTasks,proto3" json:"numberOfActiveTasks,omitempty"` + NumberOfInactiveTasks int32 `protobuf:"varint,18,opt,name=numberOfInactiveTasks,proto3" json:"numberOfInactiveTasks,omitempty"` } -func (m *NewEnvironmentRequest) Reset() { *m = NewEnvironmentRequest{} } -func (m *NewEnvironmentRequest) String() string { return proto.CompactTextString(m) } -func (*NewEnvironmentRequest) ProtoMessage() {} -func (*NewEnvironmentRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{12} -} -func (m *NewEnvironmentRequest) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *NewEnvironmentRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_NewEnvironmentRequest.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err - } - return b[:n], nil +func (x *EnvironmentInfo) Reset() { + *x = EnvironmentInfo{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } } -func (m *NewEnvironmentRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_NewEnvironmentRequest.Merge(m, src) -} -func (m *NewEnvironmentRequest) XXX_Size() int { - return m.Size() -} -func (m *NewEnvironmentRequest) XXX_DiscardUnknown() { - xxx_messageInfo_NewEnvironmentRequest.DiscardUnknown(m) + +func (x *EnvironmentInfo) String() string { + return protoimpl.X.MessageStringOf(x) } -var xxx_messageInfo_NewEnvironmentRequest proto.InternalMessageInfo +func (*EnvironmentInfo) ProtoMessage() {} -func (m *NewEnvironmentRequest) GetWorkflowTemplate() string { - if m != nil { - return m.WorkflowTemplate +func (x *EnvironmentInfo) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[8] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms } - return "" + return mi.MessageOf(x) } -type NewEnvironmentReply struct { - Environment *EnvironmentInfo `protobuf:"bytes,1,opt,name=environment,proto3" json:"environment,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` +// Deprecated: Use EnvironmentInfo.ProtoReflect.Descriptor instead. +func (*EnvironmentInfo) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{8} } -func (m *NewEnvironmentReply) Reset() { *m = NewEnvironmentReply{} } -func (m *NewEnvironmentReply) String() string { return proto.CompactTextString(m) } -func (*NewEnvironmentReply) ProtoMessage() {} -func (*NewEnvironmentReply) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{13} -} -func (m *NewEnvironmentReply) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *NewEnvironmentReply) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_NewEnvironmentReply.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err - } - return b[:n], nil +func (x *EnvironmentInfo) GetId() string { + if x != nil { + return x.Id } -} -func (m *NewEnvironmentReply) XXX_Merge(src proto.Message) { - xxx_messageInfo_NewEnvironmentReply.Merge(m, src) -} -func (m *NewEnvironmentReply) XXX_Size() int { - return m.Size() -} -func (m *NewEnvironmentReply) XXX_DiscardUnknown() { - xxx_messageInfo_NewEnvironmentReply.DiscardUnknown(m) + return "" } -var xxx_messageInfo_NewEnvironmentReply proto.InternalMessageInfo - -func (m *NewEnvironmentReply) GetEnvironment() *EnvironmentInfo { - if m != nil { - return m.Environment +func (x *EnvironmentInfo) GetCreatedWhen() int64 { + if x != nil { + return x.CreatedWhen } - return nil + return 0 } -type GetEnvironmentRequest struct { - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` +func (x *EnvironmentInfo) GetState() string { + if x != nil { + return x.State + } + return "" } -func (m *GetEnvironmentRequest) Reset() { *m = GetEnvironmentRequest{} } -func (m *GetEnvironmentRequest) String() string { return proto.CompactTextString(m) } -func (*GetEnvironmentRequest) ProtoMessage() {} -func (*GetEnvironmentRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{14} -} -func (m *GetEnvironmentRequest) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *GetEnvironmentRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_GetEnvironmentRequest.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err - } - return b[:n], nil +func (x *EnvironmentInfo) GetTasks() []*ShortTaskInfo { + if x != nil { + return x.Tasks } -} -func (m *GetEnvironmentRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_GetEnvironmentRequest.Merge(m, src) -} -func (m *GetEnvironmentRequest) XXX_Size() int { - return m.Size() -} -func (m *GetEnvironmentRequest) XXX_DiscardUnknown() { - xxx_messageInfo_GetEnvironmentRequest.DiscardUnknown(m) + return nil } -var xxx_messageInfo_GetEnvironmentRequest proto.InternalMessageInfo - -func (m *GetEnvironmentRequest) GetId() string { - if m != nil { - return m.Id +func (x *EnvironmentInfo) GetRootRole() string { + if x != nil { + return x.RootRole } return "" } -type GetEnvironmentReply struct { - Environment *EnvironmentInfo `protobuf:"bytes,1,opt,name=environment,proto3" json:"environment,omitempty"` - Workflow *RoleInfo `protobuf:"bytes,2,opt,name=workflow,proto3" json:"workflow,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` +func (x *EnvironmentInfo) GetCurrentRunNumber() uint32 { + if x != nil { + return x.CurrentRunNumber + } + return 0 } -func (m *GetEnvironmentReply) Reset() { *m = GetEnvironmentReply{} } -func (m *GetEnvironmentReply) String() string { return proto.CompactTextString(m) } -func (*GetEnvironmentReply) ProtoMessage() {} -func (*GetEnvironmentReply) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{15} -} -func (m *GetEnvironmentReply) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *GetEnvironmentReply) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_GetEnvironmentReply.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err - } - return b[:n], nil +func (x *EnvironmentInfo) GetDefaults() map[string]string { + if x != nil { + return x.Defaults } -} -func (m *GetEnvironmentReply) XXX_Merge(src proto.Message) { - xxx_messageInfo_GetEnvironmentReply.Merge(m, src) -} -func (m *GetEnvironmentReply) XXX_Size() int { - return m.Size() -} -func (m *GetEnvironmentReply) XXX_DiscardUnknown() { - xxx_messageInfo_GetEnvironmentReply.DiscardUnknown(m) + return nil } -var xxx_messageInfo_GetEnvironmentReply proto.InternalMessageInfo - -func (m *GetEnvironmentReply) GetEnvironment() *EnvironmentInfo { - if m != nil { - return m.Environment +func (x *EnvironmentInfo) GetVars() map[string]string { + if x != nil { + return x.Vars } return nil } -func (m *GetEnvironmentReply) GetWorkflow() *RoleInfo { - if m != nil { - return m.Workflow +func (x *EnvironmentInfo) GetUserVars() map[string]string { + if x != nil { + return x.UserVars } return nil } -type ControlEnvironmentRequest struct { - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - Type ControlEnvironmentRequest_Optype `protobuf:"varint,2,opt,name=type,proto3,enum=o2control.ControlEnvironmentRequest_Optype" json:"type,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` +func (x *EnvironmentInfo) GetNumberOfFlps() int32 { + if x != nil { + return x.NumberOfFlps + } + return 0 } -func (m *ControlEnvironmentRequest) Reset() { *m = ControlEnvironmentRequest{} } -func (m *ControlEnvironmentRequest) String() string { return proto.CompactTextString(m) } -func (*ControlEnvironmentRequest) ProtoMessage() {} -func (*ControlEnvironmentRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{16} -} -func (m *ControlEnvironmentRequest) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *ControlEnvironmentRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_ControlEnvironmentRequest.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err - } - return b[:n], nil +func (x *EnvironmentInfo) GetIncludedDetectors() []string { + if x != nil { + return x.IncludedDetectors } -} -func (m *ControlEnvironmentRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_ControlEnvironmentRequest.Merge(m, src) -} -func (m *ControlEnvironmentRequest) XXX_Size() int { - return m.Size() -} -func (m *ControlEnvironmentRequest) XXX_DiscardUnknown() { - xxx_messageInfo_ControlEnvironmentRequest.DiscardUnknown(m) + return nil } -var xxx_messageInfo_ControlEnvironmentRequest proto.InternalMessageInfo - -func (m *ControlEnvironmentRequest) GetId() string { - if m != nil { - return m.Id +func (x *EnvironmentInfo) GetDescription() string { + if x != nil { + return x.Description } return "" } -func (m *ControlEnvironmentRequest) GetType() ControlEnvironmentRequest_Optype { - if m != nil { - return m.Type +func (x *EnvironmentInfo) GetNumberOfHosts() int32 { + if x != nil { + return x.NumberOfHosts } - return ControlEnvironmentRequest_NOOP + return 0 } -type ControlEnvironmentReply struct { - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - State string `protobuf:"bytes,2,opt,name=state,proto3" json:"state,omitempty"` - CurrentRunNumber uint32 `protobuf:"varint,3,opt,name=currentRunNumber,proto3" json:"currentRunNumber,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` +func (x *EnvironmentInfo) GetIntegratedServicesData() map[string]string { + if x != nil { + return x.IntegratedServicesData + } + return nil } -func (m *ControlEnvironmentReply) Reset() { *m = ControlEnvironmentReply{} } -func (m *ControlEnvironmentReply) String() string { return proto.CompactTextString(m) } -func (*ControlEnvironmentReply) ProtoMessage() {} -func (*ControlEnvironmentReply) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{17} -} -func (m *ControlEnvironmentReply) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *ControlEnvironmentReply) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_ControlEnvironmentReply.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err - } - return b[:n], nil +func (x *EnvironmentInfo) GetNumberOfTasks() int32 { + if x != nil { + return x.NumberOfTasks } -} -func (m *ControlEnvironmentReply) XXX_Merge(src proto.Message) { - xxx_messageInfo_ControlEnvironmentReply.Merge(m, src) -} -func (m *ControlEnvironmentReply) XXX_Size() int { - return m.Size() -} -func (m *ControlEnvironmentReply) XXX_DiscardUnknown() { - xxx_messageInfo_ControlEnvironmentReply.DiscardUnknown(m) + return 0 } -var xxx_messageInfo_ControlEnvironmentReply proto.InternalMessageInfo - -func (m *ControlEnvironmentReply) GetId() string { - if m != nil { - return m.Id +func (x *EnvironmentInfo) GetCurrentTransition() string { + if x != nil { + return x.CurrentTransition } return "" } -func (m *ControlEnvironmentReply) GetState() string { - if m != nil { - return m.State +func (x *EnvironmentInfo) GetNumberOfActiveTasks() int32 { + if x != nil { + return x.NumberOfActiveTasks } - return "" + return 0 } -func (m *ControlEnvironmentReply) GetCurrentRunNumber() uint32 { - if m != nil { - return m.CurrentRunNumber +func (x *EnvironmentInfo) GetNumberOfInactiveTasks() int32 { + if x != nil { + return x.NumberOfInactiveTasks } return 0 } -type ModifyEnvironmentRequest struct { - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - Operations []*EnvironmentOperation `protobuf:"bytes,2,rep,name=operations,proto3" json:"operations,omitempty"` - ReconfigureAll bool `protobuf:"varint,3,opt,name=reconfigureAll,proto3" json:"reconfigureAll,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` +type NewEnvironmentRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + WorkflowTemplate string `protobuf:"bytes,1,opt,name=workflowTemplate,proto3" json:"workflowTemplate,omitempty"` + Vars map[string]string `protobuf:"bytes,2,rep,name=vars,proto3" json:"vars,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Public bool `protobuf:"varint,3,opt,name=public,proto3" json:"public,omitempty"` + AutoTransition bool `protobuf:"varint,4,opt,name=autoTransition,proto3" json:"autoTransition,omitempty"` + RequestUser *protos.User `protobuf:"bytes,5,opt,name=requestUser,proto3" json:"requestUser,omitempty"` } -func (m *ModifyEnvironmentRequest) Reset() { *m = ModifyEnvironmentRequest{} } -func (m *ModifyEnvironmentRequest) String() string { return proto.CompactTextString(m) } -func (*ModifyEnvironmentRequest) ProtoMessage() {} -func (*ModifyEnvironmentRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{18} -} -func (m *ModifyEnvironmentRequest) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *ModifyEnvironmentRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_ModifyEnvironmentRequest.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err - } - return b[:n], nil +func (x *NewEnvironmentRequest) Reset() { + *x = NewEnvironmentRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } } -func (m *ModifyEnvironmentRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_ModifyEnvironmentRequest.Merge(m, src) -} -func (m *ModifyEnvironmentRequest) XXX_Size() int { - return m.Size() + +func (x *NewEnvironmentRequest) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *ModifyEnvironmentRequest) XXX_DiscardUnknown() { - xxx_messageInfo_ModifyEnvironmentRequest.DiscardUnknown(m) + +func (*NewEnvironmentRequest) ProtoMessage() {} + +func (x *NewEnvironmentRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[9] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_ModifyEnvironmentRequest proto.InternalMessageInfo +// Deprecated: Use NewEnvironmentRequest.ProtoReflect.Descriptor instead. +func (*NewEnvironmentRequest) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{9} +} -func (m *ModifyEnvironmentRequest) GetId() string { - if m != nil { - return m.Id +func (x *NewEnvironmentRequest) GetWorkflowTemplate() string { + if x != nil { + return x.WorkflowTemplate } return "" } -func (m *ModifyEnvironmentRequest) GetOperations() []*EnvironmentOperation { - if m != nil { - return m.Operations +func (x *NewEnvironmentRequest) GetVars() map[string]string { + if x != nil { + return x.Vars } return nil } -func (m *ModifyEnvironmentRequest) GetReconfigureAll() bool { - if m != nil { - return m.ReconfigureAll +func (x *NewEnvironmentRequest) GetPublic() bool { + if x != nil { + return x.Public } return false } -type EnvironmentOperation struct { - Type EnvironmentOperation_Optype `protobuf:"varint,1,opt,name=type,proto3,enum=o2control.EnvironmentOperation_Optype" json:"type,omitempty"` - RoleName string `protobuf:"bytes,2,opt,name=roleName,proto3" json:"roleName,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` +func (x *NewEnvironmentRequest) GetAutoTransition() bool { + if x != nil { + return x.AutoTransition + } + return false } -func (m *EnvironmentOperation) Reset() { *m = EnvironmentOperation{} } -func (m *EnvironmentOperation) String() string { return proto.CompactTextString(m) } -func (*EnvironmentOperation) ProtoMessage() {} -func (*EnvironmentOperation) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{19} -} -func (m *EnvironmentOperation) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *EnvironmentOperation) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_EnvironmentOperation.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err - } - return b[:n], nil +func (x *NewEnvironmentRequest) GetRequestUser() *protos.User { + if x != nil { + return x.RequestUser } -} -func (m *EnvironmentOperation) XXX_Merge(src proto.Message) { - xxx_messageInfo_EnvironmentOperation.Merge(m, src) -} -func (m *EnvironmentOperation) XXX_Size() int { - return m.Size() -} -func (m *EnvironmentOperation) XXX_DiscardUnknown() { - xxx_messageInfo_EnvironmentOperation.DiscardUnknown(m) + return nil } -var xxx_messageInfo_EnvironmentOperation proto.InternalMessageInfo +type NewEnvironmentReply struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields -func (m *EnvironmentOperation) GetType() EnvironmentOperation_Optype { - if m != nil { - return m.Type - } - return EnvironmentOperation_NOOP + Environment *EnvironmentInfo `protobuf:"bytes,1,opt,name=environment,proto3" json:"environment,omitempty"` + Public bool `protobuf:"varint,2,opt,name=public,proto3" json:"public,omitempty"` + Timestamp int64 `protobuf:"varint,3,opt,name=timestamp,proto3" json:"timestamp,omitempty"` // timestamp of when this object was sent in unix milliseconds } -func (m *EnvironmentOperation) GetRoleName() string { - if m != nil { - return m.RoleName +func (x *NewEnvironmentReply) Reset() { + *x = NewEnvironmentReply{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return "" } -type ModifyEnvironmentReply struct { - FailedOperations []*EnvironmentOperation `protobuf:"bytes,1,rep,name=failedOperations,proto3" json:"failedOperations,omitempty"` - Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"` - State string `protobuf:"bytes,3,opt,name=state,proto3" json:"state,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` +func (x *NewEnvironmentReply) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *ModifyEnvironmentReply) Reset() { *m = ModifyEnvironmentReply{} } -func (m *ModifyEnvironmentReply) String() string { return proto.CompactTextString(m) } -func (*ModifyEnvironmentReply) ProtoMessage() {} -func (*ModifyEnvironmentReply) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{20} -} -func (m *ModifyEnvironmentReply) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *ModifyEnvironmentReply) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_ModifyEnvironmentReply.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err +func (*NewEnvironmentReply) ProtoMessage() {} + +func (x *NewEnvironmentReply) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[10] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) } - return b[:n], nil + return ms } -} -func (m *ModifyEnvironmentReply) XXX_Merge(src proto.Message) { - xxx_messageInfo_ModifyEnvironmentReply.Merge(m, src) -} -func (m *ModifyEnvironmentReply) XXX_Size() int { - return m.Size() -} -func (m *ModifyEnvironmentReply) XXX_DiscardUnknown() { - xxx_messageInfo_ModifyEnvironmentReply.DiscardUnknown(m) + return mi.MessageOf(x) } -var xxx_messageInfo_ModifyEnvironmentReply proto.InternalMessageInfo +// Deprecated: Use NewEnvironmentReply.ProtoReflect.Descriptor instead. +func (*NewEnvironmentReply) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{10} +} -func (m *ModifyEnvironmentReply) GetFailedOperations() []*EnvironmentOperation { - if m != nil { - return m.FailedOperations +func (x *NewEnvironmentReply) GetEnvironment() *EnvironmentInfo { + if x != nil { + return x.Environment } return nil } -func (m *ModifyEnvironmentReply) GetId() string { - if m != nil { - return m.Id +func (x *NewEnvironmentReply) GetPublic() bool { + if x != nil { + return x.Public } - return "" + return false } -func (m *ModifyEnvironmentReply) GetState() string { - if m != nil { - return m.State +func (x *NewEnvironmentReply) GetTimestamp() int64 { + if x != nil { + return x.Timestamp } - return "" + return 0 } -type DestroyEnvironmentRequest struct { - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - KeepTasks bool `protobuf:"varint,2,opt,name=keepTasks,proto3" json:"keepTasks,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` +type NewAutoEnvironmentRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + WorkflowTemplate string `protobuf:"bytes,1,opt,name=workflowTemplate,proto3" json:"workflowTemplate,omitempty"` + Vars map[string]string `protobuf:"bytes,2,rep,name=vars,proto3" json:"vars,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Id string `protobuf:"bytes,3,opt,name=id,proto3" json:"id,omitempty"` + RequestUser *protos.User `protobuf:"bytes,4,opt,name=requestUser,proto3" json:"requestUser,omitempty"` } -func (m *DestroyEnvironmentRequest) Reset() { *m = DestroyEnvironmentRequest{} } -func (m *DestroyEnvironmentRequest) String() string { return proto.CompactTextString(m) } -func (*DestroyEnvironmentRequest) ProtoMessage() {} -func (*DestroyEnvironmentRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{21} -} -func (m *DestroyEnvironmentRequest) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *DestroyEnvironmentRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_DestroyEnvironmentRequest.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err - } - return b[:n], nil +func (x *NewAutoEnvironmentRequest) Reset() { + *x = NewAutoEnvironmentRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } } -func (m *DestroyEnvironmentRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_DestroyEnvironmentRequest.Merge(m, src) -} -func (m *DestroyEnvironmentRequest) XXX_Size() int { - return m.Size() + +func (x *NewAutoEnvironmentRequest) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *DestroyEnvironmentRequest) XXX_DiscardUnknown() { - xxx_messageInfo_DestroyEnvironmentRequest.DiscardUnknown(m) + +func (*NewAutoEnvironmentRequest) ProtoMessage() {} + +func (x *NewAutoEnvironmentRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[11] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_DestroyEnvironmentRequest proto.InternalMessageInfo +// Deprecated: Use NewAutoEnvironmentRequest.ProtoReflect.Descriptor instead. +func (*NewAutoEnvironmentRequest) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{11} +} -func (m *DestroyEnvironmentRequest) GetId() string { - if m != nil { - return m.Id +func (x *NewAutoEnvironmentRequest) GetWorkflowTemplate() string { + if x != nil { + return x.WorkflowTemplate } return "" } -func (m *DestroyEnvironmentRequest) GetKeepTasks() bool { - if m != nil { - return m.KeepTasks +func (x *NewAutoEnvironmentRequest) GetVars() map[string]string { + if x != nil { + return x.Vars } - return false + return nil } -type DestroyEnvironmentReply struct { - CleanupTasksReply *CleanupTasksReply `protobuf:"bytes,1,opt,name=cleanupTasksReply,proto3" json:"cleanupTasksReply,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` +func (x *NewAutoEnvironmentRequest) GetId() string { + if x != nil { + return x.Id + } + return "" } -func (m *DestroyEnvironmentReply) Reset() { *m = DestroyEnvironmentReply{} } -func (m *DestroyEnvironmentReply) String() string { return proto.CompactTextString(m) } -func (*DestroyEnvironmentReply) ProtoMessage() {} -func (*DestroyEnvironmentReply) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{22} -} -func (m *DestroyEnvironmentReply) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *DestroyEnvironmentReply) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_DestroyEnvironmentReply.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err - } - return b[:n], nil +func (x *NewAutoEnvironmentRequest) GetRequestUser() *protos.User { + if x != nil { + return x.RequestUser } + return nil } -func (m *DestroyEnvironmentReply) XXX_Merge(src proto.Message) { - xxx_messageInfo_DestroyEnvironmentReply.Merge(m, src) + +type NewAutoEnvironmentReply struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Timestamp int64 `protobuf:"varint,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"` // timestamp of when this object was sent in unix milliseconds } -func (m *DestroyEnvironmentReply) XXX_Size() int { - return m.Size() + +func (x *NewAutoEnvironmentReply) Reset() { + *x = NewAutoEnvironmentReply{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *DestroyEnvironmentReply) XXX_DiscardUnknown() { - xxx_messageInfo_DestroyEnvironmentReply.DiscardUnknown(m) + +func (x *NewAutoEnvironmentReply) String() string { + return protoimpl.X.MessageStringOf(x) } -var xxx_messageInfo_DestroyEnvironmentReply proto.InternalMessageInfo +func (*NewAutoEnvironmentReply) ProtoMessage() {} -func (m *DestroyEnvironmentReply) GetCleanupTasksReply() *CleanupTasksReply { - if m != nil { - return m.CleanupTasksReply +func (x *NewAutoEnvironmentReply) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[12] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms } - return nil + return mi.MessageOf(x) } -//////////////////////////////////////// -// Tasks -//////////////////////////////////////// -type ShortTaskInfo struct { - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - Locked bool `protobuf:"varint,2,opt,name=locked,proto3" json:"locked,omitempty"` - TaskId string `protobuf:"bytes,3,opt,name=taskId,proto3" json:"taskId,omitempty"` - Status string `protobuf:"bytes,4,opt,name=status,proto3" json:"status,omitempty"` - State string `protobuf:"bytes,5,opt,name=state,proto3" json:"state,omitempty"` - ClassName string `protobuf:"bytes,6,opt,name=className,proto3" json:"className,omitempty"` - DeploymentInfo *TaskDeploymentInfo `protobuf:"bytes,7,opt,name=deploymentInfo,proto3" json:"deploymentInfo,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *ShortTaskInfo) Reset() { *m = ShortTaskInfo{} } -func (m *ShortTaskInfo) String() string { return proto.CompactTextString(m) } -func (*ShortTaskInfo) ProtoMessage() {} -func (*ShortTaskInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{23} -} -func (m *ShortTaskInfo) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *ShortTaskInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_ShortTaskInfo.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err - } - return b[:n], nil +// Deprecated: Use NewAutoEnvironmentReply.ProtoReflect.Descriptor instead. +func (*NewAutoEnvironmentReply) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{12} +} + +func (x *NewAutoEnvironmentReply) GetTimestamp() int64 { + if x != nil { + return x.Timestamp } + return 0 } -func (m *ShortTaskInfo) XXX_Merge(src proto.Message) { - xxx_messageInfo_ShortTaskInfo.Merge(m, src) + +type GetEnvironmentRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + ShowWorkflowTree bool `protobuf:"varint,2,opt,name=showWorkflowTree,proto3" json:"showWorkflowTree,omitempty"` } -func (m *ShortTaskInfo) XXX_Size() int { - return m.Size() + +func (x *GetEnvironmentRequest) Reset() { + *x = GetEnvironmentRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *ShortTaskInfo) XXX_DiscardUnknown() { - xxx_messageInfo_ShortTaskInfo.DiscardUnknown(m) + +func (x *GetEnvironmentRequest) String() string { + return protoimpl.X.MessageStringOf(x) } -var xxx_messageInfo_ShortTaskInfo proto.InternalMessageInfo +func (*GetEnvironmentRequest) ProtoMessage() {} -func (m *ShortTaskInfo) GetName() string { - if m != nil { - return m.Name +func (x *GetEnvironmentRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[13] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms } - return "" + return mi.MessageOf(x) } -func (m *ShortTaskInfo) GetLocked() bool { - if m != nil { - return m.Locked - } - return false +// Deprecated: Use GetEnvironmentRequest.ProtoReflect.Descriptor instead. +func (*GetEnvironmentRequest) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{13} } -func (m *ShortTaskInfo) GetTaskId() string { - if m != nil { - return m.TaskId +func (x *GetEnvironmentRequest) GetId() string { + if x != nil { + return x.Id } return "" } -func (m *ShortTaskInfo) GetStatus() string { - if m != nil { - return m.Status +func (x *GetEnvironmentRequest) GetShowWorkflowTree() bool { + if x != nil { + return x.ShowWorkflowTree } - return "" + return false } -func (m *ShortTaskInfo) GetState() string { - if m != nil { - return m.State - } - return "" +type GetEnvironmentReply struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Environment *EnvironmentInfo `protobuf:"bytes,1,opt,name=environment,proto3" json:"environment,omitempty"` + Workflow *RoleInfo `protobuf:"bytes,2,opt,name=workflow,proto3" json:"workflow,omitempty"` + Public bool `protobuf:"varint,3,opt,name=public,proto3" json:"public,omitempty"` + Timestamp int64 `protobuf:"varint,4,opt,name=timestamp,proto3" json:"timestamp,omitempty"` // timestamp of when this object was sent in unix milliseconds } -func (m *ShortTaskInfo) GetClassName() string { - if m != nil { - return m.ClassName +func (x *GetEnvironmentReply) Reset() { + *x = GetEnvironmentReply{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return "" } -func (m *ShortTaskInfo) GetDeploymentInfo() *TaskDeploymentInfo { - if m != nil { - return m.DeploymentInfo - } - return nil +func (x *GetEnvironmentReply) String() string { + return protoimpl.X.MessageStringOf(x) } -type TaskDeploymentInfo struct { - Hostname string `protobuf:"bytes,1,opt,name=hostname,proto3" json:"hostname,omitempty"` - AgentId string `protobuf:"bytes,2,opt,name=agentId,proto3" json:"agentId,omitempty"` - OfferId string `protobuf:"bytes,3,opt,name=offerId,proto3" json:"offerId,omitempty"` - ExecutorId string `protobuf:"bytes,4,opt,name=executorId,proto3" json:"executorId,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *TaskDeploymentInfo) Reset() { *m = TaskDeploymentInfo{} } -func (m *TaskDeploymentInfo) String() string { return proto.CompactTextString(m) } -func (*TaskDeploymentInfo) ProtoMessage() {} -func (*TaskDeploymentInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{24} -} -func (m *TaskDeploymentInfo) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *TaskDeploymentInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_TaskDeploymentInfo.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err +func (*GetEnvironmentReply) ProtoMessage() {} + +func (x *GetEnvironmentReply) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[14] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) } - return b[:n], nil + return ms } -} -func (m *TaskDeploymentInfo) XXX_Merge(src proto.Message) { - xxx_messageInfo_TaskDeploymentInfo.Merge(m, src) -} -func (m *TaskDeploymentInfo) XXX_Size() int { - return m.Size() -} -func (m *TaskDeploymentInfo) XXX_DiscardUnknown() { - xxx_messageInfo_TaskDeploymentInfo.DiscardUnknown(m) + return mi.MessageOf(x) } -var xxx_messageInfo_TaskDeploymentInfo proto.InternalMessageInfo +// Deprecated: Use GetEnvironmentReply.ProtoReflect.Descriptor instead. +func (*GetEnvironmentReply) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{14} +} -func (m *TaskDeploymentInfo) GetHostname() string { - if m != nil { - return m.Hostname +func (x *GetEnvironmentReply) GetEnvironment() *EnvironmentInfo { + if x != nil { + return x.Environment } - return "" + return nil } -func (m *TaskDeploymentInfo) GetAgentId() string { - if m != nil { - return m.AgentId +func (x *GetEnvironmentReply) GetWorkflow() *RoleInfo { + if x != nil { + return x.Workflow } - return "" + return nil } -func (m *TaskDeploymentInfo) GetOfferId() string { - if m != nil { - return m.OfferId +func (x *GetEnvironmentReply) GetPublic() bool { + if x != nil { + return x.Public } - return "" + return false } -func (m *TaskDeploymentInfo) GetExecutorId() string { - if m != nil { - return m.ExecutorId +func (x *GetEnvironmentReply) GetTimestamp() int64 { + if x != nil { + return x.Timestamp } - return "" + return 0 } -type GetTasksRequest struct { - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` +type ControlEnvironmentRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Type ControlEnvironmentRequest_Optype `protobuf:"varint,2,opt,name=type,proto3,enum=o2control.ControlEnvironmentRequest_Optype" json:"type,omitempty"` + RequestUser *protos.User `protobuf:"bytes,3,opt,name=requestUser,proto3" json:"requestUser,omitempty"` } -func (m *GetTasksRequest) Reset() { *m = GetTasksRequest{} } -func (m *GetTasksRequest) String() string { return proto.CompactTextString(m) } -func (*GetTasksRequest) ProtoMessage() {} -func (*GetTasksRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{25} -} -func (m *GetTasksRequest) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *GetTasksRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_GetTasksRequest.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err - } - return b[:n], nil +func (x *ControlEnvironmentRequest) Reset() { + *x = ControlEnvironmentRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[15] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } } -func (m *GetTasksRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_GetTasksRequest.Merge(m, src) -} -func (m *GetTasksRequest) XXX_Size() int { - return m.Size() -} -func (m *GetTasksRequest) XXX_DiscardUnknown() { - xxx_messageInfo_GetTasksRequest.DiscardUnknown(m) -} -var xxx_messageInfo_GetTasksRequest proto.InternalMessageInfo - -type GetTasksReply struct { - Tasks []*ShortTaskInfo `protobuf:"bytes,1,rep,name=tasks,proto3" json:"tasks,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` +func (x *ControlEnvironmentRequest) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *GetTasksReply) Reset() { *m = GetTasksReply{} } -func (m *GetTasksReply) String() string { return proto.CompactTextString(m) } -func (*GetTasksReply) ProtoMessage() {} -func (*GetTasksReply) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{26} -} -func (m *GetTasksReply) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *GetTasksReply) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_GetTasksReply.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err +func (*ControlEnvironmentRequest) ProtoMessage() {} + +func (x *ControlEnvironmentRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[15] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) } - return b[:n], nil + return ms } + return mi.MessageOf(x) } -func (m *GetTasksReply) XXX_Merge(src proto.Message) { - xxx_messageInfo_GetTasksReply.Merge(m, src) -} -func (m *GetTasksReply) XXX_Size() int { - return m.Size() + +// Deprecated: Use ControlEnvironmentRequest.ProtoReflect.Descriptor instead. +func (*ControlEnvironmentRequest) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{15} } -func (m *GetTasksReply) XXX_DiscardUnknown() { - xxx_messageInfo_GetTasksReply.DiscardUnknown(m) + +func (x *ControlEnvironmentRequest) GetId() string { + if x != nil { + return x.Id + } + return "" } -var xxx_messageInfo_GetTasksReply proto.InternalMessageInfo +func (x *ControlEnvironmentRequest) GetType() ControlEnvironmentRequest_Optype { + if x != nil { + return x.Type + } + return ControlEnvironmentRequest_NOOP +} -func (m *GetTasksReply) GetTasks() []*ShortTaskInfo { - if m != nil { - return m.Tasks +func (x *ControlEnvironmentRequest) GetRequestUser() *protos.User { + if x != nil { + return x.RequestUser } return nil } -type GetTaskRequest struct { - TaskId string `protobuf:"bytes,1,opt,name=taskId,proto3" json:"taskId,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` +type ControlEnvironmentReply struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + State string `protobuf:"bytes,2,opt,name=state,proto3" json:"state,omitempty"` + CurrentRunNumber uint32 `protobuf:"varint,3,opt,name=currentRunNumber,proto3" json:"currentRunNumber,omitempty"` + // All times are in milliseconds + StartOfTransition int64 `protobuf:"varint,4,opt,name=startOfTransition,proto3" json:"startOfTransition,omitempty"` + EndOfTransition int64 `protobuf:"varint,5,opt,name=endOfTransition,proto3" json:"endOfTransition,omitempty"` + TransitionDuration int64 `protobuf:"varint,6,opt,name=transitionDuration,proto3" json:"transitionDuration,omitempty"` + Timestamp int64 `protobuf:"varint,7,opt,name=timestamp,proto3" json:"timestamp,omitempty"` // timestamp of when this object was sent in unix milliseconds } -func (m *GetTaskRequest) Reset() { *m = GetTaskRequest{} } -func (m *GetTaskRequest) String() string { return proto.CompactTextString(m) } -func (*GetTaskRequest) ProtoMessage() {} -func (*GetTaskRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{27} -} -func (m *GetTaskRequest) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *GetTaskRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_GetTaskRequest.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err - } - return b[:n], nil +func (x *ControlEnvironmentReply) Reset() { + *x = ControlEnvironmentReply{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[16] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } } -func (m *GetTaskRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_GetTaskRequest.Merge(m, src) -} -func (m *GetTaskRequest) XXX_Size() int { - return m.Size() -} -func (m *GetTaskRequest) XXX_DiscardUnknown() { - xxx_messageInfo_GetTaskRequest.DiscardUnknown(m) + +func (x *ControlEnvironmentReply) String() string { + return protoimpl.X.MessageStringOf(x) } -var xxx_messageInfo_GetTaskRequest proto.InternalMessageInfo +func (*ControlEnvironmentReply) ProtoMessage() {} -func (m *GetTaskRequest) GetTaskId() string { - if m != nil { - return m.TaskId +func (x *ControlEnvironmentReply) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[16] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms } - return "" + return mi.MessageOf(x) } -type GetTaskReply struct { - Task *TaskInfo `protobuf:"bytes,1,opt,name=task,proto3" json:"task,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` +// Deprecated: Use ControlEnvironmentReply.ProtoReflect.Descriptor instead. +func (*ControlEnvironmentReply) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{16} } -func (m *GetTaskReply) Reset() { *m = GetTaskReply{} } -func (m *GetTaskReply) String() string { return proto.CompactTextString(m) } -func (*GetTaskReply) ProtoMessage() {} -func (*GetTaskReply) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{28} -} -func (m *GetTaskReply) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *GetTaskReply) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_GetTaskReply.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err - } - return b[:n], nil +func (x *ControlEnvironmentReply) GetId() string { + if x != nil { + return x.Id } + return "" } -func (m *GetTaskReply) XXX_Merge(src proto.Message) { - xxx_messageInfo_GetTaskReply.Merge(m, src) -} -func (m *GetTaskReply) XXX_Size() int { - return m.Size() -} -func (m *GetTaskReply) XXX_DiscardUnknown() { - xxx_messageInfo_GetTaskReply.DiscardUnknown(m) -} - -var xxx_messageInfo_GetTaskReply proto.InternalMessageInfo -func (m *GetTaskReply) GetTask() *TaskInfo { - if m != nil { - return m.Task +func (x *ControlEnvironmentReply) GetState() string { + if x != nil { + return x.State } - return nil + return "" } -type TaskClassInfo struct { - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - ControlMode string `protobuf:"bytes,2,opt,name=controlMode,proto3" json:"controlMode,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` +func (x *ControlEnvironmentReply) GetCurrentRunNumber() uint32 { + if x != nil { + return x.CurrentRunNumber + } + return 0 } -func (m *TaskClassInfo) Reset() { *m = TaskClassInfo{} } -func (m *TaskClassInfo) String() string { return proto.CompactTextString(m) } -func (*TaskClassInfo) ProtoMessage() {} -func (*TaskClassInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{29} -} -func (m *TaskClassInfo) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *TaskClassInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_TaskClassInfo.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err - } - return b[:n], nil +func (x *ControlEnvironmentReply) GetStartOfTransition() int64 { + if x != nil { + return x.StartOfTransition } + return 0 } -func (m *TaskClassInfo) XXX_Merge(src proto.Message) { - xxx_messageInfo_TaskClassInfo.Merge(m, src) -} -func (m *TaskClassInfo) XXX_Size() int { - return m.Size() -} -func (m *TaskClassInfo) XXX_DiscardUnknown() { - xxx_messageInfo_TaskClassInfo.DiscardUnknown(m) -} - -var xxx_messageInfo_TaskClassInfo proto.InternalMessageInfo -func (m *TaskClassInfo) GetName() string { - if m != nil { - return m.Name +func (x *ControlEnvironmentReply) GetEndOfTransition() int64 { + if x != nil { + return x.EndOfTransition } - return "" + return 0 } -func (m *TaskClassInfo) GetControlMode() string { - if m != nil { - return m.ControlMode +func (x *ControlEnvironmentReply) GetTransitionDuration() int64 { + if x != nil { + return x.TransitionDuration } - return "" + return 0 } -type CommandInfo struct { - Env []string `protobuf:"bytes,1,rep,name=env,proto3" json:"env,omitempty"` - Shell bool `protobuf:"varint,2,opt,name=shell,proto3" json:"shell,omitempty"` - Value string `protobuf:"bytes,3,opt,name=value,proto3" json:"value,omitempty"` - Arguments []string `protobuf:"bytes,4,rep,name=arguments,proto3" json:"arguments,omitempty"` - User string `protobuf:"bytes,5,opt,name=user,proto3" json:"user,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *CommandInfo) Reset() { *m = CommandInfo{} } -func (m *CommandInfo) String() string { return proto.CompactTextString(m) } -func (*CommandInfo) ProtoMessage() {} -func (*CommandInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{30} -} -func (m *CommandInfo) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *CommandInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_CommandInfo.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err - } - return b[:n], nil +func (x *ControlEnvironmentReply) GetTimestamp() int64 { + if x != nil { + return x.Timestamp } + return 0 } -func (m *CommandInfo) XXX_Merge(src proto.Message) { - xxx_messageInfo_CommandInfo.Merge(m, src) + +type ModifyEnvironmentRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Operations []*EnvironmentOperation `protobuf:"bytes,2,rep,name=operations,proto3" json:"operations,omitempty"` + ReconfigureAll bool `protobuf:"varint,3,opt,name=reconfigureAll,proto3" json:"reconfigureAll,omitempty"` } -func (m *CommandInfo) XXX_Size() int { - return m.Size() + +func (x *ModifyEnvironmentRequest) Reset() { + *x = ModifyEnvironmentRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[17] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *CommandInfo) XXX_DiscardUnknown() { - xxx_messageInfo_CommandInfo.DiscardUnknown(m) + +func (x *ModifyEnvironmentRequest) String() string { + return protoimpl.X.MessageStringOf(x) } -var xxx_messageInfo_CommandInfo proto.InternalMessageInfo +func (*ModifyEnvironmentRequest) ProtoMessage() {} -func (m *CommandInfo) GetEnv() []string { - if m != nil { - return m.Env +func (x *ModifyEnvironmentRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[17] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms } - return nil + return mi.MessageOf(x) } -func (m *CommandInfo) GetShell() bool { - if m != nil { - return m.Shell - } - return false +// Deprecated: Use ModifyEnvironmentRequest.ProtoReflect.Descriptor instead. +func (*ModifyEnvironmentRequest) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{17} } -func (m *CommandInfo) GetValue() string { - if m != nil { - return m.Value +func (x *ModifyEnvironmentRequest) GetId() string { + if x != nil { + return x.Id } return "" } -func (m *CommandInfo) GetArguments() []string { - if m != nil { - return m.Arguments +func (x *ModifyEnvironmentRequest) GetOperations() []*EnvironmentOperation { + if x != nil { + return x.Operations } return nil } -func (m *CommandInfo) GetUser() string { - if m != nil { - return m.User +func (x *ModifyEnvironmentRequest) GetReconfigureAll() bool { + if x != nil { + return x.ReconfigureAll } - return "" + return false } -type ChannelInfo struct { - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - Type string `protobuf:"bytes,2,opt,name=type,proto3" json:"type,omitempty"` - Target string `protobuf:"bytes,3,opt,name=target,proto3" json:"target,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` +type EnvironmentOperation struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Type EnvironmentOperation_Optype `protobuf:"varint,1,opt,name=type,proto3,enum=o2control.EnvironmentOperation_Optype" json:"type,omitempty"` + RoleName string `protobuf:"bytes,2,opt,name=roleName,proto3" json:"roleName,omitempty"` } -func (m *ChannelInfo) Reset() { *m = ChannelInfo{} } -func (m *ChannelInfo) String() string { return proto.CompactTextString(m) } -func (*ChannelInfo) ProtoMessage() {} -func (*ChannelInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{31} -} -func (m *ChannelInfo) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *ChannelInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_ChannelInfo.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err - } - return b[:n], nil +func (x *EnvironmentOperation) Reset() { + *x = EnvironmentOperation{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[18] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } } -func (m *ChannelInfo) XXX_Merge(src proto.Message) { - xxx_messageInfo_ChannelInfo.Merge(m, src) -} -func (m *ChannelInfo) XXX_Size() int { - return m.Size() -} -func (m *ChannelInfo) XXX_DiscardUnknown() { - xxx_messageInfo_ChannelInfo.DiscardUnknown(m) + +func (x *EnvironmentOperation) String() string { + return protoimpl.X.MessageStringOf(x) } -var xxx_messageInfo_ChannelInfo proto.InternalMessageInfo +func (*EnvironmentOperation) ProtoMessage() {} -func (m *ChannelInfo) GetName() string { - if m != nil { - return m.Name +func (x *EnvironmentOperation) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[18] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms } - return "" + return mi.MessageOf(x) } -func (m *ChannelInfo) GetType() string { - if m != nil { - return m.Type - } - return "" +// Deprecated: Use EnvironmentOperation.ProtoReflect.Descriptor instead. +func (*EnvironmentOperation) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{18} } -func (m *ChannelInfo) GetTarget() string { - if m != nil { - return m.Target +func (x *EnvironmentOperation) GetType() EnvironmentOperation_Optype { + if x != nil { + return x.Type } - return "" + return EnvironmentOperation_NOOP } -type TaskInfo struct { - ShortInfo *ShortTaskInfo `protobuf:"bytes,1,opt,name=shortInfo,proto3" json:"shortInfo,omitempty"` - ClassInfo *TaskClassInfo `protobuf:"bytes,2,opt,name=classInfo,proto3" json:"classInfo,omitempty"` - InboundChannels []*ChannelInfo `protobuf:"bytes,3,rep,name=inboundChannels,proto3" json:"inboundChannels,omitempty"` - OutboundChannels []*ChannelInfo `protobuf:"bytes,4,rep,name=outboundChannels,proto3" json:"outboundChannels,omitempty"` - CommandInfo *CommandInfo `protobuf:"bytes,5,opt,name=commandInfo,proto3" json:"commandInfo,omitempty"` - TaskPath string `protobuf:"bytes,6,opt,name=taskPath,proto3" json:"taskPath,omitempty"` - EnvId string `protobuf:"bytes,7,opt,name=envId,proto3" json:"envId,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *TaskInfo) Reset() { *m = TaskInfo{} } -func (m *TaskInfo) String() string { return proto.CompactTextString(m) } -func (*TaskInfo) ProtoMessage() {} -func (*TaskInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{32} -} -func (m *TaskInfo) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *TaskInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_TaskInfo.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err - } - return b[:n], nil +func (x *EnvironmentOperation) GetRoleName() string { + if x != nil { + return x.RoleName } -} -func (m *TaskInfo) XXX_Merge(src proto.Message) { - xxx_messageInfo_TaskInfo.Merge(m, src) -} -func (m *TaskInfo) XXX_Size() int { - return m.Size() -} -func (m *TaskInfo) XXX_DiscardUnknown() { - xxx_messageInfo_TaskInfo.DiscardUnknown(m) + return "" } -var xxx_messageInfo_TaskInfo proto.InternalMessageInfo +type ModifyEnvironmentReply struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields -func (m *TaskInfo) GetShortInfo() *ShortTaskInfo { - if m != nil { - return m.ShortInfo - } - return nil + FailedOperations []*EnvironmentOperation `protobuf:"bytes,1,rep,name=failedOperations,proto3" json:"failedOperations,omitempty"` + Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"` + State string `protobuf:"bytes,3,opt,name=state,proto3" json:"state,omitempty"` } -func (m *TaskInfo) GetClassInfo() *TaskClassInfo { - if m != nil { - return m.ClassInfo +func (x *ModifyEnvironmentReply) Reset() { + *x = ModifyEnvironmentReply{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[19] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return nil } -func (m *TaskInfo) GetInboundChannels() []*ChannelInfo { - if m != nil { - return m.InboundChannels - } - return nil +func (x *ModifyEnvironmentReply) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *TaskInfo) GetOutboundChannels() []*ChannelInfo { - if m != nil { - return m.OutboundChannels +func (*ModifyEnvironmentReply) ProtoMessage() {} + +func (x *ModifyEnvironmentReply) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[19] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms } - return nil + return mi.MessageOf(x) +} + +// Deprecated: Use ModifyEnvironmentReply.ProtoReflect.Descriptor instead. +func (*ModifyEnvironmentReply) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{19} } -func (m *TaskInfo) GetCommandInfo() *CommandInfo { - if m != nil { - return m.CommandInfo +func (x *ModifyEnvironmentReply) GetFailedOperations() []*EnvironmentOperation { + if x != nil { + return x.FailedOperations } return nil } -func (m *TaskInfo) GetTaskPath() string { - if m != nil { - return m.TaskPath +func (x *ModifyEnvironmentReply) GetId() string { + if x != nil { + return x.Id } return "" } -func (m *TaskInfo) GetEnvId() string { - if m != nil { - return m.EnvId +func (x *ModifyEnvironmentReply) GetState() string { + if x != nil { + return x.State } return "" } -type CleanupTasksRequest struct { - TaskIds []string `protobuf:"bytes,1,rep,name=taskIds,proto3" json:"taskIds,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` +type DestroyEnvironmentRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + KeepTasks bool `protobuf:"varint,2,opt,name=keepTasks,proto3" json:"keepTasks,omitempty"` + AllowInRunningState bool `protobuf:"varint,3,opt,name=allowInRunningState,proto3" json:"allowInRunningState,omitempty"` + Force bool `protobuf:"varint,4,opt,name=force,proto3" json:"force,omitempty"` + RequestUser *protos.User `protobuf:"bytes,5,opt,name=requestUser,proto3" json:"requestUser,omitempty"` } -func (m *CleanupTasksRequest) Reset() { *m = CleanupTasksRequest{} } -func (m *CleanupTasksRequest) String() string { return proto.CompactTextString(m) } -func (*CleanupTasksRequest) ProtoMessage() {} -func (*CleanupTasksRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{33} -} -func (m *CleanupTasksRequest) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *CleanupTasksRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_CleanupTasksRequest.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err - } - return b[:n], nil +func (x *DestroyEnvironmentRequest) Reset() { + *x = DestroyEnvironmentRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[20] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } } -func (m *CleanupTasksRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_CleanupTasksRequest.Merge(m, src) -} -func (m *CleanupTasksRequest) XXX_Size() int { - return m.Size() -} -func (m *CleanupTasksRequest) XXX_DiscardUnknown() { - xxx_messageInfo_CleanupTasksRequest.DiscardUnknown(m) + +func (x *DestroyEnvironmentRequest) String() string { + return protoimpl.X.MessageStringOf(x) } -var xxx_messageInfo_CleanupTasksRequest proto.InternalMessageInfo +func (*DestroyEnvironmentRequest) ProtoMessage() {} -func (m *CleanupTasksRequest) GetTaskIds() []string { - if m != nil { - return m.TaskIds +func (x *DestroyEnvironmentRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[20] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms } - return nil + return mi.MessageOf(x) } -type CleanupTasksReply struct { - KilledTasks []*ShortTaskInfo `protobuf:"bytes,1,rep,name=killedTasks,proto3" json:"killedTasks,omitempty"` - RunningTasks []*ShortTaskInfo `protobuf:"bytes,2,rep,name=runningTasks,proto3" json:"runningTasks,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` +// Deprecated: Use DestroyEnvironmentRequest.ProtoReflect.Descriptor instead. +func (*DestroyEnvironmentRequest) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{20} } -func (m *CleanupTasksReply) Reset() { *m = CleanupTasksReply{} } -func (m *CleanupTasksReply) String() string { return proto.CompactTextString(m) } -func (*CleanupTasksReply) ProtoMessage() {} -func (*CleanupTasksReply) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{34} -} -func (m *CleanupTasksReply) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *CleanupTasksReply) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_CleanupTasksReply.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err - } - return b[:n], nil +func (x *DestroyEnvironmentRequest) GetId() string { + if x != nil { + return x.Id } + return "" } -func (m *CleanupTasksReply) XXX_Merge(src proto.Message) { - xxx_messageInfo_CleanupTasksReply.Merge(m, src) -} -func (m *CleanupTasksReply) XXX_Size() int { - return m.Size() -} -func (m *CleanupTasksReply) XXX_DiscardUnknown() { - xxx_messageInfo_CleanupTasksReply.DiscardUnknown(m) + +func (x *DestroyEnvironmentRequest) GetKeepTasks() bool { + if x != nil { + return x.KeepTasks + } + return false } -var xxx_messageInfo_CleanupTasksReply proto.InternalMessageInfo +func (x *DestroyEnvironmentRequest) GetAllowInRunningState() bool { + if x != nil { + return x.AllowInRunningState + } + return false +} -func (m *CleanupTasksReply) GetKilledTasks() []*ShortTaskInfo { - if m != nil { - return m.KilledTasks +func (x *DestroyEnvironmentRequest) GetForce() bool { + if x != nil { + return x.Force } - return nil + return false } -func (m *CleanupTasksReply) GetRunningTasks() []*ShortTaskInfo { - if m != nil { - return m.RunningTasks +func (x *DestroyEnvironmentRequest) GetRequestUser() *protos.User { + if x != nil { + return x.RequestUser } return nil } -//////////////////////////////////////// -// Roles -//////////////////////////////////////// -type GetRolesRequest struct { - EnvId string `protobuf:"bytes,1,opt,name=envId,proto3" json:"envId,omitempty"` - PathSpec string `protobuf:"bytes,2,opt,name=pathSpec,proto3" json:"pathSpec,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` +type DestroyEnvironmentReply struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + CleanupTasksReply *CleanupTasksReply `protobuf:"bytes,1,opt,name=cleanupTasksReply,proto3" json:"cleanupTasksReply,omitempty"` + Timestamp int64 `protobuf:"varint,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"` // timestamp of when this object was sent in unix milliseconds } -func (m *GetRolesRequest) Reset() { *m = GetRolesRequest{} } -func (m *GetRolesRequest) String() string { return proto.CompactTextString(m) } -func (*GetRolesRequest) ProtoMessage() {} -func (*GetRolesRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{35} -} -func (m *GetRolesRequest) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *GetRolesRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_GetRolesRequest.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err - } - return b[:n], nil +func (x *DestroyEnvironmentReply) Reset() { + *x = DestroyEnvironmentReply{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[21] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } } -func (m *GetRolesRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_GetRolesRequest.Merge(m, src) -} -func (m *GetRolesRequest) XXX_Size() int { - return m.Size() -} -func (m *GetRolesRequest) XXX_DiscardUnknown() { - xxx_messageInfo_GetRolesRequest.DiscardUnknown(m) + +func (x *DestroyEnvironmentReply) String() string { + return protoimpl.X.MessageStringOf(x) } -var xxx_messageInfo_GetRolesRequest proto.InternalMessageInfo +func (*DestroyEnvironmentReply) ProtoMessage() {} -func (m *GetRolesRequest) GetEnvId() string { - if m != nil { - return m.EnvId +func (x *DestroyEnvironmentReply) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[21] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms } - return "" + return mi.MessageOf(x) +} + +// Deprecated: Use DestroyEnvironmentReply.ProtoReflect.Descriptor instead. +func (*DestroyEnvironmentReply) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{21} } -func (m *GetRolesRequest) GetPathSpec() string { - if m != nil { - return m.PathSpec +func (x *DestroyEnvironmentReply) GetCleanupTasksReply() *CleanupTasksReply { + if x != nil { + return x.CleanupTasksReply } - return "" + return nil } -type RoleInfo struct { - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - Status string `protobuf:"bytes,2,opt,name=status,proto3" json:"status,omitempty"` - State string `protobuf:"bytes,3,opt,name=state,proto3" json:"state,omitempty"` - FullPath string `protobuf:"bytes,4,opt,name=fullPath,proto3" json:"fullPath,omitempty"` - TaskIds []string `protobuf:"bytes,5,rep,name=taskIds,proto3" json:"taskIds,omitempty"` - Roles []*RoleInfo `protobuf:"bytes,6,rep,name=roles,proto3" json:"roles,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *RoleInfo) Reset() { *m = RoleInfo{} } -func (m *RoleInfo) String() string { return proto.CompactTextString(m) } -func (*RoleInfo) ProtoMessage() {} -func (*RoleInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{36} -} -func (m *RoleInfo) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *RoleInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_RoleInfo.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err - } - return b[:n], nil +func (x *DestroyEnvironmentReply) GetTimestamp() int64 { + if x != nil { + return x.Timestamp } + return 0 } -func (m *RoleInfo) XXX_Merge(src proto.Message) { - xxx_messageInfo_RoleInfo.Merge(m, src) + +type GetActiveDetectorsReply struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Detectors []string `protobuf:"bytes,1,rep,name=detectors,proto3" json:"detectors,omitempty"` + Timestamp int64 `protobuf:"varint,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"` // timestamp of when this object was sent in unix milliseconds } -func (m *RoleInfo) XXX_Size() int { - return m.Size() + +func (x *GetActiveDetectorsReply) Reset() { + *x = GetActiveDetectorsReply{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[22] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *RoleInfo) XXX_DiscardUnknown() { - xxx_messageInfo_RoleInfo.DiscardUnknown(m) + +func (x *GetActiveDetectorsReply) String() string { + return protoimpl.X.MessageStringOf(x) } -var xxx_messageInfo_RoleInfo proto.InternalMessageInfo +func (*GetActiveDetectorsReply) ProtoMessage() {} -func (m *RoleInfo) GetName() string { - if m != nil { - return m.Name +func (x *GetActiveDetectorsReply) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[22] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms } - return "" + return mi.MessageOf(x) } -func (m *RoleInfo) GetStatus() string { - if m != nil { - return m.Status - } - return "" +// Deprecated: Use GetActiveDetectorsReply.ProtoReflect.Descriptor instead. +func (*GetActiveDetectorsReply) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{22} } -func (m *RoleInfo) GetState() string { - if m != nil { - return m.State +func (x *GetActiveDetectorsReply) GetDetectors() []string { + if x != nil { + return x.Detectors } - return "" + return nil } -func (m *RoleInfo) GetFullPath() string { - if m != nil { - return m.FullPath +func (x *GetActiveDetectorsReply) GetTimestamp() int64 { + if x != nil { + return x.Timestamp } - return "" + return 0 } -func (m *RoleInfo) GetTaskIds() []string { - if m != nil { - return m.TaskIds - } - return nil +type GetAvailableDetectorsReply struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Detectors []string `protobuf:"bytes,1,rep,name=detectors,proto3" json:"detectors,omitempty"` + Timestamp int64 `protobuf:"varint,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"` // timestamp of when this object was sent in unix milliseconds } -func (m *RoleInfo) GetRoles() []*RoleInfo { - if m != nil { - return m.Roles +func (x *GetAvailableDetectorsReply) Reset() { + *x = GetAvailableDetectorsReply{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[23] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return nil } -type GetRolesReply struct { - Roles []*RoleInfo `protobuf:"bytes,1,rep,name=roles,proto3" json:"roles,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` +func (x *GetAvailableDetectorsReply) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *GetRolesReply) Reset() { *m = GetRolesReply{} } -func (m *GetRolesReply) String() string { return proto.CompactTextString(m) } -func (*GetRolesReply) ProtoMessage() {} -func (*GetRolesReply) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{37} -} -func (m *GetRolesReply) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *GetRolesReply) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_GetRolesReply.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err +func (*GetAvailableDetectorsReply) ProtoMessage() {} + +func (x *GetAvailableDetectorsReply) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[23] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) } - return b[:n], nil + return ms } + return mi.MessageOf(x) } -func (m *GetRolesReply) XXX_Merge(src proto.Message) { - xxx_messageInfo_GetRolesReply.Merge(m, src) + +// Deprecated: Use GetAvailableDetectorsReply.ProtoReflect.Descriptor instead. +func (*GetAvailableDetectorsReply) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{23} } -func (m *GetRolesReply) XXX_Size() int { - return m.Size() + +func (x *GetAvailableDetectorsReply) GetDetectors() []string { + if x != nil { + return x.Detectors + } + return nil } -func (m *GetRolesReply) XXX_DiscardUnknown() { - xxx_messageInfo_GetRolesReply.DiscardUnknown(m) + +func (x *GetAvailableDetectorsReply) GetTimestamp() int64 { + if x != nil { + return x.Timestamp + } + return 0 } -var xxx_messageInfo_GetRolesReply proto.InternalMessageInfo +// ////////////////////////////////////// +// Environment, GET/SET properties +// ////////////////////////////////////// +type SetEnvironmentPropertiesRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + // If properties == nil, the core sets nothing + // and reply ok + Properties map[string]string `protobuf:"bytes,2,rep,name=properties,proto3" json:"properties,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} -func (m *GetRolesReply) GetRoles() []*RoleInfo { - if m != nil { - return m.Roles +func (x *SetEnvironmentPropertiesRequest) Reset() { + *x = SetEnvironmentPropertiesRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[24] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return nil } -type GetWorkflowTemplatesRequest struct { - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` +func (x *SetEnvironmentPropertiesRequest) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *GetWorkflowTemplatesRequest) Reset() { *m = GetWorkflowTemplatesRequest{} } -func (m *GetWorkflowTemplatesRequest) String() string { return proto.CompactTextString(m) } -func (*GetWorkflowTemplatesRequest) ProtoMessage() {} -func (*GetWorkflowTemplatesRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{38} -} -func (m *GetWorkflowTemplatesRequest) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *GetWorkflowTemplatesRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_GetWorkflowTemplatesRequest.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err +func (*SetEnvironmentPropertiesRequest) ProtoMessage() {} + +func (x *SetEnvironmentPropertiesRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[24] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) } - return b[:n], nil + return ms } + return mi.MessageOf(x) } -func (m *GetWorkflowTemplatesRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_GetWorkflowTemplatesRequest.Merge(m, src) -} -func (m *GetWorkflowTemplatesRequest) XXX_Size() int { - return m.Size() + +// Deprecated: Use SetEnvironmentPropertiesRequest.ProtoReflect.Descriptor instead. +func (*SetEnvironmentPropertiesRequest) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{24} } -func (m *GetWorkflowTemplatesRequest) XXX_DiscardUnknown() { - xxx_messageInfo_GetWorkflowTemplatesRequest.DiscardUnknown(m) + +func (x *SetEnvironmentPropertiesRequest) GetId() string { + if x != nil { + return x.Id + } + return "" } -var xxx_messageInfo_GetWorkflowTemplatesRequest proto.InternalMessageInfo +func (x *SetEnvironmentPropertiesRequest) GetProperties() map[string]string { + if x != nil { + return x.Properties + } + return nil +} -type WorkflowTemplateInfo struct { - Repo string `protobuf:"bytes,1,opt,name=repo,proto3" json:"repo,omitempty"` - Template string `protobuf:"bytes,2,opt,name=template,proto3" json:"template,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` +type SetEnvironmentPropertiesReply struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields } -func (m *WorkflowTemplateInfo) Reset() { *m = WorkflowTemplateInfo{} } -func (m *WorkflowTemplateInfo) String() string { return proto.CompactTextString(m) } -func (*WorkflowTemplateInfo) ProtoMessage() {} -func (*WorkflowTemplateInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{39} -} -func (m *WorkflowTemplateInfo) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *WorkflowTemplateInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_WorkflowTemplateInfo.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err - } - return b[:n], nil +func (x *SetEnvironmentPropertiesReply) Reset() { + *x = SetEnvironmentPropertiesReply{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[25] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } } -func (m *WorkflowTemplateInfo) XXX_Merge(src proto.Message) { - xxx_messageInfo_WorkflowTemplateInfo.Merge(m, src) + +func (x *SetEnvironmentPropertiesReply) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *WorkflowTemplateInfo) XXX_Size() int { - return m.Size() + +func (*SetEnvironmentPropertiesReply) ProtoMessage() {} + +func (x *SetEnvironmentPropertiesReply) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[25] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -func (m *WorkflowTemplateInfo) XXX_DiscardUnknown() { - xxx_messageInfo_WorkflowTemplateInfo.DiscardUnknown(m) + +// Deprecated: Use SetEnvironmentPropertiesReply.ProtoReflect.Descriptor instead. +func (*SetEnvironmentPropertiesReply) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{25} } -var xxx_messageInfo_WorkflowTemplateInfo proto.InternalMessageInfo +type GetEnvironmentPropertiesRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields -func (m *WorkflowTemplateInfo) GetRepo() string { - if m != nil { - return m.Repo - } - return "" + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + // If len(queries) == 0, we return an + // empty map. + // To retrieve all KVs, use query '*' + Queries []string `protobuf:"bytes,2,rep,name=queries,proto3" json:"queries,omitempty"` + ExcludeGlobals bool `protobuf:"varint,3,opt,name=excludeGlobals,proto3" json:"excludeGlobals,omitempty"` } -func (m *WorkflowTemplateInfo) GetTemplate() string { - if m != nil { - return m.Template +func (x *GetEnvironmentPropertiesRequest) Reset() { + *x = GetEnvironmentPropertiesRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[26] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return "" } -type GetWorkflowTemplatesReply struct { - WorkflowTemplates []*WorkflowTemplateInfo `protobuf:"bytes,1,rep,name=workflowTemplates,proto3" json:"workflowTemplates,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` +func (x *GetEnvironmentPropertiesRequest) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *GetWorkflowTemplatesReply) Reset() { *m = GetWorkflowTemplatesReply{} } -func (m *GetWorkflowTemplatesReply) String() string { return proto.CompactTextString(m) } -func (*GetWorkflowTemplatesReply) ProtoMessage() {} -func (*GetWorkflowTemplatesReply) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{40} -} -func (m *GetWorkflowTemplatesReply) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *GetWorkflowTemplatesReply) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_GetWorkflowTemplatesReply.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err +func (*GetEnvironmentPropertiesRequest) ProtoMessage() {} + +func (x *GetEnvironmentPropertiesRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[26] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) } - return b[:n], nil + return ms } + return mi.MessageOf(x) +} + +// Deprecated: Use GetEnvironmentPropertiesRequest.ProtoReflect.Descriptor instead. +func (*GetEnvironmentPropertiesRequest) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{26} } -func (m *GetWorkflowTemplatesReply) XXX_Merge(src proto.Message) { - xxx_messageInfo_GetWorkflowTemplatesReply.Merge(m, src) + +func (x *GetEnvironmentPropertiesRequest) GetId() string { + if x != nil { + return x.Id + } + return "" } -func (m *GetWorkflowTemplatesReply) XXX_Size() int { - return m.Size() + +func (x *GetEnvironmentPropertiesRequest) GetQueries() []string { + if x != nil { + return x.Queries + } + return nil } -func (m *GetWorkflowTemplatesReply) XXX_DiscardUnknown() { - xxx_messageInfo_GetWorkflowTemplatesReply.DiscardUnknown(m) + +func (x *GetEnvironmentPropertiesRequest) GetExcludeGlobals() bool { + if x != nil { + return x.ExcludeGlobals + } + return false } -var xxx_messageInfo_GetWorkflowTemplatesReply proto.InternalMessageInfo +type GetEnvironmentPropertiesReply struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Properties map[string]string `protobuf:"bytes,1,rep,name=properties,proto3" json:"properties,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} -func (m *GetWorkflowTemplatesReply) GetWorkflowTemplates() []*WorkflowTemplateInfo { - if m != nil { - return m.WorkflowTemplates +func (x *GetEnvironmentPropertiesReply) Reset() { + *x = GetEnvironmentPropertiesReply{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[27] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return nil } -type ListReposRequest struct { - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` +func (x *GetEnvironmentPropertiesReply) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *ListReposRequest) Reset() { *m = ListReposRequest{} } -func (m *ListReposRequest) String() string { return proto.CompactTextString(m) } -func (*ListReposRequest) ProtoMessage() {} -func (*ListReposRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{41} -} -func (m *ListReposRequest) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *ListReposRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_ListReposRequest.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err +func (*GetEnvironmentPropertiesReply) ProtoMessage() {} + +func (x *GetEnvironmentPropertiesReply) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[27] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) } - return b[:n], nil + return ms } + return mi.MessageOf(x) } -func (m *ListReposRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_ListReposRequest.Merge(m, src) -} -func (m *ListReposRequest) XXX_Size() int { - return m.Size() + +// Deprecated: Use GetEnvironmentPropertiesReply.ProtoReflect.Descriptor instead. +func (*GetEnvironmentPropertiesReply) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{27} } -func (m *ListReposRequest) XXX_DiscardUnknown() { - xxx_messageInfo_ListReposRequest.DiscardUnknown(m) + +func (x *GetEnvironmentPropertiesReply) GetProperties() map[string]string { + if x != nil { + return x.Properties + } + return nil } -var xxx_messageInfo_ListReposRequest proto.InternalMessageInfo +// ////////////////////////////////////// +// Tasks +// ////////////////////////////////////// +type ShortTaskInfo struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields -type RepoInfo struct { - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - Default bool `protobuf:"varint,2,opt,name=default,proto3" json:"default,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Locked bool `protobuf:"varint,2,opt,name=locked,proto3" json:"locked,omitempty"` + TaskId string `protobuf:"bytes,3,opt,name=taskId,proto3" json:"taskId,omitempty"` + Status string `protobuf:"bytes,4,opt,name=status,proto3" json:"status,omitempty"` + State string `protobuf:"bytes,5,opt,name=state,proto3" json:"state,omitempty"` + ClassName string `protobuf:"bytes,6,opt,name=className,proto3" json:"className,omitempty"` + DeploymentInfo *TaskDeploymentInfo `protobuf:"bytes,7,opt,name=deploymentInfo,proto3" json:"deploymentInfo,omitempty"` + Pid string `protobuf:"bytes,8,opt,name=pid,proto3" json:"pid,omitempty"` + SandboxStdout string `protobuf:"bytes,9,opt,name=sandboxStdout,proto3" json:"sandboxStdout,omitempty"` + Claimable bool `protobuf:"varint,10,opt,name=claimable,proto3" json:"claimable,omitempty"` + Critical bool `protobuf:"varint,11,opt,name=critical,proto3" json:"critical,omitempty"` } -func (m *RepoInfo) Reset() { *m = RepoInfo{} } -func (m *RepoInfo) String() string { return proto.CompactTextString(m) } -func (*RepoInfo) ProtoMessage() {} -func (*RepoInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{42} -} -func (m *RepoInfo) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *RepoInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_RepoInfo.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err - } - return b[:n], nil +func (x *ShortTaskInfo) Reset() { + *x = ShortTaskInfo{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[28] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } } -func (m *RepoInfo) XXX_Merge(src proto.Message) { - xxx_messageInfo_RepoInfo.Merge(m, src) -} -func (m *RepoInfo) XXX_Size() int { - return m.Size() + +func (x *ShortTaskInfo) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *RepoInfo) XXX_DiscardUnknown() { - xxx_messageInfo_RepoInfo.DiscardUnknown(m) + +func (*ShortTaskInfo) ProtoMessage() {} + +func (x *ShortTaskInfo) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[28] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_RepoInfo proto.InternalMessageInfo +// Deprecated: Use ShortTaskInfo.ProtoReflect.Descriptor instead. +func (*ShortTaskInfo) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{28} +} -func (m *RepoInfo) GetName() string { - if m != nil { - return m.Name +func (x *ShortTaskInfo) GetName() string { + if x != nil { + return x.Name } return "" } -func (m *RepoInfo) GetDefault() bool { - if m != nil { - return m.Default +func (x *ShortTaskInfo) GetLocked() bool { + if x != nil { + return x.Locked } return false } -type ListReposReply struct { - Repos []*RepoInfo `protobuf:"bytes,1,rep,name=repos,proto3" json:"repos,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` +func (x *ShortTaskInfo) GetTaskId() string { + if x != nil { + return x.TaskId + } + return "" } -func (m *ListReposReply) Reset() { *m = ListReposReply{} } -func (m *ListReposReply) String() string { return proto.CompactTextString(m) } -func (*ListReposReply) ProtoMessage() {} -func (*ListReposReply) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{43} -} -func (m *ListReposReply) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *ListReposReply) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_ListReposReply.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err - } - return b[:n], nil +func (x *ShortTaskInfo) GetStatus() string { + if x != nil { + return x.Status } + return "" } -func (m *ListReposReply) XXX_Merge(src proto.Message) { - xxx_messageInfo_ListReposReply.Merge(m, src) -} -func (m *ListReposReply) XXX_Size() int { - return m.Size() -} -func (m *ListReposReply) XXX_DiscardUnknown() { - xxx_messageInfo_ListReposReply.DiscardUnknown(m) + +func (x *ShortTaskInfo) GetState() string { + if x != nil { + return x.State + } + return "" } -var xxx_messageInfo_ListReposReply proto.InternalMessageInfo +func (x *ShortTaskInfo) GetClassName() string { + if x != nil { + return x.ClassName + } + return "" +} -func (m *ListReposReply) GetRepos() []*RepoInfo { - if m != nil { - return m.Repos +func (x *ShortTaskInfo) GetDeploymentInfo() *TaskDeploymentInfo { + if x != nil { + return x.DeploymentInfo } return nil } -type AddRepoRequest struct { - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` +func (x *ShortTaskInfo) GetPid() string { + if x != nil { + return x.Pid + } + return "" } -func (m *AddRepoRequest) Reset() { *m = AddRepoRequest{} } -func (m *AddRepoRequest) String() string { return proto.CompactTextString(m) } -func (*AddRepoRequest) ProtoMessage() {} -func (*AddRepoRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{44} -} -func (m *AddRepoRequest) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *AddRepoRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_AddRepoRequest.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err - } - return b[:n], nil +func (x *ShortTaskInfo) GetSandboxStdout() string { + if x != nil { + return x.SandboxStdout } + return "" } -func (m *AddRepoRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_AddRepoRequest.Merge(m, src) -} -func (m *AddRepoRequest) XXX_Size() int { - return m.Size() + +func (x *ShortTaskInfo) GetClaimable() bool { + if x != nil { + return x.Claimable + } + return false } -func (m *AddRepoRequest) XXX_DiscardUnknown() { - xxx_messageInfo_AddRepoRequest.DiscardUnknown(m) + +func (x *ShortTaskInfo) GetCritical() bool { + if x != nil { + return x.Critical + } + return false } -var xxx_messageInfo_AddRepoRequest proto.InternalMessageInfo +type TaskDeploymentInfo struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Hostname string `protobuf:"bytes,1,opt,name=hostname,proto3" json:"hostname,omitempty"` + AgentId string `protobuf:"bytes,2,opt,name=agentId,proto3" json:"agentId,omitempty"` + OfferId string `protobuf:"bytes,3,opt,name=offerId,proto3" json:"offerId,omitempty"` + ExecutorId string `protobuf:"bytes,4,opt,name=executorId,proto3" json:"executorId,omitempty"` +} -func (m *AddRepoRequest) GetName() string { - if m != nil { - return m.Name +func (x *TaskDeploymentInfo) Reset() { + *x = TaskDeploymentInfo{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[29] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return "" } -type AddRepoReply struct { - ErrorString string `protobuf:"bytes,1,opt,name=errorString,proto3" json:"errorString,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` +func (x *TaskDeploymentInfo) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *AddRepoReply) Reset() { *m = AddRepoReply{} } -func (m *AddRepoReply) String() string { return proto.CompactTextString(m) } -func (*AddRepoReply) ProtoMessage() {} -func (*AddRepoReply) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{45} -} -func (m *AddRepoReply) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *AddRepoReply) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_AddRepoReply.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err +func (*TaskDeploymentInfo) ProtoMessage() {} + +func (x *TaskDeploymentInfo) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[29] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) } - return b[:n], nil + return ms } -} -func (m *AddRepoReply) XXX_Merge(src proto.Message) { - xxx_messageInfo_AddRepoReply.Merge(m, src) -} -func (m *AddRepoReply) XXX_Size() int { - return m.Size() -} -func (m *AddRepoReply) XXX_DiscardUnknown() { - xxx_messageInfo_AddRepoReply.DiscardUnknown(m) + return mi.MessageOf(x) } -var xxx_messageInfo_AddRepoReply proto.InternalMessageInfo +// Deprecated: Use TaskDeploymentInfo.ProtoReflect.Descriptor instead. +func (*TaskDeploymentInfo) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{29} +} -func (m *AddRepoReply) GetErrorString() string { - if m != nil { - return m.ErrorString +func (x *TaskDeploymentInfo) GetHostname() string { + if x != nil { + return x.Hostname } return "" } -type RemoveRepoRequest struct { - Index int32 `protobuf:"varint,1,opt,name=index,proto3" json:"index,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` +func (x *TaskDeploymentInfo) GetAgentId() string { + if x != nil { + return x.AgentId + } + return "" } -func (m *RemoveRepoRequest) Reset() { *m = RemoveRepoRequest{} } -func (m *RemoveRepoRequest) String() string { return proto.CompactTextString(m) } -func (*RemoveRepoRequest) ProtoMessage() {} -func (*RemoveRepoRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{46} -} -func (m *RemoveRepoRequest) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *RemoveRepoRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_RemoveRepoRequest.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err - } - return b[:n], nil +func (x *TaskDeploymentInfo) GetOfferId() string { + if x != nil { + return x.OfferId } + return "" } -func (m *RemoveRepoRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_RemoveRepoRequest.Merge(m, src) -} -func (m *RemoveRepoRequest) XXX_Size() int { - return m.Size() -} -func (m *RemoveRepoRequest) XXX_DiscardUnknown() { - xxx_messageInfo_RemoveRepoRequest.DiscardUnknown(m) + +func (x *TaskDeploymentInfo) GetExecutorId() string { + if x != nil { + return x.ExecutorId + } + return "" } -var xxx_messageInfo_RemoveRepoRequest proto.InternalMessageInfo +type GetTasksRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} -func (m *RemoveRepoRequest) GetIndex() int32 { - if m != nil { - return m.Index +func (x *GetTasksRequest) Reset() { + *x = GetTasksRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[30] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return 0 } -type RemoveRepoReply struct { - Ok bool `protobuf:"varint,1,opt,name=ok,proto3" json:"ok,omitempty"` - NewDefaultRepo string `protobuf:"bytes,2,opt,name=newDefaultRepo,proto3" json:"newDefaultRepo,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` +func (x *GetTasksRequest) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *RemoveRepoReply) Reset() { *m = RemoveRepoReply{} } -func (m *RemoveRepoReply) String() string { return proto.CompactTextString(m) } -func (*RemoveRepoReply) ProtoMessage() {} -func (*RemoveRepoReply) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{47} -} -func (m *RemoveRepoReply) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *RemoveRepoReply) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_RemoveRepoReply.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err +func (*GetTasksRequest) ProtoMessage() {} + +func (x *GetTasksRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[30] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) } - return b[:n], nil + return ms } + return mi.MessageOf(x) } -func (m *RemoveRepoReply) XXX_Merge(src proto.Message) { - xxx_messageInfo_RemoveRepoReply.Merge(m, src) -} -func (m *RemoveRepoReply) XXX_Size() int { - return m.Size() -} -func (m *RemoveRepoReply) XXX_DiscardUnknown() { - xxx_messageInfo_RemoveRepoReply.DiscardUnknown(m) + +// Deprecated: Use GetTasksRequest.ProtoReflect.Descriptor instead. +func (*GetTasksRequest) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{30} } -var xxx_messageInfo_RemoveRepoReply proto.InternalMessageInfo +type GetTasksReply struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields -func (m *RemoveRepoReply) GetOk() bool { - if m != nil { - return m.Ok - } - return false + Tasks []*ShortTaskInfo `protobuf:"bytes,1,rep,name=tasks,proto3" json:"tasks,omitempty"` + Timestamp int64 `protobuf:"varint,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"` // timestamp of when this object was sent in unix milliseconds } -func (m *RemoveRepoReply) GetNewDefaultRepo() string { - if m != nil { - return m.NewDefaultRepo +func (x *GetTasksReply) Reset() { + *x = GetTasksReply{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[31] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return "" } -type RefreshReposRequest struct { - Index int32 `protobuf:"varint,1,opt,name=index,proto3" json:"index,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` +func (x *GetTasksReply) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *RefreshReposRequest) Reset() { *m = RefreshReposRequest{} } -func (m *RefreshReposRequest) String() string { return proto.CompactTextString(m) } -func (*RefreshReposRequest) ProtoMessage() {} -func (*RefreshReposRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{48} -} -func (m *RefreshReposRequest) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *RefreshReposRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_RefreshReposRequest.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err +func (*GetTasksReply) ProtoMessage() {} + +func (x *GetTasksReply) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[31] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) } - return b[:n], nil + return ms } + return mi.MessageOf(x) } -func (m *RefreshReposRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_RefreshReposRequest.Merge(m, src) -} -func (m *RefreshReposRequest) XXX_Size() int { - return m.Size() -} -func (m *RefreshReposRequest) XXX_DiscardUnknown() { - xxx_messageInfo_RefreshReposRequest.DiscardUnknown(m) + +// Deprecated: Use GetTasksReply.ProtoReflect.Descriptor instead. +func (*GetTasksReply) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{31} } -var xxx_messageInfo_RefreshReposRequest proto.InternalMessageInfo +func (x *GetTasksReply) GetTasks() []*ShortTaskInfo { + if x != nil { + return x.Tasks + } + return nil +} -func (m *RefreshReposRequest) GetIndex() int32 { - if m != nil { - return m.Index +func (x *GetTasksReply) GetTimestamp() int64 { + if x != nil { + return x.Timestamp } return 0 } -type RefreshReposReply struct { - ErrorString string `protobuf:"bytes,1,opt,name=errorString,proto3" json:"errorString,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` +type GetTaskRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + TaskId string `protobuf:"bytes,1,opt,name=taskId,proto3" json:"taskId,omitempty"` } -func (m *RefreshReposReply) Reset() { *m = RefreshReposReply{} } -func (m *RefreshReposReply) String() string { return proto.CompactTextString(m) } -func (*RefreshReposReply) ProtoMessage() {} -func (*RefreshReposReply) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{49} +func (x *GetTaskRequest) Reset() { + *x = GetTaskRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[32] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *RefreshReposReply) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) + +func (x *GetTaskRequest) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *RefreshReposReply) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_RefreshReposReply.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err + +func (*GetTaskRequest) ProtoMessage() {} + +func (x *GetTaskRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[32] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) } - return b[:n], nil + return ms } -} -func (m *RefreshReposReply) XXX_Merge(src proto.Message) { - xxx_messageInfo_RefreshReposReply.Merge(m, src) -} -func (m *RefreshReposReply) XXX_Size() int { - return m.Size() -} -func (m *RefreshReposReply) XXX_DiscardUnknown() { - xxx_messageInfo_RefreshReposReply.DiscardUnknown(m) + return mi.MessageOf(x) } -var xxx_messageInfo_RefreshReposReply proto.InternalMessageInfo +// Deprecated: Use GetTaskRequest.ProtoReflect.Descriptor instead. +func (*GetTaskRequest) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{32} +} -func (m *RefreshReposReply) GetErrorString() string { - if m != nil { - return m.ErrorString +func (x *GetTaskRequest) GetTaskId() string { + if x != nil { + return x.TaskId } return "" } -type SetDefaultRepoRequest struct { - Index int32 `protobuf:"varint,1,opt,name=index,proto3" json:"index,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` +type GetTaskReply struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Task *TaskInfo `protobuf:"bytes,1,opt,name=task,proto3" json:"task,omitempty"` + Timestamp int64 `protobuf:"varint,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"` // timestamp of when this object was sent in unix milliseconds } -func (m *SetDefaultRepoRequest) Reset() { *m = SetDefaultRepoRequest{} } -func (m *SetDefaultRepoRequest) String() string { return proto.CompactTextString(m) } -func (*SetDefaultRepoRequest) ProtoMessage() {} -func (*SetDefaultRepoRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{50} -} -func (m *SetDefaultRepoRequest) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *SetDefaultRepoRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_SetDefaultRepoRequest.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err - } - return b[:n], nil +func (x *GetTaskReply) Reset() { + *x = GetTaskReply{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[33] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } } -func (m *SetDefaultRepoRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_SetDefaultRepoRequest.Merge(m, src) -} -func (m *SetDefaultRepoRequest) XXX_Size() int { - return m.Size() -} -func (m *SetDefaultRepoRequest) XXX_DiscardUnknown() { - xxx_messageInfo_SetDefaultRepoRequest.DiscardUnknown(m) + +func (x *GetTaskReply) String() string { + return protoimpl.X.MessageStringOf(x) } -var xxx_messageInfo_SetDefaultRepoRequest proto.InternalMessageInfo +func (*GetTaskReply) ProtoMessage() {} -func (m *SetDefaultRepoRequest) GetIndex() int32 { - if m != nil { - return m.Index +func (x *GetTaskReply) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[33] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms } - return 0 + return mi.MessageOf(x) } -type SetDefaultRepoReply struct { - ErrorString string `protobuf:"bytes,1,opt,name=errorString,proto3" json:"errorString,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` +// Deprecated: Use GetTaskReply.ProtoReflect.Descriptor instead. +func (*GetTaskReply) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{33} } -func (m *SetDefaultRepoReply) Reset() { *m = SetDefaultRepoReply{} } -func (m *SetDefaultRepoReply) String() string { return proto.CompactTextString(m) } -func (*SetDefaultRepoReply) ProtoMessage() {} -func (*SetDefaultRepoReply) Descriptor() ([]byte, []int) { - return fileDescriptor_2aa6aa9a1f02efa9, []int{51} -} -func (m *SetDefaultRepoReply) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) +func (x *GetTaskReply) GetTask() *TaskInfo { + if x != nil { + return x.Task + } + return nil } -func (m *SetDefaultRepoReply) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_SetDefaultRepoReply.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err - } - return b[:n], nil + +func (x *GetTaskReply) GetTimestamp() int64 { + if x != nil { + return x.Timestamp } + return 0 } -func (m *SetDefaultRepoReply) XXX_Merge(src proto.Message) { - xxx_messageInfo_SetDefaultRepoReply.Merge(m, src) + +type CommandInfo struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Env []string `protobuf:"bytes,1,rep,name=env,proto3" json:"env,omitempty"` + Shell bool `protobuf:"varint,2,opt,name=shell,proto3" json:"shell,omitempty"` + Value string `protobuf:"bytes,3,opt,name=value,proto3" json:"value,omitempty"` + Arguments []string `protobuf:"bytes,4,rep,name=arguments,proto3" json:"arguments,omitempty"` + User string `protobuf:"bytes,5,opt,name=user,proto3" json:"user,omitempty"` } -func (m *SetDefaultRepoReply) XXX_Size() int { - return m.Size() + +func (x *CommandInfo) Reset() { + *x = CommandInfo{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[34] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *SetDefaultRepoReply) XXX_DiscardUnknown() { - xxx_messageInfo_SetDefaultRepoReply.DiscardUnknown(m) + +func (x *CommandInfo) String() string { + return protoimpl.X.MessageStringOf(x) } -var xxx_messageInfo_SetDefaultRepoReply proto.InternalMessageInfo +func (*CommandInfo) ProtoMessage() {} -func (m *SetDefaultRepoReply) GetErrorString() string { - if m != nil { - return m.ErrorString +func (x *CommandInfo) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[34] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms } - return "" + return mi.MessageOf(x) } -func init() { - proto.RegisterEnum("o2control.StatusUpdate_Level", StatusUpdate_Level_name, StatusUpdate_Level_value) - proto.RegisterEnum("o2control.ControlEnvironmentRequest_Optype", ControlEnvironmentRequest_Optype_name, ControlEnvironmentRequest_Optype_value) - proto.RegisterEnum("o2control.EnvironmentOperation_Optype", EnvironmentOperation_Optype_name, EnvironmentOperation_Optype_value) - proto.RegisterType((*Event_MesosHeartbeat)(nil), "o2control.Event_MesosHeartbeat") - proto.RegisterType((*StatusRequest)(nil), "o2control.StatusRequest") - proto.RegisterType((*StatusReply)(nil), "o2control.StatusReply") - proto.RegisterType((*StatusUpdate)(nil), "o2control.StatusUpdate") - proto.RegisterType((*GetFrameworkInfoRequest)(nil), "o2control.GetFrameworkInfoRequest") - proto.RegisterType((*Version)(nil), "o2control.Version") - proto.RegisterType((*GetFrameworkInfoReply)(nil), "o2control.GetFrameworkInfoReply") - proto.RegisterType((*TeardownRequest)(nil), "o2control.TeardownRequest") - proto.RegisterType((*TeardownReply)(nil), "o2control.TeardownReply") - proto.RegisterType((*GetEnvironmentsRequest)(nil), "o2control.GetEnvironmentsRequest") - proto.RegisterType((*GetEnvironmentsReply)(nil), "o2control.GetEnvironmentsReply") - proto.RegisterType((*EnvironmentInfo)(nil), "o2control.EnvironmentInfo") - proto.RegisterType((*NewEnvironmentRequest)(nil), "o2control.NewEnvironmentRequest") - proto.RegisterType((*NewEnvironmentReply)(nil), "o2control.NewEnvironmentReply") - proto.RegisterType((*GetEnvironmentRequest)(nil), "o2control.GetEnvironmentRequest") - proto.RegisterType((*GetEnvironmentReply)(nil), "o2control.GetEnvironmentReply") - proto.RegisterType((*ControlEnvironmentRequest)(nil), "o2control.ControlEnvironmentRequest") - proto.RegisterType((*ControlEnvironmentReply)(nil), "o2control.ControlEnvironmentReply") - proto.RegisterType((*ModifyEnvironmentRequest)(nil), "o2control.ModifyEnvironmentRequest") - proto.RegisterType((*EnvironmentOperation)(nil), "o2control.EnvironmentOperation") - proto.RegisterType((*ModifyEnvironmentReply)(nil), "o2control.ModifyEnvironmentReply") - proto.RegisterType((*DestroyEnvironmentRequest)(nil), "o2control.DestroyEnvironmentRequest") - proto.RegisterType((*DestroyEnvironmentReply)(nil), "o2control.DestroyEnvironmentReply") - proto.RegisterType((*ShortTaskInfo)(nil), "o2control.ShortTaskInfo") - proto.RegisterType((*TaskDeploymentInfo)(nil), "o2control.TaskDeploymentInfo") - proto.RegisterType((*GetTasksRequest)(nil), "o2control.GetTasksRequest") - proto.RegisterType((*GetTasksReply)(nil), "o2control.GetTasksReply") - proto.RegisterType((*GetTaskRequest)(nil), "o2control.GetTaskRequest") - proto.RegisterType((*GetTaskReply)(nil), "o2control.GetTaskReply") - proto.RegisterType((*TaskClassInfo)(nil), "o2control.TaskClassInfo") - proto.RegisterType((*CommandInfo)(nil), "o2control.CommandInfo") - proto.RegisterType((*ChannelInfo)(nil), "o2control.ChannelInfo") - proto.RegisterType((*TaskInfo)(nil), "o2control.TaskInfo") - proto.RegisterType((*CleanupTasksRequest)(nil), "o2control.CleanupTasksRequest") - proto.RegisterType((*CleanupTasksReply)(nil), "o2control.CleanupTasksReply") - proto.RegisterType((*GetRolesRequest)(nil), "o2control.GetRolesRequest") - proto.RegisterType((*RoleInfo)(nil), "o2control.RoleInfo") - proto.RegisterType((*GetRolesReply)(nil), "o2control.GetRolesReply") - proto.RegisterType((*GetWorkflowTemplatesRequest)(nil), "o2control.GetWorkflowTemplatesRequest") - proto.RegisterType((*WorkflowTemplateInfo)(nil), "o2control.WorkflowTemplateInfo") - proto.RegisterType((*GetWorkflowTemplatesReply)(nil), "o2control.GetWorkflowTemplatesReply") - proto.RegisterType((*ListReposRequest)(nil), "o2control.ListReposRequest") - proto.RegisterType((*RepoInfo)(nil), "o2control.RepoInfo") - proto.RegisterType((*ListReposReply)(nil), "o2control.ListReposReply") - proto.RegisterType((*AddRepoRequest)(nil), "o2control.AddRepoRequest") - proto.RegisterType((*AddRepoReply)(nil), "o2control.AddRepoReply") - proto.RegisterType((*RemoveRepoRequest)(nil), "o2control.RemoveRepoRequest") - proto.RegisterType((*RemoveRepoReply)(nil), "o2control.RemoveRepoReply") - proto.RegisterType((*RefreshReposRequest)(nil), "o2control.RefreshReposRequest") - proto.RegisterType((*RefreshReposReply)(nil), "o2control.RefreshReposReply") - proto.RegisterType((*SetDefaultRepoRequest)(nil), "o2control.SetDefaultRepoRequest") - proto.RegisterType((*SetDefaultRepoReply)(nil), "o2control.SetDefaultRepoReply") -} - -func init() { proto.RegisterFile("protos/o2control.proto", fileDescriptor_2aa6aa9a1f02efa9) } - -var fileDescriptor_2aa6aa9a1f02efa9 = []byte{ - // 2159 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x59, 0x4f, 0x73, 0x1b, 0x49, - 0x15, 0xd7, 0xc8, 0x92, 0x25, 0x3d, 0xc9, 0xb2, 0xdc, 0x76, 0xec, 0xb1, 0xd6, 0xeb, 0x78, 0x9b, - 0x54, 0x36, 0xd9, 0x5d, 0x9c, 0xc5, 0x0b, 0x6c, 0x2a, 0x2c, 0x04, 0x5b, 0x56, 0x1c, 0x41, 0x6c, - 0xa5, 0x46, 0x4a, 0x52, 0x6c, 0x15, 0x15, 0xc6, 0x9a, 0x96, 0xa5, 0xf5, 0x68, 0x5a, 0xcc, 0x1f, - 0x3b, 0x3e, 0x70, 0xe3, 0x46, 0x51, 0x7b, 0xe0, 0xc2, 0x95, 0xe2, 0xcc, 0xf7, 0xe0, 0x42, 0x01, - 0xdf, 0x80, 0x0a, 0x55, 0x9c, 0xf8, 0x10, 0x54, 0xff, 0x99, 0x99, 0x9e, 0xd1, 0xc8, 0x76, 0x15, - 0x37, 0xbd, 0xd7, 0xbf, 0xf7, 0xba, 0xdf, 0x9f, 0x7e, 0xef, 0xf5, 0x08, 0xd6, 0xa7, 0x2e, 0xf5, - 0xa9, 0xf7, 0x88, 0xee, 0x0d, 0xa8, 0xe3, 0xbb, 0xd4, 0xde, 0xe5, 0x0c, 0x54, 0x89, 0x18, 0x78, - 0x1d, 0xd6, 0xda, 0x17, 0xc4, 0xf1, 0xdf, 0x1e, 0x13, 0x8f, 0x7a, 0xcf, 0x89, 0xe9, 0xfa, 0xa7, - 0xc4, 0xf4, 0xf1, 0x32, 0x2c, 0xf5, 0x7c, 0xd3, 0x0f, 0x3c, 0x83, 0xfc, 0x3a, 0x20, 0x9e, 0x8f, - 0x4f, 0xa1, 0x1a, 0x32, 0xa6, 0xf6, 0x15, 0x5a, 0x83, 0xa2, 0xe7, 0x9b, 0x3e, 0xd1, 0xb5, 0x1d, - 0xed, 0x41, 0xc5, 0x10, 0x04, 0xfa, 0x31, 0x2c, 0x79, 0x1c, 0xf4, 0x6a, 0x6a, 0x99, 0x3e, 0xf1, - 0xf4, 0xfc, 0xce, 0xc2, 0x83, 0xea, 0xde, 0xc6, 0x6e, 0x7c, 0x82, 0x9e, 0xb2, 0x6e, 0x24, 0xd1, - 0xf8, 0x6f, 0x1a, 0xd4, 0xd4, 0x75, 0xf4, 0x05, 0x14, 0x6d, 0x72, 0x41, 0x6c, 0xbe, 0x4b, 0x7d, - 0xef, 0xc3, 0x39, 0x7a, 0x76, 0x5f, 0x30, 0x90, 0x21, 0xb0, 0xa8, 0x03, 0xf5, 0x49, 0xc2, 0x18, - 0x3d, 0xbf, 0xa3, 0x3d, 0xa8, 0xee, 0xdd, 0x55, 0xa4, 0xb3, 0x6c, 0x7e, 0x9e, 0x33, 0x52, 0x82, - 0xf8, 0xfb, 0x50, 0xe4, 0xaa, 0x51, 0x05, 0x8a, 0x87, 0xed, 0x83, 0x57, 0x47, 0x8d, 0x1c, 0x2a, - 0x43, 0xa1, 0x73, 0xf2, 0xac, 0xdb, 0xd0, 0x50, 0x15, 0x4a, 0x6f, 0xf6, 0x8d, 0x93, 0xce, 0xc9, - 0x51, 0x23, 0xcf, 0x10, 0x6d, 0xc3, 0xe8, 0x1a, 0x8d, 0x85, 0x83, 0x12, 0x14, 0xb9, 0x7e, 0xbc, - 0x09, 0x1b, 0x47, 0xc4, 0x7f, 0xe6, 0x9a, 0x13, 0x72, 0x49, 0xdd, 0xf3, 0x8e, 0x33, 0xa4, 0xa1, - 0x3b, 0xff, 0xac, 0x41, 0xe9, 0x35, 0x71, 0xbd, 0x31, 0x75, 0x98, 0x2f, 0x27, 0xe6, 0x37, 0xd4, - 0xe5, 0x56, 0x16, 0x0d, 0x41, 0x70, 0xee, 0xd8, 0xa1, 0x2e, 0x3f, 0x3d, 0xe3, 0x32, 0x82, 0x71, - 0xa7, 0xa6, 0x3f, 0x18, 0xe9, 0x0b, 0x82, 0xcb, 0x09, 0xc6, 0x3d, 0x0d, 0xc6, 0xb6, 0xa5, 0x17, - 0x44, 0x34, 0x38, 0x81, 0x76, 0xa0, 0x3a, 0x75, 0xa9, 0x15, 0x0c, 0xfc, 0x13, 0x73, 0x42, 0xf4, - 0x22, 0x5f, 0x53, 0x59, 0x68, 0x1b, 0xe0, 0x42, 0x1c, 0xa2, 0xe7, 0xbb, 0xfa, 0x22, 0x07, 0x28, - 0x1c, 0xfc, 0x6d, 0x1e, 0xee, 0xcc, 0x5a, 0xc0, 0xe2, 0xbf, 0x03, 0xd5, 0x61, 0xc4, 0xb5, 0x64, - 0x16, 0xa8, 0x2c, 0xf4, 0x19, 0xac, 0x10, 0xe7, 0x62, 0xec, 0x52, 0x67, 0x42, 0x1c, 0xdf, 0x6b, - 0xd1, 0xc0, 0xf1, 0xa5, 0x2d, 0xb3, 0x0b, 0xec, 0x24, 0xbe, 0xe9, 0x9d, 0x4b, 0x98, 0x30, 0x4e, - 0xe1, 0xc4, 0xf9, 0x56, 0x50, 0xf3, 0x6d, 0x1b, 0x60, 0x44, 0xbd, 0x50, 0x79, 0x51, 0x48, 0xc5, - 0x1c, 0x84, 0xa1, 0x36, 0x76, 0x3c, 0xdf, 0x74, 0x06, 0x84, 0xbb, 0x40, 0x58, 0x98, 0xe0, 0xa1, - 0xcf, 0xa0, 0x24, 0x2d, 0xd6, 0x4b, 0x3c, 0x4f, 0x90, 0x92, 0x27, 0x32, 0x44, 0x46, 0x08, 0xc1, - 0x0f, 0x61, 0xb9, 0x4f, 0x4c, 0xd7, 0xa2, 0x97, 0x8e, 0x0c, 0x25, 0x5a, 0x87, 0x45, 0x97, 0x98, - 0x1e, 0x75, 0xa4, 0x17, 0x24, 0xc5, 0xae, 0x50, 0x0c, 0x9d, 0xda, 0x57, 0x58, 0x87, 0xf5, 0x23, - 0xe2, 0xb7, 0x15, 0xdb, 0xc3, 0x6c, 0x78, 0x07, 0x6b, 0x33, 0x2b, 0xb7, 0xf3, 0xf2, 0x4f, 0xa0, - 0xa6, 0x3a, 0x53, 0x5e, 0xb8, 0xa6, 0x9a, 0xea, 0xf1, 0x32, 0x0f, 0x5f, 0x02, 0x8f, 0xff, 0xa9, - 0xc1, 0x72, 0x0a, 0x81, 0xea, 0x90, 0x1f, 0x87, 0x9b, 0xe5, 0xc7, 0x3c, 0x8f, 0x06, 0x2e, 0x31, - 0x7d, 0x62, 0xbd, 0x19, 0x11, 0x87, 0xc7, 0xb0, 0x62, 0xa8, 0xac, 0x38, 0x3a, 0x0b, 0x6a, 0x74, - 0x76, 0xa1, 0xc8, 0x23, 0xa8, 0x17, 0xf8, 0xa1, 0x74, 0xf5, 0xf6, 0x8e, 0xa8, 0xeb, 0xf7, 0x4d, - 0x4f, 0x64, 0x94, 0x80, 0xa1, 0x26, 0x94, 0x5d, 0x4a, 0x7d, 0x83, 0xda, 0x61, 0xb2, 0x46, 0x34, - 0xfa, 0x04, 0x1a, 0x83, 0xc0, 0x75, 0x89, 0xe3, 0x1b, 0x81, 0x73, 0x12, 0x4c, 0x4e, 0x89, 0xc8, - 0xd7, 0x25, 0x63, 0x86, 0x8f, 0x5b, 0x70, 0xe7, 0x84, 0x5c, 0x2a, 0x56, 0x85, 0x91, 0xfa, 0x04, - 0x1a, 0xcc, 0x6d, 0x43, 0x9b, 0x5e, 0xf6, 0xc9, 0x64, 0x6a, 0xc7, 0xf5, 0x6b, 0x86, 0x8f, 0x7b, - 0xb0, 0x9a, 0x56, 0xc2, 0x22, 0xf2, 0x15, 0x54, 0x15, 0xff, 0x71, 0xe9, 0xeb, 0xdd, 0xad, 0xc2, - 0xf1, 0xc7, 0xfc, 0x3a, 0x65, 0x9c, 0x2c, 0xe5, 0x72, 0xfc, 0x5b, 0x0d, 0x56, 0xd3, 0xc8, 0xff, - 0x7b, 0x7b, 0xf4, 0x08, 0xca, 0xa1, 0x9d, 0xb2, 0x26, 0xae, 0x2a, 0xa2, 0xcc, 0xcf, 0x5c, 0x26, - 0x02, 0xe1, 0xbf, 0x6b, 0xb0, 0xd9, 0x12, 0xcb, 0x37, 0x1f, 0x1a, 0x3d, 0x85, 0x82, 0x7f, 0x35, - 0x25, 0x5c, 0x75, 0x7d, 0xef, 0x53, 0x45, 0xf5, 0x5c, 0x1d, 0xbb, 0xdd, 0x29, 0x13, 0x31, 0xb8, - 0x20, 0x36, 0x61, 0x51, 0xd0, 0xac, 0xc8, 0x9e, 0x74, 0xbb, 0x2f, 0x1b, 0x39, 0x84, 0xa0, 0xde, - 0xeb, 0xef, 0x1b, 0xfd, 0xb7, 0xfb, 0xad, 0x7e, 0xe7, 0x75, 0xa7, 0xff, 0x8b, 0x86, 0x86, 0x56, - 0x60, 0xa9, 0xd7, 0xef, 0xbe, 0x8c, 0x59, 0x79, 0xb4, 0x04, 0x95, 0x56, 0xf7, 0xe4, 0x59, 0xe7, - 0xe8, 0x95, 0xd1, 0x6e, 0x2c, 0xb0, 0x6a, 0x6c, 0xb4, 0x7b, 0xed, 0x7e, 0xa3, 0x80, 0x6a, 0x50, - 0x3e, 0xea, 0xbe, 0x15, 0xb5, 0xb9, 0x88, 0xcf, 0x61, 0x23, 0xeb, 0x30, 0xcc, 0xb7, 0x69, 0x73, - 0xa2, 0xa4, 0xce, 0xab, 0x49, 0x9d, 0x95, 0x88, 0x0b, 0x73, 0x12, 0xf1, 0x0f, 0x1a, 0xe8, 0xc7, - 0xd4, 0x1a, 0x0f, 0xaf, 0x6e, 0xe5, 0x3d, 0xa0, 0x53, 0xe2, 0x9a, 0xfe, 0x98, 0x3a, 0xe1, 0x3d, - 0xbe, 0x9b, 0x1d, 0xd9, 0x6e, 0x88, 0x33, 0x14, 0x11, 0x74, 0x1f, 0xea, 0x2e, 0x19, 0x50, 0x67, - 0x38, 0x3e, 0x0b, 0x5c, 0xb2, 0x6f, 0xdb, 0xfc, 0x5c, 0x65, 0x23, 0xc5, 0xc5, 0x7f, 0xd2, 0x60, - 0x2d, 0x4b, 0x19, 0x7a, 0x22, 0xe3, 0x27, 0x9a, 0xed, 0xfd, 0x1b, 0xf6, 0x4e, 0x84, 0x4e, 0xdc, - 0x5d, 0x5b, 0x54, 0xd9, 0x7c, 0x78, 0x77, 0x05, 0x8d, 0xbf, 0x97, 0x11, 0xd6, 0x65, 0xa8, 0x1a, - 0xed, 0xe3, 0xee, 0xeb, 0xf6, 0x5b, 0xa3, 0xfb, 0x82, 0x45, 0xac, 0x06, 0xe5, 0xfd, 0xc3, 0x43, - 0x41, 0x15, 0xf0, 0xef, 0x34, 0x58, 0xcf, 0xf0, 0x1c, 0x0b, 0xd3, 0xcf, 0xa1, 0x31, 0x34, 0xc7, - 0x36, 0xb1, 0xba, 0xb1, 0xb7, 0xb4, 0xdb, 0x79, 0x6b, 0x46, 0x50, 0x06, 0x21, 0x3f, 0x1b, 0x73, - 0xb5, 0x90, 0xe1, 0x0e, 0x6c, 0x1e, 0x12, 0xcf, 0x77, 0xe9, 0x6d, 0xe2, 0xb8, 0x05, 0x95, 0x73, - 0x42, 0xa6, 0x7d, 0x5e, 0xf9, 0xf2, 0x3c, 0x02, 0x31, 0x03, 0x13, 0xd8, 0xc8, 0x52, 0xc5, 0x0c, - 0xfb, 0x19, 0xac, 0x0c, 0x6c, 0x62, 0x3a, 0x81, 0x80, 0x72, 0xa6, 0xbc, 0xe1, 0x5b, 0xea, 0x5d, - 0x4a, 0x63, 0x8c, 0x59, 0x31, 0xfc, 0x1f, 0x0d, 0x96, 0x12, 0x35, 0x16, 0x21, 0x28, 0x38, 0x2c, - 0x38, 0xe2, 0xa0, 0xfc, 0x37, 0xeb, 0x5c, 0x36, 0x1d, 0x9c, 0x13, 0x4b, 0x9e, 0x53, 0x52, 0x8c, - 0xcf, 0x2a, 0x72, 0xc7, 0x92, 0x6e, 0x90, 0x14, 0xe3, 0x8b, 0x81, 0x4d, 0x76, 0x61, 0x49, 0xc5, - 0x5e, 0x2b, 0xaa, 0x37, 0x65, 0x0b, 0x2a, 0x03, 0xdb, 0xf4, 0x3c, 0xa5, 0xf3, 0xc6, 0x0c, 0xd4, - 0x86, 0xba, 0x45, 0xa6, 0x36, 0xbd, 0x0a, 0x4b, 0x95, 0xec, 0xbe, 0xea, 0x8c, 0xc7, 0x0e, 0x7f, - 0x98, 0x00, 0x19, 0x29, 0x21, 0x56, 0x28, 0xd1, 0x2c, 0x8c, 0xa5, 0x23, 0x1b, 0x03, 0x14, 0x8b, - 0x23, 0x1a, 0xe9, 0x50, 0x32, 0xcf, 0x18, 0x30, 0x0c, 0x7c, 0x48, 0xb2, 0x15, 0x3a, 0x1c, 0x12, - 0x37, 0x32, 0x3c, 0x24, 0xd9, 0xa0, 0x41, 0xde, 0x91, 0x41, 0xe0, 0x53, 0xb6, 0x28, 0xac, 0x57, - 0x38, 0x78, 0x05, 0x96, 0x8f, 0x88, 0x2f, 0x03, 0x20, 0x7a, 0xfa, 0x53, 0x58, 0x8a, 0x59, 0x2c, - 0xbe, 0x51, 0x3b, 0xd4, 0x6e, 0xd5, 0x0e, 0xf1, 0x03, 0xa8, 0x4b, 0x05, 0xca, 0xa4, 0x21, 0xe3, - 0xa2, 0xa9, 0x71, 0xc1, 0x5f, 0x42, 0x2d, 0x42, 0xb2, 0x9d, 0x3e, 0x86, 0x02, 0x5b, 0x91, 0xc9, - 0xb3, 0x9a, 0xf2, 0x28, 0xdf, 0x83, 0x03, 0x70, 0x1b, 0x96, 0x18, 0xa7, 0xc5, 0xa2, 0x32, 0x37, - 0x4b, 0x58, 0xfb, 0x17, 0xe2, 0xc7, 0xd4, 0x22, 0x51, 0xfb, 0x8f, 0x59, 0xf8, 0x37, 0x50, 0x6d, - 0xd1, 0xc9, 0xc4, 0x74, 0x2c, 0xae, 0xa4, 0x01, 0x0b, 0xc4, 0xb9, 0xe0, 0x66, 0x56, 0x0c, 0xf6, - 0x93, 0x27, 0xc8, 0x88, 0xd8, 0xb6, 0xcc, 0x33, 0x41, 0x30, 0xee, 0x85, 0x69, 0x07, 0xd1, 0x65, - 0xe3, 0x04, 0x4b, 0x1b, 0xd3, 0x3d, 0x0b, 0xc4, 0x38, 0x53, 0xe0, 0x3a, 0x62, 0x06, 0x3b, 0x60, - 0xe0, 0x11, 0x57, 0x66, 0x1a, 0xff, 0x8d, 0x8f, 0xa1, 0xda, 0x1a, 0x99, 0x8e, 0x43, 0xec, 0xb9, - 0x36, 0x20, 0xa5, 0x35, 0x55, 0x64, 0xc9, 0xe2, 0xde, 0x74, 0xcf, 0x88, 0x1f, 0x67, 0x39, 0xa3, - 0xf0, 0x7f, 0xf3, 0x50, 0x8e, 0xae, 0xcd, 0x0f, 0xa1, 0xe2, 0xb1, 0xe0, 0xf0, 0x0c, 0x15, 0xfe, - 0x9c, 0x1f, 0xb8, 0x18, 0xca, 0xe4, 0x06, 0xa1, 0x57, 0x65, 0xaf, 0xd5, 0x53, 0x71, 0x88, 0xbc, - 0x6e, 0xc4, 0x50, 0xf4, 0x53, 0x58, 0x1e, 0x3b, 0xa7, 0x34, 0x70, 0x2c, 0x69, 0x92, 0xa7, 0x2f, - 0xf0, 0x74, 0x59, 0x57, 0x4b, 0x40, 0x6c, 0xad, 0x91, 0x86, 0xa3, 0x03, 0x68, 0xd0, 0xc0, 0x4f, - 0xaa, 0x28, 0x5c, 0xab, 0x62, 0x06, 0x8f, 0x1e, 0xb3, 0x90, 0x47, 0x01, 0xe5, 0xce, 0x4e, 0x89, - 0xc7, 0xab, 0x86, 0x0a, 0x65, 0x17, 0x8f, 0x65, 0xd6, 0x4b, 0xd3, 0x1f, 0xc9, 0x3b, 0x1f, 0xd1, - 0x2c, 0xde, 0xc4, 0xb9, 0xe8, 0x58, 0xfc, 0xa6, 0x57, 0x0c, 0x41, 0xe0, 0x47, 0xb0, 0x9a, 0x2c, - 0x69, 0x22, 0xd7, 0x75, 0x28, 0x89, 0xec, 0xf6, 0x64, 0x22, 0x85, 0x24, 0xfe, 0xbd, 0x06, 0x2b, - 0x33, 0x45, 0x10, 0x3d, 0x81, 0xea, 0xf9, 0xd8, 0xb6, 0x89, 0xd5, 0xbf, 0xd5, 0x1d, 0x53, 0xc1, - 0xe8, 0x2b, 0xa8, 0xb9, 0x81, 0xe3, 0x8c, 0x9d, 0xb3, 0xb0, 0x6a, 0x5f, 0x2f, 0x9c, 0x40, 0xe3, - 0x16, 0xbf, 0xfb, 0x6c, 0x7a, 0x8a, 0x0e, 0x1f, 0x59, 0xaa, 0x29, 0x96, 0x32, 0xdf, 0x4c, 0x4d, - 0x7f, 0xd4, 0x9b, 0x92, 0x41, 0xd8, 0x23, 0x43, 0x1a, 0xff, 0x45, 0x83, 0x72, 0x38, 0x80, 0xcd, - 0xab, 0xd5, 0xb2, 0xf6, 0xe6, 0xb3, 0x6b, 0x6f, 0x62, 0xf4, 0x6e, 0x42, 0x79, 0x18, 0xd8, 0x36, - 0x0f, 0x83, 0xa8, 0x56, 0x11, 0xad, 0x7a, 0xb6, 0x98, 0xf0, 0x2c, 0x7a, 0x08, 0x45, 0xd6, 0xb4, - 0x3d, 0x7d, 0x91, 0x3b, 0x20, 0x73, 0x38, 0x14, 0x08, 0xfc, 0x84, 0x57, 0x37, 0x69, 0x34, 0xf3, - 0x7f, 0x24, 0xab, 0xdd, 0x28, 0xfb, 0x21, 0x7c, 0x70, 0x44, 0xfc, 0x37, 0xa9, 0x89, 0x3b, 0x2a, - 0x9c, 0xcf, 0x60, 0x2d, 0xbd, 0x16, 0x7a, 0xc5, 0x25, 0x53, 0x1a, 0x7a, 0x85, 0xfd, 0xe6, 0xe9, - 0x16, 0x4e, 0xf2, 0xd2, 0xa5, 0x21, 0x8d, 0xbf, 0x81, 0xcd, 0xec, 0x6d, 0xd8, 0x71, 0x8f, 0x61, - 0x25, 0x3d, 0xf2, 0x67, 0x8d, 0x11, 0x59, 0x07, 0x31, 0x66, 0x25, 0x31, 0x82, 0xc6, 0x8b, 0xb1, - 0xc7, 0x1a, 0x39, 0x8d, 0xec, 0x78, 0x0c, 0x65, 0x46, 0xcf, 0x8d, 0xa8, 0x0e, 0x25, 0x8b, 0x0c, - 0xcd, 0xc0, 0xf6, 0x65, 0x59, 0x0c, 0x49, 0xfc, 0x23, 0xa8, 0x2b, 0xda, 0x42, 0xef, 0x32, 0x2a, - 0xcb, 0xbb, 0x72, 0x0f, 0x43, 0x20, 0xf0, 0x3d, 0xa8, 0xef, 0x5b, 0x16, 0xe3, 0x86, 0xd9, 0x98, - 0xb1, 0x39, 0xfe, 0x1c, 0x6a, 0x11, 0x4a, 0xbe, 0x34, 0x89, 0xeb, 0x52, 0xb7, 0xe7, 0xbb, 0x63, - 0xe7, 0x2c, 0x7c, 0x69, 0x2a, 0x2c, 0xfc, 0x10, 0x56, 0x0c, 0x32, 0xa1, 0x17, 0x44, 0x55, 0xbd, - 0x06, 0xc5, 0xb1, 0x63, 0x91, 0x77, 0xe1, 0xa7, 0x0b, 0x4e, 0xe0, 0x0e, 0x2c, 0xab, 0x50, 0x39, - 0x5c, 0x53, 0xd1, 0x90, 0xca, 0x46, 0x9e, 0x9e, 0xb3, 0x61, 0xd5, 0x21, 0x97, 0x87, 0xc2, 0x60, - 0x06, 0x93, 0xe1, 0x4b, 0x71, 0xf1, 0xa7, 0xb0, 0x6a, 0x90, 0xa1, 0x4b, 0xbc, 0x91, 0xea, 0xdb, - 0x39, 0xfb, 0xfe, 0x80, 0x1d, 0x51, 0x05, 0xdf, 0xce, 0xb2, 0xef, 0xc2, 0x9d, 0x1e, 0xf1, 0x95, - 0x5d, 0xaf, 0xdf, 0xe5, 0x4b, 0x58, 0x4d, 0xc3, 0x6f, 0xb5, 0xcf, 0xde, 0xb7, 0x35, 0x28, 0xc9, - 0xc7, 0x07, 0x6a, 0x41, 0xb5, 0xef, 0x9a, 0x83, 0x73, 0xf1, 0x19, 0x0b, 0xe9, 0x33, 0x5f, 0xb6, - 0xe4, 0x19, 0x9a, 0xeb, 0x19, 0x2b, 0x6c, 0xc2, 0xcb, 0x7d, 0xae, 0xa1, 0xaf, 0xa1, 0x91, 0xfe, - 0x3a, 0x83, 0xb0, 0x82, 0x9f, 0xf3, 0xf1, 0xa9, 0xb9, 0x73, 0x2d, 0x86, 0x6b, 0x47, 0x07, 0x50, - 0x0e, 0xbf, 0x5e, 0x20, 0xf5, 0x81, 0x99, 0xfa, 0xfa, 0xd1, 0xd4, 0x33, 0xd7, 0x84, 0x8e, 0x37, - 0xbc, 0x32, 0xaa, 0x9f, 0x35, 0xd0, 0x47, 0xc9, 0xad, 0x33, 0x3e, 0x86, 0x34, 0xef, 0x5e, 0x07, - 0x11, 0x8a, 0xfb, 0x50, 0x4f, 0x3e, 0xce, 0x91, 0x6a, 0x52, 0xe6, 0xe3, 0xbf, 0xb9, 0x7d, 0x0d, - 0x22, 0xd2, 0x9a, 0xdc, 0x0f, 0xed, 0xcc, 0x3d, 0x4a, 0x96, 0xd6, 0x8c, 0x07, 0x3b, 0xce, 0xa1, - 0x5f, 0x01, 0x9a, 0x7d, 0x71, 0xa2, 0x7b, 0xb7, 0x79, 0x1d, 0x37, 0xf1, 0x0d, 0x28, 0xb1, 0xc3, - 0x2f, 0x61, 0x65, 0xe6, 0xad, 0x84, 0xbe, 0xa3, 0x88, 0xce, 0x7b, 0x83, 0x36, 0x3f, 0xba, 0x1e, - 0x14, 0x19, 0x30, 0xfb, 0x64, 0x49, 0x18, 0x30, 0xf7, 0x71, 0x94, 0x30, 0x60, 0xce, 0xbb, 0x47, - 0xe4, 0x5a, 0x38, 0x2a, 0x27, 0x72, 0x2d, 0x35, 0x52, 0x27, 0x72, 0x2d, 0x31, 0x5b, 0xe3, 0x1c, - 0x7a, 0x0a, 0x25, 0xc9, 0x42, 0x9b, 0xb3, 0xb0, 0x50, 0xc3, 0x46, 0xd6, 0x92, 0x50, 0x70, 0x02, - 0x35, 0x75, 0xaa, 0x40, 0xdb, 0x73, 0xdf, 0x5c, 0x42, 0xd5, 0xb5, 0x6f, 0xb2, 0xc8, 0x28, 0xde, - 0x21, 0xd3, 0x46, 0xa9, 0xb3, 0x42, 0xda, 0xa8, 0xb8, 0xa5, 0xe2, 0x1c, 0x1a, 0xf2, 0xef, 0x82, - 0x33, 0x2d, 0x0c, 0xdd, 0x4f, 0xca, 0xcc, 0x6b, 0xa5, 0xcd, 0x7b, 0x37, 0xe2, 0xc4, 0x3e, 0x6d, - 0xa8, 0x44, 0x0d, 0x07, 0x7d, 0xa0, 0x08, 0xa5, 0x9b, 0x5a, 0x73, 0x33, 0x7b, 0x31, 0x8a, 0x81, - 0x6c, 0x2a, 0x89, 0x18, 0x24, 0xdb, 0x51, 0x22, 0x06, 0x6a, 0x0f, 0xc2, 0x39, 0xf4, 0x1c, 0x20, - 0x6e, 0x1c, 0x68, 0x2b, 0xd1, 0xe5, 0x52, 0xad, 0xa7, 0xd9, 0x9c, 0xb3, 0x1a, 0x45, 0x53, 0x6d, - 0x05, 0x89, 0x68, 0x66, 0x34, 0x94, 0xe6, 0xd6, 0xdc, 0xf5, 0xa8, 0x36, 0x24, 0x8b, 0x7e, 0xa2, - 0x36, 0x64, 0xb6, 0x8f, 0x44, 0x6d, 0xc8, 0xe8, 0x18, 0x38, 0x77, 0xf0, 0xf8, 0xaf, 0xef, 0xb7, - 0xb5, 0x7f, 0xbc, 0xdf, 0xd6, 0xfe, 0xf5, 0x7e, 0x5b, 0xfb, 0xe3, 0xbf, 0xb7, 0x73, 0x80, 0x07, - 0xa3, 0xdd, 0x01, 0x71, 0x9d, 0x5d, 0xd3, 0x1e, 0x0f, 0xc8, 0x2e, 0xdd, 0xdb, 0x0d, 0x35, 0xb8, - 0xd3, 0x81, 0x47, 0xdc, 0x0b, 0xe2, 0x7e, 0x9d, 0x9f, 0x9e, 0x9e, 0x2e, 0xf2, 0x7f, 0x72, 0xbe, - 0xf8, 0x5f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xb4, 0x51, 0xba, 0xac, 0xe3, 0x19, 0x00, 0x00, -} - -// Reference imports to suppress errors if they are not otherwise used. -var _ context.Context -var _ grpc.ClientConn - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the grpc package it is being compiled against. -const _ = grpc.SupportPackageIsVersion4 - -// ControlClient is the client API for Control service. -// -// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. -type ControlClient interface { - TrackStatus(ctx context.Context, in *StatusRequest, opts ...grpc.CallOption) (Control_TrackStatusClient, error) - GetFrameworkInfo(ctx context.Context, in *GetFrameworkInfoRequest, opts ...grpc.CallOption) (*GetFrameworkInfoReply, error) - Teardown(ctx context.Context, in *TeardownRequest, opts ...grpc.CallOption) (*TeardownReply, error) - GetEnvironments(ctx context.Context, in *GetEnvironmentsRequest, opts ...grpc.CallOption) (*GetEnvironmentsReply, error) - NewEnvironment(ctx context.Context, in *NewEnvironmentRequest, opts ...grpc.CallOption) (*NewEnvironmentReply, error) - GetEnvironment(ctx context.Context, in *GetEnvironmentRequest, opts ...grpc.CallOption) (*GetEnvironmentReply, error) - ControlEnvironment(ctx context.Context, in *ControlEnvironmentRequest, opts ...grpc.CallOption) (*ControlEnvironmentReply, error) - ModifyEnvironment(ctx context.Context, in *ModifyEnvironmentRequest, opts ...grpc.CallOption) (*ModifyEnvironmentReply, error) - DestroyEnvironment(ctx context.Context, in *DestroyEnvironmentRequest, opts ...grpc.CallOption) (*DestroyEnvironmentReply, error) - GetTasks(ctx context.Context, in *GetTasksRequest, opts ...grpc.CallOption) (*GetTasksReply, error) - GetTask(ctx context.Context, in *GetTaskRequest, opts ...grpc.CallOption) (*GetTaskReply, error) - CleanupTasks(ctx context.Context, in *CleanupTasksRequest, opts ...grpc.CallOption) (*CleanupTasksReply, error) - GetRoles(ctx context.Context, in *GetRolesRequest, opts ...grpc.CallOption) (*GetRolesReply, error) - GetWorkflowTemplates(ctx context.Context, in *GetWorkflowTemplatesRequest, opts ...grpc.CallOption) (*GetWorkflowTemplatesReply, error) - ListRepos(ctx context.Context, in *ListReposRequest, opts ...grpc.CallOption) (*ListReposReply, error) - AddRepo(ctx context.Context, in *AddRepoRequest, opts ...grpc.CallOption) (*AddRepoReply, error) - RemoveRepo(ctx context.Context, in *RemoveRepoRequest, opts ...grpc.CallOption) (*RemoveRepoReply, error) - RefreshRepos(ctx context.Context, in *RefreshReposRequest, opts ...grpc.CallOption) (*RefreshReposReply, error) - SetDefaultRepo(ctx context.Context, in *SetDefaultRepoRequest, opts ...grpc.CallOption) (*SetDefaultRepoReply, error) +// Deprecated: Use CommandInfo.ProtoReflect.Descriptor instead. +func (*CommandInfo) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{34} } -type controlClient struct { - cc *grpc.ClientConn +func (x *CommandInfo) GetEnv() []string { + if x != nil { + return x.Env + } + return nil } -func NewControlClient(cc *grpc.ClientConn) ControlClient { - return &controlClient{cc} +func (x *CommandInfo) GetShell() bool { + if x != nil { + return x.Shell + } + return false } -func (c *controlClient) TrackStatus(ctx context.Context, in *StatusRequest, opts ...grpc.CallOption) (Control_TrackStatusClient, error) { - stream, err := c.cc.NewStream(ctx, &_Control_serviceDesc.Streams[0], "/o2control.Control/TrackStatus", opts...) - if err != nil { - return nil, err - } - x := &controlTrackStatusClient{stream} - if err := x.ClientStream.SendMsg(in); err != nil { - return nil, err +func (x *CommandInfo) GetValue() string { + if x != nil { + return x.Value } - if err := x.ClientStream.CloseSend(); err != nil { - return nil, err + return "" +} + +func (x *CommandInfo) GetArguments() []string { + if x != nil { + return x.Arguments } - return x, nil + return nil } -type Control_TrackStatusClient interface { - Recv() (*StatusReply, error) - grpc.ClientStream +func (x *CommandInfo) GetUser() string { + if x != nil { + return x.User + } + return "" } -type controlTrackStatusClient struct { - grpc.ClientStream +type ChannelInfo struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Type string `protobuf:"bytes,2,opt,name=type,proto3" json:"type,omitempty"` + Target string `protobuf:"bytes,3,opt,name=target,proto3" json:"target,omitempty"` } -func (x *controlTrackStatusClient) Recv() (*StatusReply, error) { - m := new(StatusReply) - if err := x.ClientStream.RecvMsg(m); err != nil { - return nil, err +func (x *ChannelInfo) Reset() { + *x = ChannelInfo{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[35] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return m, nil } -func (c *controlClient) GetFrameworkInfo(ctx context.Context, in *GetFrameworkInfoRequest, opts ...grpc.CallOption) (*GetFrameworkInfoReply, error) { - out := new(GetFrameworkInfoReply) - err := c.cc.Invoke(ctx, "/o2control.Control/GetFrameworkInfo", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil +func (x *ChannelInfo) String() string { + return protoimpl.X.MessageStringOf(x) } -func (c *controlClient) Teardown(ctx context.Context, in *TeardownRequest, opts ...grpc.CallOption) (*TeardownReply, error) { - out := new(TeardownReply) - err := c.cc.Invoke(ctx, "/o2control.Control/Teardown", in, out, opts...) - if err != nil { - return nil, err +func (*ChannelInfo) ProtoMessage() {} + +func (x *ChannelInfo) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[35] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms } - return out, nil + return mi.MessageOf(x) } -func (c *controlClient) GetEnvironments(ctx context.Context, in *GetEnvironmentsRequest, opts ...grpc.CallOption) (*GetEnvironmentsReply, error) { - out := new(GetEnvironmentsReply) - err := c.cc.Invoke(ctx, "/o2control.Control/GetEnvironments", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil +// Deprecated: Use ChannelInfo.ProtoReflect.Descriptor instead. +func (*ChannelInfo) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{35} } -func (c *controlClient) NewEnvironment(ctx context.Context, in *NewEnvironmentRequest, opts ...grpc.CallOption) (*NewEnvironmentReply, error) { - out := new(NewEnvironmentReply) - err := c.cc.Invoke(ctx, "/o2control.Control/NewEnvironment", in, out, opts...) - if err != nil { - return nil, err +func (x *ChannelInfo) GetName() string { + if x != nil { + return x.Name } - return out, nil + return "" } -func (c *controlClient) GetEnvironment(ctx context.Context, in *GetEnvironmentRequest, opts ...grpc.CallOption) (*GetEnvironmentReply, error) { - out := new(GetEnvironmentReply) - err := c.cc.Invoke(ctx, "/o2control.Control/GetEnvironment", in, out, opts...) - if err != nil { - return nil, err +func (x *ChannelInfo) GetType() string { + if x != nil { + return x.Type } - return out, nil + return "" } -func (c *controlClient) ControlEnvironment(ctx context.Context, in *ControlEnvironmentRequest, opts ...grpc.CallOption) (*ControlEnvironmentReply, error) { - out := new(ControlEnvironmentReply) - err := c.cc.Invoke(ctx, "/o2control.Control/ControlEnvironment", in, out, opts...) - if err != nil { - return nil, err +func (x *ChannelInfo) GetTarget() string { + if x != nil { + return x.Target } - return out, nil + return "" } -func (c *controlClient) ModifyEnvironment(ctx context.Context, in *ModifyEnvironmentRequest, opts ...grpc.CallOption) (*ModifyEnvironmentReply, error) { - out := new(ModifyEnvironmentReply) - err := c.cc.Invoke(ctx, "/o2control.Control/ModifyEnvironment", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil +type TaskInfo struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ShortInfo *ShortTaskInfo `protobuf:"bytes,1,opt,name=shortInfo,proto3" json:"shortInfo,omitempty"` + InboundChannels []*ChannelInfo `protobuf:"bytes,3,rep,name=inboundChannels,proto3" json:"inboundChannels,omitempty"` + OutboundChannels []*ChannelInfo `protobuf:"bytes,4,rep,name=outboundChannels,proto3" json:"outboundChannels,omitempty"` + CommandInfo *CommandInfo `protobuf:"bytes,5,opt,name=commandInfo,proto3" json:"commandInfo,omitempty"` + TaskPath string `protobuf:"bytes,6,opt,name=taskPath,proto3" json:"taskPath,omitempty"` + EnvId string `protobuf:"bytes,7,opt,name=envId,proto3" json:"envId,omitempty"` + Properties map[string]string `protobuf:"bytes,9,rep,name=properties,proto3" json:"properties,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` } -func (c *controlClient) DestroyEnvironment(ctx context.Context, in *DestroyEnvironmentRequest, opts ...grpc.CallOption) (*DestroyEnvironmentReply, error) { - out := new(DestroyEnvironmentReply) - err := c.cc.Invoke(ctx, "/o2control.Control/DestroyEnvironment", in, out, opts...) - if err != nil { - return nil, err +func (x *TaskInfo) Reset() { + *x = TaskInfo{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[36] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return out, nil } -func (c *controlClient) GetTasks(ctx context.Context, in *GetTasksRequest, opts ...grpc.CallOption) (*GetTasksReply, error) { - out := new(GetTasksReply) - err := c.cc.Invoke(ctx, "/o2control.Control/GetTasks", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil +func (x *TaskInfo) String() string { + return protoimpl.X.MessageStringOf(x) } -func (c *controlClient) GetTask(ctx context.Context, in *GetTaskRequest, opts ...grpc.CallOption) (*GetTaskReply, error) { - out := new(GetTaskReply) - err := c.cc.Invoke(ctx, "/o2control.Control/GetTask", in, out, opts...) - if err != nil { - return nil, err +func (*TaskInfo) ProtoMessage() {} + +func (x *TaskInfo) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[36] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms } - return out, nil + return mi.MessageOf(x) } -func (c *controlClient) CleanupTasks(ctx context.Context, in *CleanupTasksRequest, opts ...grpc.CallOption) (*CleanupTasksReply, error) { - out := new(CleanupTasksReply) - err := c.cc.Invoke(ctx, "/o2control.Control/CleanupTasks", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil +// Deprecated: Use TaskInfo.ProtoReflect.Descriptor instead. +func (*TaskInfo) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{36} } -func (c *controlClient) GetRoles(ctx context.Context, in *GetRolesRequest, opts ...grpc.CallOption) (*GetRolesReply, error) { - out := new(GetRolesReply) - err := c.cc.Invoke(ctx, "/o2control.Control/GetRoles", in, out, opts...) - if err != nil { - return nil, err +func (x *TaskInfo) GetShortInfo() *ShortTaskInfo { + if x != nil { + return x.ShortInfo } - return out, nil + return nil } -func (c *controlClient) GetWorkflowTemplates(ctx context.Context, in *GetWorkflowTemplatesRequest, opts ...grpc.CallOption) (*GetWorkflowTemplatesReply, error) { - out := new(GetWorkflowTemplatesReply) - err := c.cc.Invoke(ctx, "/o2control.Control/GetWorkflowTemplates", in, out, opts...) - if err != nil { - return nil, err +func (x *TaskInfo) GetInboundChannels() []*ChannelInfo { + if x != nil { + return x.InboundChannels } - return out, nil + return nil } -func (c *controlClient) ListRepos(ctx context.Context, in *ListReposRequest, opts ...grpc.CallOption) (*ListReposReply, error) { - out := new(ListReposReply) - err := c.cc.Invoke(ctx, "/o2control.Control/ListRepos", in, out, opts...) - if err != nil { - return nil, err +func (x *TaskInfo) GetOutboundChannels() []*ChannelInfo { + if x != nil { + return x.OutboundChannels } - return out, nil + return nil } -func (c *controlClient) AddRepo(ctx context.Context, in *AddRepoRequest, opts ...grpc.CallOption) (*AddRepoReply, error) { - out := new(AddRepoReply) - err := c.cc.Invoke(ctx, "/o2control.Control/AddRepo", in, out, opts...) - if err != nil { - return nil, err +func (x *TaskInfo) GetCommandInfo() *CommandInfo { + if x != nil { + return x.CommandInfo } - return out, nil + return nil } -func (c *controlClient) RemoveRepo(ctx context.Context, in *RemoveRepoRequest, opts ...grpc.CallOption) (*RemoveRepoReply, error) { - out := new(RemoveRepoReply) - err := c.cc.Invoke(ctx, "/o2control.Control/RemoveRepo", in, out, opts...) - if err != nil { - return nil, err +func (x *TaskInfo) GetTaskPath() string { + if x != nil { + return x.TaskPath } - return out, nil + return "" } -func (c *controlClient) RefreshRepos(ctx context.Context, in *RefreshReposRequest, opts ...grpc.CallOption) (*RefreshReposReply, error) { - out := new(RefreshReposReply) - err := c.cc.Invoke(ctx, "/o2control.Control/RefreshRepos", in, out, opts...) - if err != nil { - return nil, err +func (x *TaskInfo) GetEnvId() string { + if x != nil { + return x.EnvId } - return out, nil + return "" } -func (c *controlClient) SetDefaultRepo(ctx context.Context, in *SetDefaultRepoRequest, opts ...grpc.CallOption) (*SetDefaultRepoReply, error) { - out := new(SetDefaultRepoReply) - err := c.cc.Invoke(ctx, "/o2control.Control/SetDefaultRepo", in, out, opts...) - if err != nil { - return nil, err +func (x *TaskInfo) GetProperties() map[string]string { + if x != nil { + return x.Properties } - return out, nil + return nil } -// ControlServer is the server API for Control service. -type ControlServer interface { - TrackStatus(*StatusRequest, Control_TrackStatusServer) error - GetFrameworkInfo(context.Context, *GetFrameworkInfoRequest) (*GetFrameworkInfoReply, error) - Teardown(context.Context, *TeardownRequest) (*TeardownReply, error) - GetEnvironments(context.Context, *GetEnvironmentsRequest) (*GetEnvironmentsReply, error) - NewEnvironment(context.Context, *NewEnvironmentRequest) (*NewEnvironmentReply, error) - GetEnvironment(context.Context, *GetEnvironmentRequest) (*GetEnvironmentReply, error) - ControlEnvironment(context.Context, *ControlEnvironmentRequest) (*ControlEnvironmentReply, error) - ModifyEnvironment(context.Context, *ModifyEnvironmentRequest) (*ModifyEnvironmentReply, error) - DestroyEnvironment(context.Context, *DestroyEnvironmentRequest) (*DestroyEnvironmentReply, error) - GetTasks(context.Context, *GetTasksRequest) (*GetTasksReply, error) - GetTask(context.Context, *GetTaskRequest) (*GetTaskReply, error) - CleanupTasks(context.Context, *CleanupTasksRequest) (*CleanupTasksReply, error) - GetRoles(context.Context, *GetRolesRequest) (*GetRolesReply, error) - GetWorkflowTemplates(context.Context, *GetWorkflowTemplatesRequest) (*GetWorkflowTemplatesReply, error) - ListRepos(context.Context, *ListReposRequest) (*ListReposReply, error) - AddRepo(context.Context, *AddRepoRequest) (*AddRepoReply, error) - RemoveRepo(context.Context, *RemoveRepoRequest) (*RemoveRepoReply, error) - RefreshRepos(context.Context, *RefreshReposRequest) (*RefreshReposReply, error) - SetDefaultRepo(context.Context, *SetDefaultRepoRequest) (*SetDefaultRepoReply, error) -} +type CleanupTasksRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields -func RegisterControlServer(s *grpc.Server, srv ControlServer) { - s.RegisterService(&_Control_serviceDesc, srv) + TaskIds []string `protobuf:"bytes,1,rep,name=taskIds,proto3" json:"taskIds,omitempty"` } -func _Control_TrackStatus_Handler(srv interface{}, stream grpc.ServerStream) error { - m := new(StatusRequest) - if err := stream.RecvMsg(m); err != nil { - return err +func (x *CleanupTasksRequest) Reset() { + *x = CleanupTasksRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[37] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return srv.(ControlServer).TrackStatus(m, &controlTrackStatusServer{stream}) } -type Control_TrackStatusServer interface { - Send(*StatusReply) error - grpc.ServerStream +func (x *CleanupTasksRequest) String() string { + return protoimpl.X.MessageStringOf(x) } -type controlTrackStatusServer struct { - grpc.ServerStream +func (*CleanupTasksRequest) ProtoMessage() {} + +func (x *CleanupTasksRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[37] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -func (x *controlTrackStatusServer) Send(m *StatusReply) error { - return x.ServerStream.SendMsg(m) +// Deprecated: Use CleanupTasksRequest.ProtoReflect.Descriptor instead. +func (*CleanupTasksRequest) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{37} } -func _Control_GetFrameworkInfo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetFrameworkInfoRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ControlServer).GetFrameworkInfo(ctx, in) +func (x *CleanupTasksRequest) GetTaskIds() []string { + if x != nil { + return x.TaskIds } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/o2control.Control/GetFrameworkInfo", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ControlServer).GetFrameworkInfo(ctx, req.(*GetFrameworkInfoRequest)) - } - return interceptor(ctx, in, info, handler) + return nil } -func _Control_Teardown_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(TeardownRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ControlServer).Teardown(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/o2control.Control/Teardown", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ControlServer).Teardown(ctx, req.(*TeardownRequest)) - } - return interceptor(ctx, in, info, handler) +type CleanupTasksReply struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + KilledTasks []*ShortTaskInfo `protobuf:"bytes,1,rep,name=killedTasks,proto3" json:"killedTasks,omitempty"` + RunningTasks []*ShortTaskInfo `protobuf:"bytes,2,rep,name=runningTasks,proto3" json:"runningTasks,omitempty"` + Timestamp int64 `protobuf:"varint,3,opt,name=timestamp,proto3" json:"timestamp,omitempty"` // timestamp of when this object was sent in unix milliseconds } -func _Control_GetEnvironments_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetEnvironmentsRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ControlServer).GetEnvironments(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/o2control.Control/GetEnvironments", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ControlServer).GetEnvironments(ctx, req.(*GetEnvironmentsRequest)) +func (x *CleanupTasksReply) Reset() { + *x = CleanupTasksReply{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[38] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return interceptor(ctx, in, info, handler) } -func _Control_NewEnvironment_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(NewEnvironmentRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ControlServer).NewEnvironment(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/o2control.Control/NewEnvironment", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ControlServer).NewEnvironment(ctx, req.(*NewEnvironmentRequest)) - } - return interceptor(ctx, in, info, handler) +func (x *CleanupTasksReply) String() string { + return protoimpl.X.MessageStringOf(x) } -func _Control_GetEnvironment_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetEnvironmentRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ControlServer).GetEnvironment(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/o2control.Control/GetEnvironment", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ControlServer).GetEnvironment(ctx, req.(*GetEnvironmentRequest)) +func (*CleanupTasksReply) ProtoMessage() {} + +func (x *CleanupTasksReply) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[38] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms } - return interceptor(ctx, in, info, handler) + return mi.MessageOf(x) } -func _Control_ControlEnvironment_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ControlEnvironmentRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ControlServer).ControlEnvironment(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/o2control.Control/ControlEnvironment", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ControlServer).ControlEnvironment(ctx, req.(*ControlEnvironmentRequest)) - } - return interceptor(ctx, in, info, handler) +// Deprecated: Use CleanupTasksReply.ProtoReflect.Descriptor instead. +func (*CleanupTasksReply) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{38} } -func _Control_ModifyEnvironment_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ModifyEnvironmentRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ControlServer).ModifyEnvironment(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/o2control.Control/ModifyEnvironment", +func (x *CleanupTasksReply) GetKilledTasks() []*ShortTaskInfo { + if x != nil { + return x.KilledTasks } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ControlServer).ModifyEnvironment(ctx, req.(*ModifyEnvironmentRequest)) - } - return interceptor(ctx, in, info, handler) + return nil } -func _Control_DestroyEnvironment_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(DestroyEnvironmentRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ControlServer).DestroyEnvironment(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/o2control.Control/DestroyEnvironment", +func (x *CleanupTasksReply) GetRunningTasks() []*ShortTaskInfo { + if x != nil { + return x.RunningTasks } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ControlServer).DestroyEnvironment(ctx, req.(*DestroyEnvironmentRequest)) - } - return interceptor(ctx, in, info, handler) + return nil } -func _Control_GetTasks_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetTasksRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ControlServer).GetTasks(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/o2control.Control/GetTasks", +func (x *CleanupTasksReply) GetTimestamp() int64 { + if x != nil { + return x.Timestamp } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ControlServer).GetTasks(ctx, req.(*GetTasksRequest)) - } - return interceptor(ctx, in, info, handler) + return 0 } -func _Control_GetTask_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetTaskRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ControlServer).GetTask(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/o2control.Control/GetTask", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ControlServer).GetTask(ctx, req.(*GetTaskRequest)) - } - return interceptor(ctx, in, info, handler) +// ////////////////////////////////////// +// Roles +// ////////////////////////////////////// +type GetRolesRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + EnvId string `protobuf:"bytes,1,opt,name=envId,proto3" json:"envId,omitempty"` + PathSpec string `protobuf:"bytes,2,opt,name=pathSpec,proto3" json:"pathSpec,omitempty"` } -func _Control_CleanupTasks_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(CleanupTasksRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ControlServer).CleanupTasks(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/o2control.Control/CleanupTasks", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ControlServer).CleanupTasks(ctx, req.(*CleanupTasksRequest)) +func (x *GetRolesRequest) Reset() { + *x = GetRolesRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[39] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return interceptor(ctx, in, info, handler) } -func _Control_GetRoles_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetRolesRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ControlServer).GetRoles(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/o2control.Control/GetRoles", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ControlServer).GetRoles(ctx, req.(*GetRolesRequest)) - } - return interceptor(ctx, in, info, handler) +func (x *GetRolesRequest) String() string { + return protoimpl.X.MessageStringOf(x) } -func _Control_GetWorkflowTemplates_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetWorkflowTemplatesRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ControlServer).GetWorkflowTemplates(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/o2control.Control/GetWorkflowTemplates", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ControlServer).GetWorkflowTemplates(ctx, req.(*GetWorkflowTemplatesRequest)) +func (*GetRolesRequest) ProtoMessage() {} + +func (x *GetRolesRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[39] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms } - return interceptor(ctx, in, info, handler) + return mi.MessageOf(x) } -func _Control_ListRepos_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ListReposRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ControlServer).ListRepos(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/o2control.Control/ListRepos", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ControlServer).ListRepos(ctx, req.(*ListReposRequest)) - } - return interceptor(ctx, in, info, handler) +// Deprecated: Use GetRolesRequest.ProtoReflect.Descriptor instead. +func (*GetRolesRequest) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{39} } -func _Control_AddRepo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(AddRepoRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ControlServer).AddRepo(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/o2control.Control/AddRepo", +func (x *GetRolesRequest) GetEnvId() string { + if x != nil { + return x.EnvId } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ControlServer).AddRepo(ctx, req.(*AddRepoRequest)) - } - return interceptor(ctx, in, info, handler) + return "" } -func _Control_RemoveRepo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(RemoveRepoRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ControlServer).RemoveRepo(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/o2control.Control/RemoveRepo", +func (x *GetRolesRequest) GetPathSpec() string { + if x != nil { + return x.PathSpec } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ControlServer).RemoveRepo(ctx, req.(*RemoveRepoRequest)) - } - return interceptor(ctx, in, info, handler) + return "" } -func _Control_RefreshRepos_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(RefreshReposRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ControlServer).RefreshRepos(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/o2control.Control/RefreshRepos", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ControlServer).RefreshRepos(ctx, req.(*RefreshReposRequest)) - } - return interceptor(ctx, in, info, handler) +type RoleInfo struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Status string `protobuf:"bytes,2,opt,name=status,proto3" json:"status,omitempty"` + State string `protobuf:"bytes,3,opt,name=state,proto3" json:"state,omitempty"` + FullPath string `protobuf:"bytes,4,opt,name=fullPath,proto3" json:"fullPath,omitempty"` + TaskIds []string `protobuf:"bytes,5,rep,name=taskIds,proto3" json:"taskIds,omitempty"` + Roles []*RoleInfo `protobuf:"bytes,6,rep,name=roles,proto3" json:"roles,omitempty"` + Defaults map[string]string `protobuf:"bytes,7,rep,name=defaults,proto3" json:"defaults,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Vars map[string]string `protobuf:"bytes,8,rep,name=vars,proto3" json:"vars,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + UserVars map[string]string `protobuf:"bytes,9,rep,name=userVars,proto3" json:"userVars,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + ConsolidatedStack map[string]string `protobuf:"bytes,10,rep,name=consolidatedStack,proto3" json:"consolidatedStack,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Description string `protobuf:"bytes,11,opt,name=description,proto3" json:"description,omitempty"` } -func _Control_SetDefaultRepo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(SetDefaultRepoRequest) - if err := dec(in); err != nil { - return nil, err +func (x *RoleInfo) Reset() { + *x = RoleInfo{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[40] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - if interceptor == nil { - return srv.(ControlServer).SetDefaultRepo(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/o2control.Control/SetDefaultRepo", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ControlServer).SetDefaultRepo(ctx, req.(*SetDefaultRepoRequest)) - } - return interceptor(ctx, in, info, handler) } -var _Control_serviceDesc = grpc.ServiceDesc{ - ServiceName: "o2control.Control", - HandlerType: (*ControlServer)(nil), - Methods: []grpc.MethodDesc{ - { - MethodName: "GetFrameworkInfo", - Handler: _Control_GetFrameworkInfo_Handler, - }, - { - MethodName: "Teardown", - Handler: _Control_Teardown_Handler, - }, - { - MethodName: "GetEnvironments", - Handler: _Control_GetEnvironments_Handler, - }, - { - MethodName: "NewEnvironment", - Handler: _Control_NewEnvironment_Handler, - }, - { - MethodName: "GetEnvironment", - Handler: _Control_GetEnvironment_Handler, - }, - { - MethodName: "ControlEnvironment", - Handler: _Control_ControlEnvironment_Handler, - }, - { - MethodName: "ModifyEnvironment", - Handler: _Control_ModifyEnvironment_Handler, - }, - { - MethodName: "DestroyEnvironment", - Handler: _Control_DestroyEnvironment_Handler, - }, - { - MethodName: "GetTasks", - Handler: _Control_GetTasks_Handler, - }, - { - MethodName: "GetTask", - Handler: _Control_GetTask_Handler, - }, - { - MethodName: "CleanupTasks", - Handler: _Control_CleanupTasks_Handler, - }, - { - MethodName: "GetRoles", - Handler: _Control_GetRoles_Handler, - }, - { - MethodName: "GetWorkflowTemplates", - Handler: _Control_GetWorkflowTemplates_Handler, - }, - { - MethodName: "ListRepos", - Handler: _Control_ListRepos_Handler, - }, - { - MethodName: "AddRepo", - Handler: _Control_AddRepo_Handler, - }, - { - MethodName: "RemoveRepo", - Handler: _Control_RemoveRepo_Handler, - }, - { - MethodName: "RefreshRepos", - Handler: _Control_RefreshRepos_Handler, - }, - { - MethodName: "SetDefaultRepo", - Handler: _Control_SetDefaultRepo_Handler, - }, - }, - Streams: []grpc.StreamDesc{ - { - StreamName: "TrackStatus", - Handler: _Control_TrackStatus_Handler, - ServerStreams: true, - }, - }, - Metadata: "protos/o2control.proto", +func (x *RoleInfo) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *Event_MesosHeartbeat) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err +func (*RoleInfo) ProtoMessage() {} + +func (x *RoleInfo) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[40] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms } - return dAtA[:n], nil + return mi.MessageOf(x) } -func (m *Event_MesosHeartbeat) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) - } - return i, nil +// Deprecated: Use RoleInfo.ProtoReflect.Descriptor instead. +func (*RoleInfo) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{40} } -func (m *StatusRequest) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err +func (x *RoleInfo) GetName() string { + if x != nil { + return x.Name } - return dAtA[:n], nil + return "" } -func (m *StatusRequest) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) +func (x *RoleInfo) GetStatus() string { + if x != nil { + return x.Status } - return i, nil + return "" } -func (m *StatusReply) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err +func (x *RoleInfo) GetState() string { + if x != nil { + return x.State } - return dAtA[:n], nil + return "" } -func (m *StatusReply) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.State) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.State))) - i += copy(dAtA[i:], m.State) - } - if len(m.StatusUpdates) > 0 { - for _, msg := range m.StatusUpdates { - dAtA[i] = 0x12 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n - } +func (x *RoleInfo) GetFullPath() string { + if x != nil { + return x.FullPath } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) - } - return i, nil + return "" } -func (m *StatusUpdate) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err +func (x *RoleInfo) GetTaskIds() []string { + if x != nil { + return x.TaskIds } - return dAtA[:n], nil + return nil } -func (m *StatusUpdate) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if m.Level != 0 { - dAtA[i] = 0x8 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(m.Level)) - } - if m.Event != nil { - nn1, err1 := m.Event.MarshalTo(dAtA[i:]) - if err1 != nil { - return 0, err1 - } - i += nn1 - } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) +func (x *RoleInfo) GetRoles() []*RoleInfo { + if x != nil { + return x.Roles } - return i, nil + return nil } -func (m *StatusUpdate_MesosHeartbeat) MarshalTo(dAtA []byte) (int, error) { - i := 0 - if m.MesosHeartbeat != nil { - dAtA[i] = 0x12 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(m.MesosHeartbeat.Size())) - n2, err2 := m.MesosHeartbeat.MarshalTo(dAtA[i:]) - if err2 != nil { - return 0, err2 - } - i += n2 - } - return i, nil -} -func (m *GetFrameworkInfoRequest) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *GetFrameworkInfoRequest) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) - } - return i, nil -} - -func (m *Version) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *Version) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if m.Major != 0 { - dAtA[i] = 0x8 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(m.Major)) - } - if m.Minor != 0 { - dAtA[i] = 0x10 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(m.Minor)) - } - if m.Patch != 0 { - dAtA[i] = 0x18 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(m.Patch)) - } - if len(m.Build) > 0 { - dAtA[i] = 0x22 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.Build))) - i += copy(dAtA[i:], m.Build) - } - if len(m.ProductName) > 0 { - dAtA[i] = 0x2a - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.ProductName))) - i += copy(dAtA[i:], m.ProductName) - } - if len(m.VersionStr) > 0 { - dAtA[i] = 0x32 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.VersionStr))) - i += copy(dAtA[i:], m.VersionStr) - } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) - } - return i, nil -} - -func (m *GetFrameworkInfoReply) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *GetFrameworkInfoReply) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.FrameworkId) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.FrameworkId))) - i += copy(dAtA[i:], m.FrameworkId) - } - if m.EnvironmentsCount != 0 { - dAtA[i] = 0x10 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(m.EnvironmentsCount)) - } - if m.TasksCount != 0 { - dAtA[i] = 0x18 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(m.TasksCount)) - } - if len(m.State) > 0 { - dAtA[i] = 0x22 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.State))) - i += copy(dAtA[i:], m.State) - } - if m.HostsCount != 0 { - dAtA[i] = 0x28 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(m.HostsCount)) - } - if len(m.InstanceName) > 0 { - dAtA[i] = 0x32 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.InstanceName))) - i += copy(dAtA[i:], m.InstanceName) - } - if m.Version != nil { - dAtA[i] = 0x3a - i++ - i = encodeVarintO2Control(dAtA, i, uint64(m.Version.Size())) - n3, err3 := m.Version.MarshalTo(dAtA[i:]) - if err3 != nil { - return 0, err3 - } - i += n3 - } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) +func (x *RoleInfo) GetDefaults() map[string]string { + if x != nil { + return x.Defaults } - return i, nil + return nil } -func (m *TeardownRequest) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err +func (x *RoleInfo) GetVars() map[string]string { + if x != nil { + return x.Vars } - return dAtA[:n], nil + return nil } -func (m *TeardownRequest) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.Reason) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.Reason))) - i += copy(dAtA[i:], m.Reason) +func (x *RoleInfo) GetUserVars() map[string]string { + if x != nil { + return x.UserVars } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) - } - return i, nil + return nil } -func (m *TeardownReply) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err +func (x *RoleInfo) GetConsolidatedStack() map[string]string { + if x != nil { + return x.ConsolidatedStack } - return dAtA[:n], nil + return nil } -func (m *TeardownReply) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) +func (x *RoleInfo) GetDescription() string { + if x != nil { + return x.Description } - return i, nil + return "" } -func (m *GetEnvironmentsRequest) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil +type GetRolesReply struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Roles []*RoleInfo `protobuf:"bytes,1,rep,name=roles,proto3" json:"roles,omitempty"` + Timestamp int64 `protobuf:"varint,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"` // timestamp of when this object was sent in unix milliseconds } -func (m *GetEnvironmentsRequest) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) +func (x *GetRolesReply) Reset() { + *x = GetRolesReply{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[41] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return i, nil } -func (m *GetEnvironmentsReply) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil +func (x *GetRolesReply) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *GetEnvironmentsReply) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.FrameworkId) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.FrameworkId))) - i += copy(dAtA[i:], m.FrameworkId) - } - if len(m.Environments) > 0 { - for _, msg := range m.Environments { - dAtA[i] = 0x12 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n - } - } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) - } - return i, nil -} - -func (m *EnvironmentInfo) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *EnvironmentInfo) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.Id) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.Id))) - i += copy(dAtA[i:], m.Id) - } - if len(m.CreatedWhen) > 0 { - dAtA[i] = 0x12 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.CreatedWhen))) - i += copy(dAtA[i:], m.CreatedWhen) - } - if len(m.State) > 0 { - dAtA[i] = 0x1a - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.State))) - i += copy(dAtA[i:], m.State) - } - if len(m.Tasks) > 0 { - for _, msg := range m.Tasks { - dAtA[i] = 0x22 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n +func (*GetRolesReply) ProtoMessage() {} + +func (x *GetRolesReply) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[41] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) } + return ms } - if len(m.RootRole) > 0 { - dAtA[i] = 0x2a - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.RootRole))) - i += copy(dAtA[i:], m.RootRole) - } - if m.CurrentRunNumber != 0 { - dAtA[i] = 0x30 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(m.CurrentRunNumber)) - } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) - } - return i, nil + return mi.MessageOf(x) } -func (m *NewEnvironmentRequest) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil +// Deprecated: Use GetRolesReply.ProtoReflect.Descriptor instead. +func (*GetRolesReply) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{41} } -func (m *NewEnvironmentRequest) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.WorkflowTemplate) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.WorkflowTemplate))) - i += copy(dAtA[i:], m.WorkflowTemplate) - } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) +func (x *GetRolesReply) GetRoles() []*RoleInfo { + if x != nil { + return x.Roles } - return i, nil + return nil } -func (m *NewEnvironmentReply) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err +func (x *GetRolesReply) GetTimestamp() int64 { + if x != nil { + return x.Timestamp } - return dAtA[:n], nil + return 0 } -func (m *NewEnvironmentReply) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if m.Environment != nil { - dAtA[i] = 0xa - i++ - i = encodeVarintO2Control(dAtA, i, uint64(m.Environment.Size())) - n4, err4 := m.Environment.MarshalTo(dAtA[i:]) - if err4 != nil { - return 0, err4 - } - i += n4 - } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) - } - return i, nil -} +type GetWorkflowTemplatesRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields -func (m *GetEnvironmentRequest) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil + RepoPattern string `protobuf:"bytes,1,opt,name=repoPattern,proto3" json:"repoPattern,omitempty"` + RevisionPattern string `protobuf:"bytes,2,opt,name=revisionPattern,proto3" json:"revisionPattern,omitempty"` + AllBranches bool `protobuf:"varint,3,opt,name=allBranches,proto3" json:"allBranches,omitempty"` + AllTags bool `protobuf:"varint,4,opt,name=allTags,proto3" json:"allTags,omitempty"` + AllWorkflows bool `protobuf:"varint,5,opt,name=allWorkflows,proto3" json:"allWorkflows,omitempty"` } -func (m *GetEnvironmentRequest) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.Id) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.Id))) - i += copy(dAtA[i:], m.Id) +func (x *GetWorkflowTemplatesRequest) Reset() { + *x = GetWorkflowTemplatesRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[42] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) - } - return i, nil } -func (m *GetEnvironmentReply) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil +func (x *GetWorkflowTemplatesRequest) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *GetEnvironmentReply) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if m.Environment != nil { - dAtA[i] = 0xa - i++ - i = encodeVarintO2Control(dAtA, i, uint64(m.Environment.Size())) - n5, err5 := m.Environment.MarshalTo(dAtA[i:]) - if err5 != nil { - return 0, err5 - } - i += n5 - } - if m.Workflow != nil { - dAtA[i] = 0x12 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(m.Workflow.Size())) - n6, err6 := m.Workflow.MarshalTo(dAtA[i:]) - if err6 != nil { - return 0, err6 - } - i += n6 - } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) - } - return i, nil -} - -func (m *ControlEnvironmentRequest) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *ControlEnvironmentRequest) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.Id) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.Id))) - i += copy(dAtA[i:], m.Id) - } - if m.Type != 0 { - dAtA[i] = 0x10 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(m.Type)) - } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) - } - return i, nil -} - -func (m *ControlEnvironmentReply) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *ControlEnvironmentReply) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.Id) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.Id))) - i += copy(dAtA[i:], m.Id) - } - if len(m.State) > 0 { - dAtA[i] = 0x12 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.State))) - i += copy(dAtA[i:], m.State) - } - if m.CurrentRunNumber != 0 { - dAtA[i] = 0x18 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(m.CurrentRunNumber)) - } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) - } - return i, nil -} - -func (m *ModifyEnvironmentRequest) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *ModifyEnvironmentRequest) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.Id) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.Id))) - i += copy(dAtA[i:], m.Id) - } - if len(m.Operations) > 0 { - for _, msg := range m.Operations { - dAtA[i] = 0x12 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n - } - } - if m.ReconfigureAll { - dAtA[i] = 0x18 - i++ - if m.ReconfigureAll { - dAtA[i] = 1 - } else { - dAtA[i] = 0 +func (*GetWorkflowTemplatesRequest) ProtoMessage() {} + +func (x *GetWorkflowTemplatesRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[42] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) } - i++ - } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) + return ms } - return i, nil + return mi.MessageOf(x) } -func (m *EnvironmentOperation) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil +// Deprecated: Use GetWorkflowTemplatesRequest.ProtoReflect.Descriptor instead. +func (*GetWorkflowTemplatesRequest) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{42} } -func (m *EnvironmentOperation) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if m.Type != 0 { - dAtA[i] = 0x8 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(m.Type)) +func (x *GetWorkflowTemplatesRequest) GetRepoPattern() string { + if x != nil { + return x.RepoPattern } - if len(m.RoleName) > 0 { - dAtA[i] = 0x12 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.RoleName))) - i += copy(dAtA[i:], m.RoleName) - } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) - } - return i, nil + return "" } -func (m *ModifyEnvironmentReply) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err +func (x *GetWorkflowTemplatesRequest) GetRevisionPattern() string { + if x != nil { + return x.RevisionPattern } - return dAtA[:n], nil + return "" } -func (m *ModifyEnvironmentReply) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.FailedOperations) > 0 { - for _, msg := range m.FailedOperations { - dAtA[i] = 0xa - i++ - i = encodeVarintO2Control(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n - } - } - if len(m.Id) > 0 { - dAtA[i] = 0x12 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.Id))) - i += copy(dAtA[i:], m.Id) - } - if len(m.State) > 0 { - dAtA[i] = 0x1a - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.State))) - i += copy(dAtA[i:], m.State) - } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) - } - return i, nil -} - -func (m *DestroyEnvironmentRequest) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *DestroyEnvironmentRequest) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.Id) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.Id))) - i += copy(dAtA[i:], m.Id) - } - if m.KeepTasks { - dAtA[i] = 0x10 - i++ - if m.KeepTasks { - dAtA[i] = 1 - } else { - dAtA[i] = 0 - } - i++ +func (x *GetWorkflowTemplatesRequest) GetAllBranches() bool { + if x != nil { + return x.AllBranches } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) - } - return i, nil + return false } -func (m *DestroyEnvironmentReply) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err +func (x *GetWorkflowTemplatesRequest) GetAllTags() bool { + if x != nil { + return x.AllTags } - return dAtA[:n], nil + return false } -func (m *DestroyEnvironmentReply) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if m.CleanupTasksReply != nil { - dAtA[i] = 0xa - i++ - i = encodeVarintO2Control(dAtA, i, uint64(m.CleanupTasksReply.Size())) - n7, err7 := m.CleanupTasksReply.MarshalTo(dAtA[i:]) - if err7 != nil { - return 0, err7 - } - i += n7 - } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) - } - return i, nil -} - -func (m *ShortTaskInfo) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *ShortTaskInfo) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.Name) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.Name))) - i += copy(dAtA[i:], m.Name) - } - if m.Locked { - dAtA[i] = 0x10 - i++ - if m.Locked { - dAtA[i] = 1 - } else { - dAtA[i] = 0 - } - i++ - } - if len(m.TaskId) > 0 { - dAtA[i] = 0x1a - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.TaskId))) - i += copy(dAtA[i:], m.TaskId) - } - if len(m.Status) > 0 { - dAtA[i] = 0x22 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.Status))) - i += copy(dAtA[i:], m.Status) - } - if len(m.State) > 0 { - dAtA[i] = 0x2a - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.State))) - i += copy(dAtA[i:], m.State) - } - if len(m.ClassName) > 0 { - dAtA[i] = 0x32 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.ClassName))) - i += copy(dAtA[i:], m.ClassName) - } - if m.DeploymentInfo != nil { - dAtA[i] = 0x3a - i++ - i = encodeVarintO2Control(dAtA, i, uint64(m.DeploymentInfo.Size())) - n8, err8 := m.DeploymentInfo.MarshalTo(dAtA[i:]) - if err8 != nil { - return 0, err8 - } - i += n8 - } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) - } - return i, nil -} - -func (m *TaskDeploymentInfo) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *TaskDeploymentInfo) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.Hostname) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.Hostname))) - i += copy(dAtA[i:], m.Hostname) - } - if len(m.AgentId) > 0 { - dAtA[i] = 0x12 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.AgentId))) - i += copy(dAtA[i:], m.AgentId) - } - if len(m.OfferId) > 0 { - dAtA[i] = 0x1a - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.OfferId))) - i += copy(dAtA[i:], m.OfferId) - } - if len(m.ExecutorId) > 0 { - dAtA[i] = 0x22 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.ExecutorId))) - i += copy(dAtA[i:], m.ExecutorId) - } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) - } - return i, nil -} - -func (m *GetTasksRequest) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *GetTasksRequest) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) - } - return i, nil -} - -func (m *GetTasksReply) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *GetTasksReply) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.Tasks) > 0 { - for _, msg := range m.Tasks { - dAtA[i] = 0xa - i++ - i = encodeVarintO2Control(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n - } +func (x *GetWorkflowTemplatesRequest) GetAllWorkflows() bool { + if x != nil { + return x.AllWorkflows } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) - } - return i, nil + return false } -func (m *GetTaskRequest) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil +type VarSpecMessage struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + DefaultValue string `protobuf:"bytes,1,opt,name=defaultValue,proto3" json:"defaultValue,omitempty"` + Type VarSpecMessage_Type `protobuf:"varint,2,opt,name=type,proto3,enum=o2control.VarSpecMessage_Type" json:"type,omitempty"` + Label string `protobuf:"bytes,3,opt,name=label,proto3" json:"label,omitempty"` + Description string `protobuf:"bytes,4,opt,name=description,proto3" json:"description,omitempty"` + Widget VarSpecMessage_UiWidget `protobuf:"varint,5,opt,name=widget,proto3,enum=o2control.VarSpecMessage_UiWidget" json:"widget,omitempty"` + Panel string `protobuf:"bytes,6,opt,name=panel,proto3" json:"panel,omitempty"` // hint for the UI on where to put or group the given variable input + AllowedValues []string `protobuf:"bytes,7,rep,name=allowedValues,proto3" json:"allowedValues,omitempty"` // list of offered values from which to choose (only for some UiWidgets) + Index int32 `protobuf:"varint,8,opt,name=index,proto3" json:"index,omitempty"` + VisibleIf string `protobuf:"bytes,9,opt,name=visibleIf,proto3" json:"visibleIf,omitempty"` // JS expression that evaluates to bool + EnabledIf string `protobuf:"bytes,10,opt,name=enabledIf,proto3" json:"enabledIf,omitempty"` // JS expression that evaluates to bool + Rows uint32 `protobuf:"varint,11,opt,name=rows,proto3" json:"rows,omitempty"` // this field is used only if widget == editBox } -func (m *GetTaskRequest) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.TaskId) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.TaskId))) - i += copy(dAtA[i:], m.TaskId) +func (x *VarSpecMessage) Reset() { + *x = VarSpecMessage{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[43] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) - } - return i, nil } -func (m *GetTaskReply) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil +func (x *VarSpecMessage) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *GetTaskReply) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if m.Task != nil { - dAtA[i] = 0xa - i++ - i = encodeVarintO2Control(dAtA, i, uint64(m.Task.Size())) - n9, err9 := m.Task.MarshalTo(dAtA[i:]) - if err9 != nil { - return 0, err9 - } - i += n9 - } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) - } - return i, nil -} - -func (m *TaskClassInfo) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *TaskClassInfo) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.Name) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.Name))) - i += copy(dAtA[i:], m.Name) - } - if len(m.ControlMode) > 0 { - dAtA[i] = 0x12 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.ControlMode))) - i += copy(dAtA[i:], m.ControlMode) - } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) - } - return i, nil -} - -func (m *CommandInfo) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *CommandInfo) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.Env) > 0 { - for _, s := range m.Env { - dAtA[i] = 0xa - i++ - l = len(s) - for l >= 1<<7 { - dAtA[i] = uint8(uint64(l)&0x7f | 0x80) - l >>= 7 - i++ - } - dAtA[i] = uint8(l) - i++ - i += copy(dAtA[i:], s) - } - } - if m.Shell { - dAtA[i] = 0x10 - i++ - if m.Shell { - dAtA[i] = 1 - } else { - dAtA[i] = 0 - } - i++ - } - if len(m.Value) > 0 { - dAtA[i] = 0x1a - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.Value))) - i += copy(dAtA[i:], m.Value) - } - if len(m.Arguments) > 0 { - for _, s := range m.Arguments { - dAtA[i] = 0x22 - i++ - l = len(s) - for l >= 1<<7 { - dAtA[i] = uint8(uint64(l)&0x7f | 0x80) - l >>= 7 - i++ - } - dAtA[i] = uint8(l) - i++ - i += copy(dAtA[i:], s) - } - } - if len(m.User) > 0 { - dAtA[i] = 0x2a - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.User))) - i += copy(dAtA[i:], m.User) - } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) - } - return i, nil -} - -func (m *ChannelInfo) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *ChannelInfo) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.Name) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.Name))) - i += copy(dAtA[i:], m.Name) - } - if len(m.Type) > 0 { - dAtA[i] = 0x12 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.Type))) - i += copy(dAtA[i:], m.Type) - } - if len(m.Target) > 0 { - dAtA[i] = 0x1a - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.Target))) - i += copy(dAtA[i:], m.Target) - } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) - } - return i, nil -} - -func (m *TaskInfo) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *TaskInfo) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if m.ShortInfo != nil { - dAtA[i] = 0xa - i++ - i = encodeVarintO2Control(dAtA, i, uint64(m.ShortInfo.Size())) - n10, err10 := m.ShortInfo.MarshalTo(dAtA[i:]) - if err10 != nil { - return 0, err10 - } - i += n10 - } - if m.ClassInfo != nil { - dAtA[i] = 0x12 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(m.ClassInfo.Size())) - n11, err11 := m.ClassInfo.MarshalTo(dAtA[i:]) - if err11 != nil { - return 0, err11 - } - i += n11 - } - if len(m.InboundChannels) > 0 { - for _, msg := range m.InboundChannels { - dAtA[i] = 0x1a - i++ - i = encodeVarintO2Control(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n - } - } - if len(m.OutboundChannels) > 0 { - for _, msg := range m.OutboundChannels { - dAtA[i] = 0x22 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n - } - } - if m.CommandInfo != nil { - dAtA[i] = 0x2a - i++ - i = encodeVarintO2Control(dAtA, i, uint64(m.CommandInfo.Size())) - n12, err12 := m.CommandInfo.MarshalTo(dAtA[i:]) - if err12 != nil { - return 0, err12 - } - i += n12 - } - if len(m.TaskPath) > 0 { - dAtA[i] = 0x32 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.TaskPath))) - i += copy(dAtA[i:], m.TaskPath) - } - if len(m.EnvId) > 0 { - dAtA[i] = 0x3a - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.EnvId))) - i += copy(dAtA[i:], m.EnvId) - } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) - } - return i, nil -} - -func (m *CleanupTasksRequest) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *CleanupTasksRequest) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.TaskIds) > 0 { - for _, s := range m.TaskIds { - dAtA[i] = 0xa - i++ - l = len(s) - for l >= 1<<7 { - dAtA[i] = uint8(uint64(l)&0x7f | 0x80) - l >>= 7 - i++ - } - dAtA[i] = uint8(l) - i++ - i += copy(dAtA[i:], s) +func (*VarSpecMessage) ProtoMessage() {} + +func (x *VarSpecMessage) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[43] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) } + return ms } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) - } - return i, nil + return mi.MessageOf(x) } -func (m *CleanupTasksReply) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil +// Deprecated: Use VarSpecMessage.ProtoReflect.Descriptor instead. +func (*VarSpecMessage) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{43} } -func (m *CleanupTasksReply) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.KilledTasks) > 0 { - for _, msg := range m.KilledTasks { - dAtA[i] = 0xa - i++ - i = encodeVarintO2Control(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n - } - } - if len(m.RunningTasks) > 0 { - for _, msg := range m.RunningTasks { - dAtA[i] = 0x12 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n - } +func (x *VarSpecMessage) GetDefaultValue() string { + if x != nil { + return x.DefaultValue } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) - } - return i, nil -} - -func (m *GetRolesRequest) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *GetRolesRequest) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.EnvId) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.EnvId))) - i += copy(dAtA[i:], m.EnvId) - } - if len(m.PathSpec) > 0 { - dAtA[i] = 0x12 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.PathSpec))) - i += copy(dAtA[i:], m.PathSpec) - } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) - } - return i, nil -} - -func (m *RoleInfo) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *RoleInfo) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.Name) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.Name))) - i += copy(dAtA[i:], m.Name) - } - if len(m.Status) > 0 { - dAtA[i] = 0x12 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.Status))) - i += copy(dAtA[i:], m.Status) - } - if len(m.State) > 0 { - dAtA[i] = 0x1a - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.State))) - i += copy(dAtA[i:], m.State) - } - if len(m.FullPath) > 0 { - dAtA[i] = 0x22 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.FullPath))) - i += copy(dAtA[i:], m.FullPath) - } - if len(m.TaskIds) > 0 { - for _, s := range m.TaskIds { - dAtA[i] = 0x2a - i++ - l = len(s) - for l >= 1<<7 { - dAtA[i] = uint8(uint64(l)&0x7f | 0x80) - l >>= 7 - i++ - } - dAtA[i] = uint8(l) - i++ - i += copy(dAtA[i:], s) - } - } - if len(m.Roles) > 0 { - for _, msg := range m.Roles { - dAtA[i] = 0x32 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n - } - } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) - } - return i, nil + return "" } -func (m *GetRolesReply) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err +func (x *VarSpecMessage) GetType() VarSpecMessage_Type { + if x != nil { + return x.Type } - return dAtA[:n], nil + return VarSpecMessage_string } -func (m *GetRolesReply) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.Roles) > 0 { - for _, msg := range m.Roles { - dAtA[i] = 0xa - i++ - i = encodeVarintO2Control(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n - } +func (x *VarSpecMessage) GetLabel() string { + if x != nil { + return x.Label } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) - } - return i, nil + return "" } -func (m *GetWorkflowTemplatesRequest) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err +func (x *VarSpecMessage) GetDescription() string { + if x != nil { + return x.Description } - return dAtA[:n], nil + return "" } -func (m *GetWorkflowTemplatesRequest) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) +func (x *VarSpecMessage) GetWidget() VarSpecMessage_UiWidget { + if x != nil { + return x.Widget } - return i, nil + return VarSpecMessage_editBox } -func (m *WorkflowTemplateInfo) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err +func (x *VarSpecMessage) GetPanel() string { + if x != nil { + return x.Panel } - return dAtA[:n], nil + return "" } -func (m *WorkflowTemplateInfo) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.Repo) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.Repo))) - i += copy(dAtA[i:], m.Repo) - } - if len(m.Template) > 0 { - dAtA[i] = 0x12 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.Template))) - i += copy(dAtA[i:], m.Template) - } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) +func (x *VarSpecMessage) GetAllowedValues() []string { + if x != nil { + return x.AllowedValues } - return i, nil + return nil } -func (m *GetWorkflowTemplatesReply) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err +func (x *VarSpecMessage) GetIndex() int32 { + if x != nil { + return x.Index } - return dAtA[:n], nil + return 0 } -func (m *GetWorkflowTemplatesReply) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.WorkflowTemplates) > 0 { - for _, msg := range m.WorkflowTemplates { - dAtA[i] = 0xa - i++ - i = encodeVarintO2Control(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n - } +func (x *VarSpecMessage) GetVisibleIf() string { + if x != nil { + return x.VisibleIf } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) - } - return i, nil + return "" } -func (m *ListReposRequest) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err +func (x *VarSpecMessage) GetEnabledIf() string { + if x != nil { + return x.EnabledIf } - return dAtA[:n], nil + return "" } -func (m *ListReposRequest) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) +func (x *VarSpecMessage) GetRows() uint32 { + if x != nil { + return x.Rows } - return i, nil + return 0 } -func (m *RepoInfo) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil +type WorkflowTemplateInfo struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Repo string `protobuf:"bytes,1,opt,name=repo,proto3" json:"repo,omitempty"` + Template string `protobuf:"bytes,2,opt,name=template,proto3" json:"template,omitempty"` + Revision string `protobuf:"bytes,3,opt,name=revision,proto3" json:"revision,omitempty"` + VarSpecMap map[string]*VarSpecMessage `protobuf:"bytes,4,rep,name=varSpecMap,proto3" json:"varSpecMap,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Description string `protobuf:"bytes,5,opt,name=description,proto3" json:"description,omitempty"` } -func (m *RepoInfo) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.Name) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.Name))) - i += copy(dAtA[i:], m.Name) - } - if m.Default { - dAtA[i] = 0x10 - i++ - if m.Default { - dAtA[i] = 1 - } else { - dAtA[i] = 0 - } - i++ - } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) +func (x *WorkflowTemplateInfo) Reset() { + *x = WorkflowTemplateInfo{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[44] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return i, nil } -func (m *ListReposReply) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil +func (x *WorkflowTemplateInfo) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *ListReposReply) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.Repos) > 0 { - for _, msg := range m.Repos { - dAtA[i] = 0xa - i++ - i = encodeVarintO2Control(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n +func (*WorkflowTemplateInfo) ProtoMessage() {} + +func (x *WorkflowTemplateInfo) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[44] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) } + return ms } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) - } - return i, nil + return mi.MessageOf(x) } -func (m *AddRepoRequest) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil +// Deprecated: Use WorkflowTemplateInfo.ProtoReflect.Descriptor instead. +func (*WorkflowTemplateInfo) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{44} } -func (m *AddRepoRequest) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.Name) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.Name))) - i += copy(dAtA[i:], m.Name) +func (x *WorkflowTemplateInfo) GetRepo() string { + if x != nil { + return x.Repo } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) - } - return i, nil + return "" } -func (m *AddRepoReply) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err +func (x *WorkflowTemplateInfo) GetTemplate() string { + if x != nil { + return x.Template } - return dAtA[:n], nil + return "" } -func (m *AddRepoReply) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.ErrorString) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.ErrorString))) - i += copy(dAtA[i:], m.ErrorString) - } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) +func (x *WorkflowTemplateInfo) GetRevision() string { + if x != nil { + return x.Revision } - return i, nil + return "" } -func (m *RemoveRepoRequest) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err +func (x *WorkflowTemplateInfo) GetVarSpecMap() map[string]*VarSpecMessage { + if x != nil { + return x.VarSpecMap } - return dAtA[:n], nil + return nil } -func (m *RemoveRepoRequest) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if m.Index != 0 { - dAtA[i] = 0x8 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(m.Index)) +func (x *WorkflowTemplateInfo) GetDescription() string { + if x != nil { + return x.Description } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) - } - return i, nil + return "" } -func (m *RemoveRepoReply) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil +type GetWorkflowTemplatesReply struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + WorkflowTemplates []*WorkflowTemplateInfo `protobuf:"bytes,1,rep,name=workflowTemplates,proto3" json:"workflowTemplates,omitempty"` + Timestamp int64 `protobuf:"varint,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"` // timestamp of when this object was sent in unix milliseconds } -func (m *RemoveRepoReply) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if m.Ok { - dAtA[i] = 0x8 - i++ - if m.Ok { - dAtA[i] = 1 - } else { - dAtA[i] = 0 - } - i++ +func (x *GetWorkflowTemplatesReply) Reset() { + *x = GetWorkflowTemplatesReply{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[45] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - if len(m.NewDefaultRepo) > 0 { - dAtA[i] = 0x12 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.NewDefaultRepo))) - i += copy(dAtA[i:], m.NewDefaultRepo) - } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) - } - return i, nil } -func (m *RefreshReposRequest) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil +func (x *GetWorkflowTemplatesReply) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *RefreshReposRequest) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if m.Index != 0 { - dAtA[i] = 0x8 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(m.Index)) - } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) +func (*GetWorkflowTemplatesReply) ProtoMessage() {} + +func (x *GetWorkflowTemplatesReply) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[45] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms } - return i, nil + return mi.MessageOf(x) } -func (m *RefreshReposReply) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil +// Deprecated: Use GetWorkflowTemplatesReply.ProtoReflect.Descriptor instead. +func (*GetWorkflowTemplatesReply) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{45} } -func (m *RefreshReposReply) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.ErrorString) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.ErrorString))) - i += copy(dAtA[i:], m.ErrorString) +func (x *GetWorkflowTemplatesReply) GetWorkflowTemplates() []*WorkflowTemplateInfo { + if x != nil { + return x.WorkflowTemplates } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) - } - return i, nil + return nil } -func (m *SetDefaultRepoRequest) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err +func (x *GetWorkflowTemplatesReply) GetTimestamp() int64 { + if x != nil { + return x.Timestamp } - return dAtA[:n], nil + return 0 } -func (m *SetDefaultRepoRequest) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if m.Index != 0 { - dAtA[i] = 0x8 - i++ - i = encodeVarintO2Control(dAtA, i, uint64(m.Index)) - } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) - } - return i, nil +type ListReposRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + GetRevisions bool `protobuf:"varint,1,opt,name=getRevisions,proto3" json:"getRevisions,omitempty"` } -func (m *SetDefaultRepoReply) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err +func (x *ListReposRequest) Reset() { + *x = ListReposRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[46] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return dAtA[:n], nil } -func (m *SetDefaultRepoReply) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.ErrorString) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintO2Control(dAtA, i, uint64(len(m.ErrorString))) - i += copy(dAtA[i:], m.ErrorString) - } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) - } - return i, nil +func (x *ListReposRequest) String() string { + return protoimpl.X.MessageStringOf(x) } -func encodeVarintO2Control(dAtA []byte, offset int, v uint64) int { - for v >= 1<<7 { - dAtA[offset] = uint8(v&0x7f | 0x80) - v >>= 7 - offset++ +func (*ListReposRequest) ProtoMessage() {} + +func (x *ListReposRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[46] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms } - dAtA[offset] = uint8(v) - return offset + 1 + return mi.MessageOf(x) } -func (m *Event_MesosHeartbeat) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n + +// Deprecated: Use ListReposRequest.ProtoReflect.Descriptor instead. +func (*ListReposRequest) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{46} } -func (m *StatusRequest) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) +func (x *ListReposRequest) GetGetRevisions() bool { + if x != nil { + return x.GetRevisions } - return n + return false } -func (m *StatusReply) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.State) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) +type RepoInfo struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Default bool `protobuf:"varint,2,opt,name=default,proto3" json:"default,omitempty"` + DefaultRevision string `protobuf:"bytes,3,opt,name=defaultRevision,proto3" json:"defaultRevision,omitempty"` + Revisions []string `protobuf:"bytes,4,rep,name=revisions,proto3" json:"revisions,omitempty"` +} + +func (x *RepoInfo) Reset() { + *x = RepoInfo{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[47] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - if len(m.StatusUpdates) > 0 { - for _, e := range m.StatusUpdates { - l = e.Size() - n += 1 + l + sovO2Control(uint64(l)) +} + +func (x *RepoInfo) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RepoInfo) ProtoMessage() {} + +func (x *RepoInfo) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[47] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) } + return ms } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n + return mi.MessageOf(x) } -func (m *StatusUpdate) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.Level != 0 { - n += 1 + sovO2Control(uint64(m.Level)) - } - if m.Event != nil { - n += m.Event.Size() - } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n +// Deprecated: Use RepoInfo.ProtoReflect.Descriptor instead. +func (*RepoInfo) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{47} } -func (m *StatusUpdate_MesosHeartbeat) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.MesosHeartbeat != nil { - l = m.MesosHeartbeat.Size() - n += 1 + l + sovO2Control(uint64(l)) +func (x *RepoInfo) GetName() string { + if x != nil { + return x.Name } - return n + return "" } -func (m *GetFrameworkInfoRequest) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) + +func (x *RepoInfo) GetDefault() bool { + if x != nil { + return x.Default } - return n + return false } -func (m *Version) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.Major != 0 { - n += 1 + sovO2Control(uint64(m.Major)) - } - if m.Minor != 0 { - n += 1 + sovO2Control(uint64(m.Minor)) +func (x *RepoInfo) GetDefaultRevision() string { + if x != nil { + return x.DefaultRevision } - if m.Patch != 0 { - n += 1 + sovO2Control(uint64(m.Patch)) - } - l = len(m.Build) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - l = len(m.ProductName) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - l = len(m.VersionStr) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n + return "" } -func (m *GetFrameworkInfoReply) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.FrameworkId) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - if m.EnvironmentsCount != 0 { - n += 1 + sovO2Control(uint64(m.EnvironmentsCount)) +func (x *RepoInfo) GetRevisions() []string { + if x != nil { + return x.Revisions } - if m.TasksCount != 0 { - n += 1 + sovO2Control(uint64(m.TasksCount)) - } - l = len(m.State) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - if m.HostsCount != 0 { - n += 1 + sovO2Control(uint64(m.HostsCount)) - } - l = len(m.InstanceName) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - if m.Version != nil { - l = m.Version.Size() - n += 1 + l + sovO2Control(uint64(l)) - } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n + return nil } -func (m *TeardownRequest) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.Reason) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n +type ListReposReply struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Repos []*RepoInfo `protobuf:"bytes,1,rep,name=repos,proto3" json:"repos,omitempty"` + GlobalDefaultRevision string `protobuf:"bytes,2,opt,name=globalDefaultRevision,proto3" json:"globalDefaultRevision,omitempty"` + Timestamp int64 `protobuf:"varint,3,opt,name=timestamp,proto3" json:"timestamp,omitempty"` // timestamp of when this object was sent in unix milliseconds } -func (m *TeardownReply) Size() (n int) { - if m == nil { - return 0 +func (x *ListReposReply) Reset() { + *x = ListReposReply{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[48] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - var l int - _ = l - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n } -func (m *GetEnvironmentsRequest) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n +func (x *ListReposReply) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *GetEnvironmentsReply) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.FrameworkId) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - if len(m.Environments) > 0 { - for _, e := range m.Environments { - l = e.Size() - n += 1 + l + sovO2Control(uint64(l)) +func (*ListReposReply) ProtoMessage() {} + +func (x *ListReposReply) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[48] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) } + return ms } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n + return mi.MessageOf(x) } -func (m *EnvironmentInfo) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.Id) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - l = len(m.CreatedWhen) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - l = len(m.State) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - if len(m.Tasks) > 0 { - for _, e := range m.Tasks { - l = e.Size() - n += 1 + l + sovO2Control(uint64(l)) - } - } - l = len(m.RootRole) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - if m.CurrentRunNumber != 0 { - n += 1 + sovO2Control(uint64(m.CurrentRunNumber)) - } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n +// Deprecated: Use ListReposReply.ProtoReflect.Descriptor instead. +func (*ListReposReply) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{48} } -func (m *NewEnvironmentRequest) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.WorkflowTemplate) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) +func (x *ListReposReply) GetRepos() []*RepoInfo { + if x != nil { + return x.Repos } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n + return nil } -func (m *NewEnvironmentReply) Size() (n int) { - if m == nil { - return 0 +func (x *ListReposReply) GetGlobalDefaultRevision() string { + if x != nil { + return x.GlobalDefaultRevision } - var l int - _ = l - if m.Environment != nil { - l = m.Environment.Size() - n += 1 + l + sovO2Control(uint64(l)) - } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n + return "" } -func (m *GetEnvironmentRequest) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.Id) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) +func (x *ListReposReply) GetTimestamp() int64 { + if x != nil { + return x.Timestamp } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n + return 0 } -func (m *GetEnvironmentReply) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.Environment != nil { - l = m.Environment.Size() - n += 1 + l + sovO2Control(uint64(l)) - } - if m.Workflow != nil { - l = m.Workflow.Size() - n += 1 + l + sovO2Control(uint64(l)) - } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n +type AddRepoRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + DefaultRevision string `protobuf:"bytes,2,opt,name=defaultRevision,proto3" json:"defaultRevision,omitempty"` } -func (m *ControlEnvironmentRequest) Size() (n int) { - if m == nil { - return 0 +func (x *AddRepoRequest) Reset() { + *x = AddRepoRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[49] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - var l int - _ = l - l = len(m.Id) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - if m.Type != 0 { - n += 1 + sovO2Control(uint64(m.Type)) - } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n } -func (m *ControlEnvironmentReply) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.Id) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - l = len(m.State) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - if m.CurrentRunNumber != 0 { - n += 1 + sovO2Control(uint64(m.CurrentRunNumber)) - } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n +func (x *AddRepoRequest) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *ModifyEnvironmentRequest) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.Id) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - if len(m.Operations) > 0 { - for _, e := range m.Operations { - l = e.Size() - n += 1 + l + sovO2Control(uint64(l)) +func (*AddRepoRequest) ProtoMessage() {} + +func (x *AddRepoRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[49] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) } + return ms } - if m.ReconfigureAll { - n += 2 - } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n + return mi.MessageOf(x) } -func (m *EnvironmentOperation) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.Type != 0 { - n += 1 + sovO2Control(uint64(m.Type)) - } - l = len(m.RoleName) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n +// Deprecated: Use AddRepoRequest.ProtoReflect.Descriptor instead. +func (*AddRepoRequest) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{49} } -func (m *ModifyEnvironmentReply) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if len(m.FailedOperations) > 0 { - for _, e := range m.FailedOperations { - l = e.Size() - n += 1 + l + sovO2Control(uint64(l)) - } +func (x *AddRepoRequest) GetName() string { + if x != nil { + return x.Name } - l = len(m.Id) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - l = len(m.State) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n + return "" } -func (m *DestroyEnvironmentRequest) Size() (n int) { - if m == nil { - return 0 +func (x *AddRepoRequest) GetDefaultRevision() string { + if x != nil { + return x.DefaultRevision } - var l int - _ = l - l = len(m.Id) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - if m.KeepTasks { - n += 2 - } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n + return "" } -func (m *DestroyEnvironmentReply) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.CleanupTasksReply != nil { - l = m.CleanupTasksReply.Size() - n += 1 + l + sovO2Control(uint64(l)) - } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n -} +type AddRepoReply struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields -func (m *ShortTaskInfo) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.Name) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - if m.Locked { - n += 2 - } - l = len(m.TaskId) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - l = len(m.Status) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - l = len(m.State) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - l = len(m.ClassName) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - if m.DeploymentInfo != nil { - l = m.DeploymentInfo.Size() - n += 1 + l + sovO2Control(uint64(l)) - } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n + NewDefaultRevision string `protobuf:"bytes,1,opt,name=newDefaultRevision,proto3" json:"newDefaultRevision,omitempty"` + Info string `protobuf:"bytes,2,opt,name=info,proto3" json:"info,omitempty"` + Timestamp int64 `protobuf:"varint,3,opt,name=timestamp,proto3" json:"timestamp,omitempty"` // timestamp of when this object was sent in unix milliseconds } -func (m *TaskDeploymentInfo) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.Hostname) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - l = len(m.AgentId) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) +func (x *AddRepoReply) Reset() { + *x = AddRepoReply{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[50] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - l = len(m.OfferId) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - l = len(m.ExecutorId) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n } -func (m *GetTasksRequest) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n +func (x *AddRepoReply) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *GetTasksReply) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if len(m.Tasks) > 0 { - for _, e := range m.Tasks { - l = e.Size() - n += 1 + l + sovO2Control(uint64(l)) +func (*AddRepoReply) ProtoMessage() {} + +func (x *AddRepoReply) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[50] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) } + return ms } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n + return mi.MessageOf(x) } -func (m *GetTaskRequest) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.TaskId) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n +// Deprecated: Use AddRepoReply.ProtoReflect.Descriptor instead. +func (*AddRepoReply) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{50} } -func (m *GetTaskReply) Size() (n int) { - if m == nil { - return 0 +func (x *AddRepoReply) GetNewDefaultRevision() string { + if x != nil { + return x.NewDefaultRevision } - var l int - _ = l - if m.Task != nil { - l = m.Task.Size() - n += 1 + l + sovO2Control(uint64(l)) - } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n + return "" } -func (m *TaskClassInfo) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.Name) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) +func (x *AddRepoReply) GetInfo() string { + if x != nil { + return x.Info } - l = len(m.ControlMode) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n + return "" } -func (m *CommandInfo) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if len(m.Env) > 0 { - for _, s := range m.Env { - l = len(s) - n += 1 + l + sovO2Control(uint64(l)) - } - } - if m.Shell { - n += 2 - } - l = len(m.Value) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - if len(m.Arguments) > 0 { - for _, s := range m.Arguments { - l = len(s) - n += 1 + l + sovO2Control(uint64(l)) - } - } - l = len(m.User) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) +func (x *AddRepoReply) GetTimestamp() int64 { + if x != nil { + return x.Timestamp } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n + return 0 } -func (m *ChannelInfo) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.Name) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - l = len(m.Type) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - l = len(m.Target) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n +type RemoveRepoRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Index int32 `protobuf:"varint,1,opt,name=index,proto3" json:"index,omitempty"` } -func (m *TaskInfo) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.ShortInfo != nil { - l = m.ShortInfo.Size() - n += 1 + l + sovO2Control(uint64(l)) - } - if m.ClassInfo != nil { - l = m.ClassInfo.Size() - n += 1 + l + sovO2Control(uint64(l)) - } - if len(m.InboundChannels) > 0 { - for _, e := range m.InboundChannels { - l = e.Size() - n += 1 + l + sovO2Control(uint64(l)) - } - } - if len(m.OutboundChannels) > 0 { - for _, e := range m.OutboundChannels { - l = e.Size() - n += 1 + l + sovO2Control(uint64(l)) - } - } - if m.CommandInfo != nil { - l = m.CommandInfo.Size() - n += 1 + l + sovO2Control(uint64(l)) - } - l = len(m.TaskPath) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - l = len(m.EnvId) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) +func (x *RemoveRepoRequest) Reset() { + *x = RemoveRepoRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[51] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n } -func (m *CleanupTasksRequest) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if len(m.TaskIds) > 0 { - for _, s := range m.TaskIds { - l = len(s) - n += 1 + l + sovO2Control(uint64(l)) - } - } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n +func (x *RemoveRepoRequest) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *CleanupTasksReply) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if len(m.KilledTasks) > 0 { - for _, e := range m.KilledTasks { - l = e.Size() - n += 1 + l + sovO2Control(uint64(l)) - } - } - if len(m.RunningTasks) > 0 { - for _, e := range m.RunningTasks { - l = e.Size() - n += 1 + l + sovO2Control(uint64(l)) +func (*RemoveRepoRequest) ProtoMessage() {} + +func (x *RemoveRepoRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[51] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) } + return ms } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n + return mi.MessageOf(x) } -func (m *GetRolesRequest) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.EnvId) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - l = len(m.PathSpec) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n +// Deprecated: Use RemoveRepoRequest.ProtoReflect.Descriptor instead. +func (*RemoveRepoRequest) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{51} } -func (m *RoleInfo) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.Name) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - l = len(m.Status) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - l = len(m.State) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - l = len(m.FullPath) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - if len(m.TaskIds) > 0 { - for _, s := range m.TaskIds { - l = len(s) - n += 1 + l + sovO2Control(uint64(l)) - } - } - if len(m.Roles) > 0 { - for _, e := range m.Roles { - l = e.Size() - n += 1 + l + sovO2Control(uint64(l)) - } - } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) +func (x *RemoveRepoRequest) GetIndex() int32 { + if x != nil { + return x.Index } - return n + return 0 } -func (m *GetRolesReply) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if len(m.Roles) > 0 { - for _, e := range m.Roles { - l = e.Size() - n += 1 + l + sovO2Control(uint64(l)) - } - } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n +type RemoveRepoReply struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + NewDefaultRepo string `protobuf:"bytes,1,opt,name=newDefaultRepo,proto3" json:"newDefaultRepo,omitempty"` + Timestamp int64 `protobuf:"varint,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"` // timestamp of when this object was sent in unix milliseconds } -func (m *GetWorkflowTemplatesRequest) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) +func (x *RemoveRepoReply) Reset() { + *x = RemoveRepoReply{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[52] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return n } -func (m *WorkflowTemplateInfo) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.Repo) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - l = len(m.Template) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n +func (x *RemoveRepoReply) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *GetWorkflowTemplatesReply) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if len(m.WorkflowTemplates) > 0 { - for _, e := range m.WorkflowTemplates { - l = e.Size() - n += 1 + l + sovO2Control(uint64(l)) +func (*RemoveRepoReply) ProtoMessage() {} + +func (x *RemoveRepoReply) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[52] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) } + return ms } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n + return mi.MessageOf(x) } -func (m *ListReposRequest) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n +// Deprecated: Use RemoveRepoReply.ProtoReflect.Descriptor instead. +func (*RemoveRepoReply) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{52} } -func (m *RepoInfo) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.Name) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - if m.Default { - n += 2 +func (x *RemoveRepoReply) GetNewDefaultRepo() string { + if x != nil { + return x.NewDefaultRepo } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n + return "" } -func (m *ListReposReply) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if len(m.Repos) > 0 { - for _, e := range m.Repos { - l = e.Size() - n += 1 + l + sovO2Control(uint64(l)) - } +func (x *RemoveRepoReply) GetTimestamp() int64 { + if x != nil { + return x.Timestamp } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n + return 0 } -func (m *AddRepoRequest) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.Name) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n -} +type RefreshReposRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields -func (m *AddRepoReply) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.ErrorString) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n + Index int32 `protobuf:"varint,1,opt,name=index,proto3" json:"index,omitempty"` } -func (m *RemoveRepoRequest) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.Index != 0 { - n += 1 + sovO2Control(uint64(m.Index)) - } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) +func (x *RefreshReposRequest) Reset() { + *x = RefreshReposRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[53] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return n } -func (m *RemoveRepoReply) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.Ok { - n += 2 - } - l = len(m.NewDefaultRepo) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n +func (x *RefreshReposRequest) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *RefreshReposRequest) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.Index != 0 { - n += 1 + sovO2Control(uint64(m.Index)) - } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) +func (*RefreshReposRequest) ProtoMessage() {} + +func (x *RefreshReposRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[53] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms } - return n + return mi.MessageOf(x) } -func (m *RefreshReposReply) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.ErrorString) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) - } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n -} - -func (m *SetDefaultRepoRequest) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.Index != 0 { - n += 1 + sovO2Control(uint64(m.Index)) - } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n +// Deprecated: Use RefreshReposRequest.ProtoReflect.Descriptor instead. +func (*RefreshReposRequest) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{53} } -func (m *SetDefaultRepoReply) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.ErrorString) - if l > 0 { - n += 1 + l + sovO2Control(uint64(l)) +func (x *RefreshReposRequest) GetIndex() int32 { + if x != nil { + return x.Index } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n + return 0 } -func sovO2Control(x uint64) (n int) { - for { - n++ - x >>= 7 - if x == 0 { - break - } - } - return n -} -func sozO2Control(x uint64) (n int) { - return sovO2Control(uint64((x << 1) ^ uint64((int64(x) >> 63)))) -} -func (m *Event_MesosHeartbeat) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: Event_MesosHeartbeat: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: Event_MesosHeartbeat: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } +type SetDefaultRepoRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil + Index int32 `protobuf:"varint,1,opt,name=index,proto3" json:"index,omitempty"` } -func (m *StatusRequest) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: StatusRequest: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: StatusRequest: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - if iNdEx > l { - return io.ErrUnexpectedEOF +func (x *SetDefaultRepoRequest) Reset() { + *x = SetDefaultRepoRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[54] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return nil } -func (m *StatusReply) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: StatusReply: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: StatusReply: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field State", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.State = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field StatusUpdates", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.StatusUpdates = append(m.StatusUpdates, &StatusUpdate{}) - if err := m.StatusUpdates[len(m.StatusUpdates)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil +func (x *SetDefaultRepoRequest) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *StatusUpdate) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: StatusUpdate: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: StatusUpdate: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Level", wireType) - } - m.Level = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Level |= StatusUpdate_Level(b&0x7F) << shift - if b < 0x80 { - break - } - } - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field MesosHeartbeat", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - v := &Event_MesosHeartbeat{} - if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - m.Event = &StatusUpdate_MesosHeartbeat{v} - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *GetFrameworkInfoRequest) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: GetFrameworkInfoRequest: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: GetFrameworkInfoRequest: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } +func (*SetDefaultRepoRequest) ProtoMessage() {} - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *Version) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: Version: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: Version: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Major", wireType) - } - m.Major = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Major |= int32(b&0x7F) << shift - if b < 0x80 { - break - } - } - case 2: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Minor", wireType) - } - m.Minor = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Minor |= int32(b&0x7F) << shift - if b < 0x80 { - break - } - } - case 3: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Patch", wireType) - } - m.Patch = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Patch |= int32(b&0x7F) << shift - if b < 0x80 { - break - } - } - case 4: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Build", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Build = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 5: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ProductName", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.ProductName = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 6: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field VersionStr", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.VersionStr = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy +func (x *SetDefaultRepoRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[54] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) } + return ms } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil + return mi.MessageOf(x) } -func (m *GetFrameworkInfoReply) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: GetFrameworkInfoReply: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: GetFrameworkInfoReply: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field FrameworkId", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.FrameworkId = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 2: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field EnvironmentsCount", wireType) - } - m.EnvironmentsCount = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.EnvironmentsCount |= int32(b&0x7F) << shift - if b < 0x80 { - break - } - } - case 3: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field TasksCount", wireType) - } - m.TasksCount = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.TasksCount |= int32(b&0x7F) << shift - if b < 0x80 { - break - } - } - case 4: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field State", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.State = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 5: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field HostsCount", wireType) - } - m.HostsCount = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.HostsCount |= int32(b&0x7F) << shift - if b < 0x80 { - break - } - } - case 6: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field InstanceName", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.InstanceName = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 7: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Version", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.Version == nil { - m.Version = &Version{} - } - if err := m.Version.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil +// Deprecated: Use SetDefaultRepoRequest.ProtoReflect.Descriptor instead. +func (*SetDefaultRepoRequest) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{54} } -func (m *TeardownRequest) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: TeardownRequest: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: TeardownRequest: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Reason", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Reason = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - if iNdEx > l { - return io.ErrUnexpectedEOF +func (x *SetDefaultRepoRequest) GetIndex() int32 { + if x != nil { + return x.Index } - return nil + return 0 } -func (m *TeardownReply) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: TeardownReply: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: TeardownReply: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *GetEnvironmentsRequest) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: GetEnvironmentsRequest: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: GetEnvironmentsRequest: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } +type SetGlobalDefaultRevisionRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil + Revision string `protobuf:"bytes,1,opt,name=revision,proto3" json:"revision,omitempty"` } -func (m *GetEnvironmentsReply) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: GetEnvironmentsReply: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: GetEnvironmentsReply: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field FrameworkId", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.FrameworkId = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Environments", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Environments = append(m.Environments, &EnvironmentInfo{}) - if err := m.Environments[len(m.Environments)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - if iNdEx > l { - return io.ErrUnexpectedEOF +func (x *SetGlobalDefaultRevisionRequest) Reset() { + *x = SetGlobalDefaultRevisionRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[55] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return nil } -func (m *EnvironmentInfo) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: EnvironmentInfo: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: EnvironmentInfo: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Id = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field CreatedWhen", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.CreatedWhen = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 3: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field State", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.State = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 4: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Tasks", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Tasks = append(m.Tasks, &ShortTaskInfo{}) - if err := m.Tasks[len(m.Tasks)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 5: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field RootRole", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.RootRole = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 6: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field CurrentRunNumber", wireType) - } - m.CurrentRunNumber = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.CurrentRunNumber |= uint32(b&0x7F) << shift - if b < 0x80 { - break - } - } - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *NewEnvironmentRequest) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: NewEnvironmentRequest: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: NewEnvironmentRequest: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field WorkflowTemplate", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.WorkflowTemplate = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *NewEnvironmentReply) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: NewEnvironmentReply: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: NewEnvironmentReply: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Environment", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.Environment == nil { - m.Environment = &EnvironmentInfo{} - } - if err := m.Environment.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *GetEnvironmentRequest) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: GetEnvironmentRequest: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: GetEnvironmentRequest: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Id = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *GetEnvironmentReply) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: GetEnvironmentReply: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: GetEnvironmentReply: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Environment", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.Environment == nil { - m.Environment = &EnvironmentInfo{} - } - if err := m.Environment.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Workflow", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.Workflow == nil { - m.Workflow = &RoleInfo{} - } - if err := m.Workflow.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *ControlEnvironmentRequest) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: ControlEnvironmentRequest: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: ControlEnvironmentRequest: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Id = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 2: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) - } - m.Type = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Type |= ControlEnvironmentRequest_Optype(b&0x7F) << shift - if b < 0x80 { - break - } - } - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *ControlEnvironmentReply) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: ControlEnvironmentReply: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: ControlEnvironmentReply: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Id = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field State", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.State = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 3: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field CurrentRunNumber", wireType) - } - m.CurrentRunNumber = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.CurrentRunNumber |= uint32(b&0x7F) << shift - if b < 0x80 { - break - } - } - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *ModifyEnvironmentRequest) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: ModifyEnvironmentRequest: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: ModifyEnvironmentRequest: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Id = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Operations", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Operations = append(m.Operations, &EnvironmentOperation{}) - if err := m.Operations[len(m.Operations)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 3: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field ReconfigureAll", wireType) - } - var v int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - v |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - m.ReconfigureAll = bool(v != 0) - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *EnvironmentOperation) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: EnvironmentOperation: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: EnvironmentOperation: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) - } - m.Type = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Type |= EnvironmentOperation_Optype(b&0x7F) << shift - if b < 0x80 { - break - } - } - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field RoleName", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.RoleName = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *ModifyEnvironmentReply) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: ModifyEnvironmentReply: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: ModifyEnvironmentReply: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field FailedOperations", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.FailedOperations = append(m.FailedOperations, &EnvironmentOperation{}) - if err := m.FailedOperations[len(m.FailedOperations)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Id = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 3: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field State", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.State = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *DestroyEnvironmentRequest) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: DestroyEnvironmentRequest: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: DestroyEnvironmentRequest: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Id = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 2: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field KeepTasks", wireType) - } - var v int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - v |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - m.KeepTasks = bool(v != 0) - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *DestroyEnvironmentReply) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: DestroyEnvironmentReply: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: DestroyEnvironmentReply: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field CleanupTasksReply", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.CleanupTasksReply == nil { - m.CleanupTasksReply = &CleanupTasksReply{} - } - if err := m.CleanupTasksReply.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *ShortTaskInfo) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: ShortTaskInfo: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: ShortTaskInfo: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Name = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 2: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Locked", wireType) - } - var v int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - v |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - m.Locked = bool(v != 0) - case 3: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field TaskId", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.TaskId = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 4: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Status", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Status = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 5: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field State", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.State = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 6: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ClassName", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.ClassName = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 7: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field DeploymentInfo", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.DeploymentInfo == nil { - m.DeploymentInfo = &TaskDeploymentInfo{} - } - if err := m.DeploymentInfo.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *TaskDeploymentInfo) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: TaskDeploymentInfo: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: TaskDeploymentInfo: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Hostname", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Hostname = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field AgentId", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.AgentId = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 3: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field OfferId", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.OfferId = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 4: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ExecutorId", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.ExecutorId = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *GetTasksRequest) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: GetTasksRequest: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: GetTasksRequest: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *GetTasksReply) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: GetTasksReply: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: GetTasksReply: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Tasks", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Tasks = append(m.Tasks, &ShortTaskInfo{}) - if err := m.Tasks[len(m.Tasks)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *GetTaskRequest) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: GetTaskRequest: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: GetTaskRequest: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field TaskId", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.TaskId = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *GetTaskReply) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: GetTaskReply: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: GetTaskReply: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Task", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.Task == nil { - m.Task = &TaskInfo{} - } - if err := m.Task.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *TaskClassInfo) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: TaskClassInfo: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: TaskClassInfo: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Name = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ControlMode", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.ControlMode = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *CommandInfo) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: CommandInfo: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: CommandInfo: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Env", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Env = append(m.Env, string(dAtA[iNdEx:postIndex])) - iNdEx = postIndex - case 2: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Shell", wireType) - } - var v int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - v |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - m.Shell = bool(v != 0) - case 3: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Value", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Value = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 4: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Arguments", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Arguments = append(m.Arguments, string(dAtA[iNdEx:postIndex])) - iNdEx = postIndex - case 5: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field User", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.User = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *ChannelInfo) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: ChannelInfo: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: ChannelInfo: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Name = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Type = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 3: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Target", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Target = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *TaskInfo) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: TaskInfo: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: TaskInfo: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ShortInfo", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.ShortInfo == nil { - m.ShortInfo = &ShortTaskInfo{} - } - if err := m.ShortInfo.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ClassInfo", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.ClassInfo == nil { - m.ClassInfo = &TaskClassInfo{} - } - if err := m.ClassInfo.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 3: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field InboundChannels", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.InboundChannels = append(m.InboundChannels, &ChannelInfo{}) - if err := m.InboundChannels[len(m.InboundChannels)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 4: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field OutboundChannels", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.OutboundChannels = append(m.OutboundChannels, &ChannelInfo{}) - if err := m.OutboundChannels[len(m.OutboundChannels)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 5: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field CommandInfo", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.CommandInfo == nil { - m.CommandInfo = &CommandInfo{} - } - if err := m.CommandInfo.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 6: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field TaskPath", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.TaskPath = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 7: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field EnvId", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.EnvId = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *CleanupTasksRequest) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: CleanupTasksRequest: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: CleanupTasksRequest: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field TaskIds", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.TaskIds = append(m.TaskIds, string(dAtA[iNdEx:postIndex])) - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *CleanupTasksReply) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: CleanupTasksReply: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: CleanupTasksReply: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field KilledTasks", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.KilledTasks = append(m.KilledTasks, &ShortTaskInfo{}) - if err := m.KilledTasks[len(m.KilledTasks)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field RunningTasks", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.RunningTasks = append(m.RunningTasks, &ShortTaskInfo{}) - if err := m.RunningTasks[len(m.RunningTasks)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *GetRolesRequest) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: GetRolesRequest: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: GetRolesRequest: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field EnvId", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.EnvId = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field PathSpec", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.PathSpec = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *RoleInfo) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: RoleInfo: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: RoleInfo: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Name = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Status", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Status = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 3: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field State", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.State = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 4: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field FullPath", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.FullPath = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 5: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field TaskIds", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.TaskIds = append(m.TaskIds, string(dAtA[iNdEx:postIndex])) - iNdEx = postIndex - case 6: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Roles", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Roles = append(m.Roles, &RoleInfo{}) - if err := m.Roles[len(m.Roles)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *GetRolesReply) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: GetRolesReply: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: GetRolesReply: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Roles", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Roles = append(m.Roles, &RoleInfo{}) - if err := m.Roles[len(m.Roles)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *GetWorkflowTemplatesRequest) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: GetWorkflowTemplatesRequest: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: GetWorkflowTemplatesRequest: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *WorkflowTemplateInfo) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: WorkflowTemplateInfo: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: WorkflowTemplateInfo: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Repo", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Repo = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Template", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Template = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *GetWorkflowTemplatesReply) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: GetWorkflowTemplatesReply: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: GetWorkflowTemplatesReply: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field WorkflowTemplates", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.WorkflowTemplates = append(m.WorkflowTemplates, &WorkflowTemplateInfo{}) - if err := m.WorkflowTemplates[len(m.WorkflowTemplates)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *ListReposRequest) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: ListReposRequest: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: ListReposRequest: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy + +func (x *SetGlobalDefaultRevisionRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SetGlobalDefaultRevisionRequest) ProtoMessage() {} + +func (x *SetGlobalDefaultRevisionRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[55] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) } + return ms } + return mi.MessageOf(x) +} - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil +// Deprecated: Use SetGlobalDefaultRevisionRequest.ProtoReflect.Descriptor instead. +func (*SetGlobalDefaultRevisionRequest) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{55} } -func (m *RepoInfo) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: RepoInfo: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: RepoInfo: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Name = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 2: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Default", wireType) - } - var v int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - v |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - m.Default = bool(v != 0) - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } + +func (x *SetGlobalDefaultRevisionRequest) GetRevision() string { + if x != nil { + return x.Revision } + return "" +} - if iNdEx > l { - return io.ErrUnexpectedEOF +type SetRepoDefaultRevisionRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Index int32 `protobuf:"varint,1,opt,name=index,proto3" json:"index,omitempty"` + Revision string `protobuf:"bytes,2,opt,name=revision,proto3" json:"revision,omitempty"` +} + +func (x *SetRepoDefaultRevisionRequest) Reset() { + *x = SetRepoDefaultRevisionRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[56] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return nil } -func (m *ListReposReply) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: ListReposReply: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: ListReposReply: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Repos", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Repos = append(m.Repos, &RepoInfo{}) - if err := m.Repos[len(m.Repos)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy + +func (x *SetRepoDefaultRevisionRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SetRepoDefaultRevisionRequest) ProtoMessage() {} + +func (x *SetRepoDefaultRevisionRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[56] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) } + return ms } + return mi.MessageOf(x) +} + +// Deprecated: Use SetRepoDefaultRevisionRequest.ProtoReflect.Descriptor instead. +func (*SetRepoDefaultRevisionRequest) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{56} +} - if iNdEx > l { - return io.ErrUnexpectedEOF +func (x *SetRepoDefaultRevisionRequest) GetIndex() int32 { + if x != nil { + return x.Index } - return nil + return 0 } -func (m *AddRepoRequest) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: AddRepoRequest: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: AddRepoRequest: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Name = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } + +func (x *SetRepoDefaultRevisionRequest) GetRevision() string { + if x != nil { + return x.Revision } + return "" +} - if iNdEx > l { - return io.ErrUnexpectedEOF +type SetRepoDefaultRevisionReply struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Info string `protobuf:"bytes,1,opt,name=info,proto3" json:"info,omitempty"` + Timestamp int64 `protobuf:"varint,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"` // timestamp of when this object was sent in unix milliseconds +} + +func (x *SetRepoDefaultRevisionReply) Reset() { + *x = SetRepoDefaultRevisionReply{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[57] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return nil } -func (m *AddRepoReply) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: AddRepoReply: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: AddRepoReply: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ErrorString", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.ErrorString = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy + +func (x *SetRepoDefaultRevisionReply) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SetRepoDefaultRevisionReply) ProtoMessage() {} + +func (x *SetRepoDefaultRevisionReply) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[57] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) } + return ms } + return mi.MessageOf(x) +} - if iNdEx > l { - return io.ErrUnexpectedEOF +// Deprecated: Use SetRepoDefaultRevisionReply.ProtoReflect.Descriptor instead. +func (*SetRepoDefaultRevisionReply) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{57} +} + +func (x *SetRepoDefaultRevisionReply) GetInfo() string { + if x != nil { + return x.Info } - return nil + return "" } -func (m *RemoveRepoRequest) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: RemoveRepoRequest: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: RemoveRepoRequest: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Index", wireType) - } - m.Index = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Index |= int32(b&0x7F) << shift - if b < 0x80 { - break - } - } - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } + +func (x *SetRepoDefaultRevisionReply) GetTimestamp() int64 { + if x != nil { + return x.Timestamp } + return 0 +} - if iNdEx > l { - return io.ErrUnexpectedEOF +type Empty struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *Empty) Reset() { + *x = Empty{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[58] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return nil } -func (m *RemoveRepoReply) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: RemoveRepoReply: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: RemoveRepoReply: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Ok", wireType) - } - var v int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - v |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - m.Ok = bool(v != 0) - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field NewDefaultRepo", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.NewDefaultRepo = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy + +func (x *Empty) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Empty) ProtoMessage() {} + +func (x *Empty) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[58] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) } + return ms } + return mi.MessageOf(x) +} + +// Deprecated: Use Empty.ProtoReflect.Descriptor instead. +func (*Empty) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{58} +} + +type ListIntegratedServicesReply struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields - if iNdEx > l { - return io.ErrUnexpectedEOF + Services map[string]*IntegratedServiceInfo `protobuf:"bytes,1,rep,name=services,proto3" json:"services,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` // keys are IDs (e.g. "ddsched"), the service name should be displayed to users instead + Timestamp int64 `protobuf:"varint,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"` // timestamp of when this object was sent in unix milliseconds +} + +func (x *ListIntegratedServicesReply) Reset() { + *x = ListIntegratedServicesReply{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[59] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return nil } -func (m *RefreshReposRequest) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: RefreshReposRequest: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: RefreshReposRequest: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Index", wireType) - } - m.Index = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Index |= int32(b&0x7F) << shift - if b < 0x80 { - break - } - } - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy + +func (x *ListIntegratedServicesReply) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListIntegratedServicesReply) ProtoMessage() {} + +func (x *ListIntegratedServicesReply) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[59] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) } + return ms } + return mi.MessageOf(x) +} - if iNdEx > l { - return io.ErrUnexpectedEOF +// Deprecated: Use ListIntegratedServicesReply.ProtoReflect.Descriptor instead. +func (*ListIntegratedServicesReply) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{59} +} + +func (x *ListIntegratedServicesReply) GetServices() map[string]*IntegratedServiceInfo { + if x != nil { + return x.Services } return nil } -func (m *RefreshReposReply) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: RefreshReposReply: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: RefreshReposReply: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ErrorString", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.ErrorString = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } + +func (x *ListIntegratedServicesReply) GetTimestamp() int64 { + if x != nil { + return x.Timestamp } + return 0 +} + +type IntegratedServiceInfo struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` // user-visible service name, e.g. "DD scheduler" + Enabled bool `protobuf:"varint,2,opt,name=enabled,proto3" json:"enabled,omitempty"` + Endpoint string `protobuf:"bytes,3,opt,name=endpoint,proto3" json:"endpoint,omitempty"` + ConnectionState string `protobuf:"bytes,4,opt,name=connectionState,proto3" json:"connectionState,omitempty"` // allowed values: READY, CONNECTING, TRANSIENT_FAILURE, IDLE, SHUTDOWN + Data string `protobuf:"bytes,5,opt,name=data,proto3" json:"data,omitempty"` // always a JSON payload with a map inside. +} - if iNdEx > l { - return io.ErrUnexpectedEOF +func (x *IntegratedServiceInfo) Reset() { + *x = IntegratedServiceInfo{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_o2control_proto_msgTypes[60] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return nil } -func (m *SetDefaultRepoRequest) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: SetDefaultRepoRequest: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: SetDefaultRepoRequest: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Index", wireType) - } - m.Index = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Index |= int32(b&0x7F) << shift - if b < 0x80 { - break - } - } - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy + +func (x *IntegratedServiceInfo) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*IntegratedServiceInfo) ProtoMessage() {} + +func (x *IntegratedServiceInfo) ProtoReflect() protoreflect.Message { + mi := &file_protos_o2control_proto_msgTypes[60] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) } + return ms } + return mi.MessageOf(x) +} + +// Deprecated: Use IntegratedServiceInfo.ProtoReflect.Descriptor instead. +func (*IntegratedServiceInfo) Descriptor() ([]byte, []int) { + return file_protos_o2control_proto_rawDescGZIP(), []int{60} +} - if iNdEx > l { - return io.ErrUnexpectedEOF +func (x *IntegratedServiceInfo) GetName() string { + if x != nil { + return x.Name } - return nil + return "" } -func (m *SetDefaultRepoReply) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: SetDefaultRepoReply: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: SetDefaultRepoReply: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ErrorString", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowO2Control - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthO2Control - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthO2Control - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.ErrorString = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipO2Control(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthO2Control - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } + +func (x *IntegratedServiceInfo) GetEnabled() bool { + if x != nil { + return x.Enabled } + return false +} - if iNdEx > l { - return io.ErrUnexpectedEOF +func (x *IntegratedServiceInfo) GetEndpoint() string { + if x != nil { + return x.Endpoint } - return nil + return "" } -func skipO2Control(dAtA []byte) (n int, err error) { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowO2Control - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - wireType := int(wire & 0x7) - switch wireType { - case 0: - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowO2Control - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - iNdEx++ - if dAtA[iNdEx-1] < 0x80 { - break - } - } - return iNdEx, nil - case 1: - iNdEx += 8 - return iNdEx, nil - case 2: - var length int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowO2Control - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - length |= (int(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - if length < 0 { - return 0, ErrInvalidLengthO2Control - } - iNdEx += length - if iNdEx < 0 { - return 0, ErrInvalidLengthO2Control - } - return iNdEx, nil - case 3: - for { - var innerWire uint64 - var start int = iNdEx - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowO2Control - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - innerWire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - innerWireType := int(innerWire & 0x7) - if innerWireType == 4 { - break - } - next, err := skipO2Control(dAtA[start:]) - if err != nil { - return 0, err - } - iNdEx = start + next - if iNdEx < 0 { - return 0, ErrInvalidLengthO2Control - } - } - return iNdEx, nil - case 4: - return iNdEx, nil - case 5: - iNdEx += 4 - return iNdEx, nil - default: - return 0, fmt.Errorf("proto: illegal wireType %d", wireType) - } + +func (x *IntegratedServiceInfo) GetConnectionState() string { + if x != nil { + return x.ConnectionState + } + return "" +} + +func (x *IntegratedServiceInfo) GetData() string { + if x != nil { + return x.Data } - panic("unreachable") + return "" +} + +var File_protos_o2control_proto protoreflect.FileDescriptor + +var file_protos_o2control_proto_rawDesc = []byte{ + 0x0a, 0x16, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, + 0x6f, 0x6c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x09, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, + 0x72, 0x6f, 0x6c, 0x1a, 0x13, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x65, 0x76, 0x65, 0x6e, + 0x74, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x22, 0x0a, 0x10, 0x53, 0x75, 0x62, 0x73, + 0x63, 0x72, 0x69, 0x62, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x19, 0x0a, 0x17, + 0x47, 0x65, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x6e, 0x66, 0x6f, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xa3, 0x01, 0x0a, 0x07, 0x56, 0x65, 0x72, 0x73, + 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x05, 0x52, 0x05, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x6d, 0x69, 0x6e, + 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x6d, 0x69, 0x6e, 0x6f, 0x72, 0x12, + 0x14, 0x0a, 0x05, 0x70, 0x61, 0x74, 0x63, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, + 0x70, 0x61, 0x74, 0x63, 0x68, 0x12, 0x14, 0x0a, 0x05, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x12, 0x20, 0x0a, 0x0b, 0x70, + 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, + 0x0a, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0a, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x72, 0x22, 0xef, 0x03, + 0x0a, 0x15, 0x47, 0x65, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x6e, + 0x66, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x20, 0x0a, 0x0b, 0x66, 0x72, 0x61, 0x6d, 0x65, + 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x66, 0x72, + 0x61, 0x6d, 0x65, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x64, 0x12, 0x2c, 0x0a, 0x11, 0x65, 0x6e, 0x76, + 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x05, 0x52, 0x11, 0x65, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, + 0x74, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x74, 0x61, 0x73, 0x6b, 0x73, + 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x74, 0x61, 0x73, + 0x6b, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1e, 0x0a, + 0x0a, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x0a, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x22, 0x0a, + 0x0c, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x06, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0c, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x4e, 0x61, 0x6d, + 0x65, 0x12, 0x2c, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x56, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, + 0x34, 0x0a, 0x15, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x15, + 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x6e, 0x64, + 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x30, 0x0a, 0x13, 0x64, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, + 0x72, 0x73, 0x49, 0x6e, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x09, 0x20, 0x03, + 0x28, 0x09, 0x52, 0x13, 0x64, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x49, 0x6e, 0x49, + 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x28, 0x0a, 0x0f, 0x61, 0x63, 0x74, 0x69, 0x76, + 0x65, 0x44, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x09, + 0x52, 0x0f, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x44, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, + 0x73, 0x12, 0x2e, 0x0a, 0x12, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x44, 0x65, + 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x09, 0x52, 0x12, 0x61, + 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x44, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, + 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x0c, + 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, + 0x29, 0x0a, 0x0f, 0x54, 0x65, 0x61, 0x72, 0x64, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x22, 0x0f, 0x0a, 0x0d, 0x54, 0x65, + 0x61, 0x72, 0x64, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0xa0, 0x01, 0x0a, 0x16, + 0x47, 0x65, 0x74, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x68, 0x6f, 0x77, 0x41, 0x6c, + 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, 0x68, 0x6f, 0x77, 0x41, 0x6c, 0x6c, + 0x12, 0x24, 0x0a, 0x0d, 0x73, 0x68, 0x6f, 0x77, 0x54, 0x61, 0x73, 0x6b, 0x49, 0x6e, 0x66, 0x6f, + 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x73, 0x68, 0x6f, 0x77, 0x54, 0x61, 0x73, + 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x73, 0x12, 0x46, 0x0a, 0x1e, 0x73, 0x68, 0x6f, 0x77, 0x44, 0x65, + 0x74, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x65, 0x64, + 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1e, + 0x73, 0x68, 0x6f, 0x77, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x49, 0x6e, 0x74, 0x65, + 0x67, 0x72, 0x61, 0x74, 0x65, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x22, 0x96, + 0x01, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, + 0x74, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x20, 0x0a, 0x0b, 0x66, 0x72, 0x61, 0x6d, 0x65, + 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x66, 0x72, + 0x61, 0x6d, 0x65, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x64, 0x12, 0x3e, 0x0a, 0x0c, 0x65, 0x6e, 0x76, + 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x1a, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x45, 0x6e, 0x76, 0x69, + 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0c, 0x65, 0x6e, 0x76, + 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0xdb, 0x08, 0x0a, 0x0f, 0x45, 0x6e, 0x76, 0x69, + 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x0e, 0x0a, 0x02, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x20, 0x0a, 0x0b, 0x63, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x57, 0x68, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x57, 0x68, 0x65, 0x6e, 0x12, 0x14, 0x0a, + 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, + 0x61, 0x74, 0x65, 0x12, 0x2e, 0x0a, 0x05, 0x74, 0x61, 0x73, 0x6b, 0x73, 0x18, 0x04, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x53, + 0x68, 0x6f, 0x72, 0x74, 0x54, 0x61, 0x73, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x05, 0x74, 0x61, + 0x73, 0x6b, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x6f, 0x6f, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x6f, 0x6f, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x12, + 0x2a, 0x0a, 0x10, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x52, 0x75, 0x6e, 0x4e, 0x75, 0x6d, + 0x62, 0x65, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x10, 0x63, 0x75, 0x72, 0x72, 0x65, + 0x6e, 0x74, 0x52, 0x75, 0x6e, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x44, 0x0a, 0x08, 0x64, + 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28, 0x2e, + 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, + 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, + 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, + 0x73, 0x12, 0x38, 0x0a, 0x04, 0x76, 0x61, 0x72, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x24, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x45, 0x6e, 0x76, 0x69, + 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x56, 0x61, 0x72, 0x73, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x04, 0x76, 0x61, 0x72, 0x73, 0x12, 0x44, 0x0a, 0x08, 0x75, + 0x73, 0x65, 0x72, 0x56, 0x61, 0x72, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28, 0x2e, + 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, + 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x56, 0x61, + 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x56, 0x61, 0x72, + 0x73, 0x12, 0x22, 0x0a, 0x0c, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x4f, 0x66, 0x46, 0x6c, 0x70, + 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x4f, + 0x66, 0x46, 0x6c, 0x70, 0x73, 0x12, 0x2c, 0x0a, 0x11, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, + 0x64, 0x44, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x09, + 0x52, 0x11, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x64, 0x44, 0x65, 0x74, 0x65, 0x63, 0x74, + 0x6f, 0x72, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x24, 0x0a, 0x0d, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x4f, + 0x66, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, 0x6e, 0x75, + 0x6d, 0x62, 0x65, 0x72, 0x4f, 0x66, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x12, 0x6e, 0x0a, 0x16, 0x69, + 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x65, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x73, 0x44, 0x61, 0x74, 0x61, 0x18, 0x0e, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x36, 0x2e, 0x6f, 0x32, + 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, + 0x65, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, + 0x65, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x44, 0x61, 0x74, 0x61, 0x45, 0x6e, + 0x74, 0x72, 0x79, 0x52, 0x16, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x65, 0x64, 0x53, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x44, 0x61, 0x74, 0x61, 0x12, 0x24, 0x0a, 0x0d, 0x6e, + 0x75, 0x6d, 0x62, 0x65, 0x72, 0x4f, 0x66, 0x54, 0x61, 0x73, 0x6b, 0x73, 0x18, 0x0f, 0x20, 0x01, + 0x28, 0x05, 0x52, 0x0d, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x4f, 0x66, 0x54, 0x61, 0x73, 0x6b, + 0x73, 0x12, 0x2c, 0x0a, 0x11, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x54, 0x72, 0x61, 0x6e, + 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x10, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x63, 0x75, + 0x72, 0x72, 0x65, 0x6e, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x30, 0x0a, 0x13, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x4f, 0x66, 0x41, 0x63, 0x74, 0x69, 0x76, + 0x65, 0x54, 0x61, 0x73, 0x6b, 0x73, 0x18, 0x11, 0x20, 0x01, 0x28, 0x05, 0x52, 0x13, 0x6e, 0x75, + 0x6d, 0x62, 0x65, 0x72, 0x4f, 0x66, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x54, 0x61, 0x73, 0x6b, + 0x73, 0x12, 0x34, 0x0a, 0x15, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x4f, 0x66, 0x49, 0x6e, 0x61, + 0x63, 0x74, 0x69, 0x76, 0x65, 0x54, 0x61, 0x73, 0x6b, 0x73, 0x18, 0x12, 0x20, 0x01, 0x28, 0x05, + 0x52, 0x15, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x4f, 0x66, 0x49, 0x6e, 0x61, 0x63, 0x74, 0x69, + 0x76, 0x65, 0x54, 0x61, 0x73, 0x6b, 0x73, 0x1a, 0x3b, 0x0a, 0x0d, 0x44, 0x65, 0x66, 0x61, 0x75, + 0x6c, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x37, 0x0a, 0x09, 0x56, 0x61, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x3b, 0x0a, + 0x0d, 0x55, 0x73, 0x65, 0x72, 0x56, 0x61, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, + 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, + 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x49, 0x0a, 0x1b, 0x49, 0x6e, + 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x65, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, + 0x44, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xac, 0x02, 0x0a, 0x15, 0x4e, 0x65, 0x77, 0x45, 0x6e, 0x76, + 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x2a, 0x0a, 0x10, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x54, 0x65, 0x6d, 0x70, 0x6c, + 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x77, 0x6f, 0x72, 0x6b, 0x66, + 0x6c, 0x6f, 0x77, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x12, 0x3e, 0x0a, 0x04, 0x76, + 0x61, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x6f, 0x32, 0x63, 0x6f, + 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x4e, 0x65, 0x77, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, + 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x56, 0x61, 0x72, 0x73, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x04, 0x76, 0x61, 0x72, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x70, + 0x75, 0x62, 0x6c, 0x69, 0x63, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x70, 0x75, 0x62, + 0x6c, 0x69, 0x63, 0x12, 0x26, 0x0a, 0x0e, 0x61, 0x75, 0x74, 0x6f, 0x54, 0x72, 0x61, 0x6e, 0x73, + 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x61, 0x75, 0x74, + 0x6f, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2e, 0x0a, 0x0b, 0x72, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x55, 0x73, 0x65, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x0c, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x0b, + 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x55, 0x73, 0x65, 0x72, 0x1a, 0x37, 0x0a, 0x09, 0x56, + 0x61, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x3a, 0x02, 0x38, 0x01, 0x22, 0x89, 0x01, 0x0a, 0x13, 0x4e, 0x65, 0x77, 0x45, 0x6e, 0x76, 0x69, + 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x3c, 0x0a, 0x0b, + 0x65, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1a, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x45, 0x6e, + 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0b, 0x65, + 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x75, + 0x62, 0x6c, 0x69, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x70, 0x75, 0x62, 0x6c, + 0x69, 0x63, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, + 0x22, 0x84, 0x02, 0x0a, 0x19, 0x4e, 0x65, 0x77, 0x41, 0x75, 0x74, 0x6f, 0x45, 0x6e, 0x76, 0x69, + 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2a, + 0x0a, 0x10, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, + 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, + 0x6f, 0x77, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x12, 0x42, 0x0a, 0x04, 0x76, 0x61, + 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, + 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x4e, 0x65, 0x77, 0x41, 0x75, 0x74, 0x6f, 0x45, 0x6e, 0x76, 0x69, + 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x56, + 0x61, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x04, 0x76, 0x61, 0x72, 0x73, 0x12, 0x0e, + 0x0a, 0x02, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x2e, + 0x0a, 0x0b, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x55, 0x73, 0x65, 0x72, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x55, 0x73, 0x65, + 0x72, 0x52, 0x0b, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x55, 0x73, 0x65, 0x72, 0x1a, 0x37, + 0x0a, 0x09, 0x56, 0x61, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, + 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x37, 0x0a, 0x17, 0x4e, 0x65, 0x77, 0x41, 0x75, + 0x74, 0x6f, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x70, + 0x6c, 0x79, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, + 0x22, 0x53, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, + 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x2a, 0x0a, 0x10, 0x73, 0x68, 0x6f, + 0x77, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x54, 0x72, 0x65, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x10, 0x73, 0x68, 0x6f, 0x77, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, + 0x77, 0x54, 0x72, 0x65, 0x65, 0x22, 0xba, 0x01, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x45, 0x6e, 0x76, + 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x3c, 0x0a, + 0x0b, 0x65, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x45, + 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0b, + 0x65, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x2f, 0x0a, 0x08, 0x77, + 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, + 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x52, 0x6f, 0x6c, 0x65, 0x49, 0x6e, + 0x66, 0x6f, 0x52, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x12, 0x16, 0x0a, 0x06, + 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x70, 0x75, + 0x62, 0x6c, 0x69, 0x63, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, + 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, + 0x6d, 0x70, 0x22, 0x8b, 0x02, 0x0a, 0x19, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x45, 0x6e, + 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, + 0x12, 0x3f, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2b, + 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x72, + 0x6f, 0x6c, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x2e, 0x4f, 0x70, 0x74, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, + 0x65, 0x12, 0x2e, 0x0a, 0x0b, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x55, 0x73, 0x65, 0x72, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, + 0x55, 0x73, 0x65, 0x72, 0x52, 0x0b, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x55, 0x73, 0x65, + 0x72, 0x22, 0x6d, 0x0a, 0x06, 0x4f, 0x70, 0x74, 0x79, 0x70, 0x65, 0x12, 0x08, 0x0a, 0x04, 0x4e, + 0x4f, 0x4f, 0x50, 0x10, 0x00, 0x12, 0x12, 0x0a, 0x0e, 0x53, 0x54, 0x41, 0x52, 0x54, 0x5f, 0x41, + 0x43, 0x54, 0x49, 0x56, 0x49, 0x54, 0x59, 0x10, 0x01, 0x12, 0x11, 0x0a, 0x0d, 0x53, 0x54, 0x4f, + 0x50, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x49, 0x54, 0x59, 0x10, 0x02, 0x12, 0x0d, 0x0a, 0x09, + 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x55, 0x52, 0x45, 0x10, 0x03, 0x12, 0x09, 0x0a, 0x05, 0x52, + 0x45, 0x53, 0x45, 0x54, 0x10, 0x04, 0x12, 0x0c, 0x0a, 0x08, 0x47, 0x4f, 0x5f, 0x45, 0x52, 0x52, + 0x4f, 0x52, 0x10, 0x05, 0x12, 0x0a, 0x0a, 0x06, 0x44, 0x45, 0x50, 0x4c, 0x4f, 0x59, 0x10, 0x06, + 0x22, 0x91, 0x02, 0x0a, 0x17, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x45, 0x6e, 0x76, 0x69, + 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x0e, 0x0a, 0x02, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x14, 0x0a, 0x05, + 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, + 0x74, 0x65, 0x12, 0x2a, 0x0a, 0x10, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x52, 0x75, 0x6e, + 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x10, 0x63, 0x75, + 0x72, 0x72, 0x65, 0x6e, 0x74, 0x52, 0x75, 0x6e, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x2c, + 0x0a, 0x11, 0x73, 0x74, 0x61, 0x72, 0x74, 0x4f, 0x66, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, + 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x11, 0x73, 0x74, 0x61, 0x72, 0x74, + 0x4f, 0x66, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x28, 0x0a, 0x0f, + 0x65, 0x6e, 0x64, 0x4f, 0x66, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x65, 0x6e, 0x64, 0x4f, 0x66, 0x54, 0x72, 0x61, 0x6e, + 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2e, 0x0a, 0x12, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x69, + 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x12, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x75, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, + 0x74, 0x61, 0x6d, 0x70, 0x22, 0x93, 0x01, 0x0a, 0x18, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x45, + 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, + 0x64, 0x12, 0x3f, 0x0a, 0x0a, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, + 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, + 0x6c, 0x2e, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x4f, 0x70, 0x65, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x12, 0x26, 0x0a, 0x0e, 0x72, 0x65, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, + 0x65, 0x41, 0x6c, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x72, 0x65, 0x63, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x65, 0x41, 0x6c, 0x6c, 0x22, 0xa1, 0x01, 0x0a, 0x14, 0x45, + 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x12, 0x3a, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0e, 0x32, 0x26, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x45, 0x6e, + 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x2e, 0x4f, 0x70, 0x74, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, + 0x1a, 0x0a, 0x08, 0x72, 0x6f, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x08, 0x72, 0x6f, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x31, 0x0a, 0x06, 0x4f, + 0x70, 0x74, 0x79, 0x70, 0x65, 0x12, 0x08, 0x0a, 0x04, 0x4e, 0x4f, 0x4f, 0x50, 0x10, 0x00, 0x12, + 0x0f, 0x0a, 0x0b, 0x52, 0x45, 0x4d, 0x4f, 0x56, 0x45, 0x5f, 0x52, 0x4f, 0x4c, 0x45, 0x10, 0x03, + 0x12, 0x0c, 0x0a, 0x08, 0x41, 0x44, 0x44, 0x5f, 0x52, 0x4f, 0x4c, 0x45, 0x10, 0x04, 0x22, 0x8b, + 0x01, 0x0a, 0x16, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, + 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x4b, 0x0a, 0x10, 0x66, 0x61, 0x69, + 0x6c, 0x65, 0x64, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, + 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x4f, 0x70, 0x65, 0x72, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x10, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x4f, 0x70, 0x65, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0xc1, 0x01, 0x0a, + 0x19, 0x44, 0x65, 0x73, 0x74, 0x72, 0x6f, 0x79, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, + 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x6b, 0x65, + 0x65, 0x70, 0x54, 0x61, 0x73, 0x6b, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x6b, + 0x65, 0x65, 0x70, 0x54, 0x61, 0x73, 0x6b, 0x73, 0x12, 0x30, 0x0a, 0x13, 0x61, 0x6c, 0x6c, 0x6f, + 0x77, 0x49, 0x6e, 0x52, 0x75, 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x65, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x49, 0x6e, 0x52, 0x75, + 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6f, + 0x72, 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, + 0x12, 0x2e, 0x0a, 0x0b, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x55, 0x73, 0x65, 0x72, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x55, + 0x73, 0x65, 0x72, 0x52, 0x0b, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x55, 0x73, 0x65, 0x72, + 0x22, 0x83, 0x01, 0x0a, 0x17, 0x44, 0x65, 0x73, 0x74, 0x72, 0x6f, 0x79, 0x45, 0x6e, 0x76, 0x69, + 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x4a, 0x0a, 0x11, + 0x63, 0x6c, 0x65, 0x61, 0x6e, 0x75, 0x70, 0x54, 0x61, 0x73, 0x6b, 0x73, 0x52, 0x65, 0x70, 0x6c, + 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, + 0x72, 0x6f, 0x6c, 0x2e, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x75, 0x70, 0x54, 0x61, 0x73, 0x6b, 0x73, + 0x52, 0x65, 0x70, 0x6c, 0x79, 0x52, 0x11, 0x63, 0x6c, 0x65, 0x61, 0x6e, 0x75, 0x70, 0x54, 0x61, + 0x73, 0x6b, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x55, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x41, 0x63, 0x74, + 0x69, 0x76, 0x65, 0x44, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x52, 0x65, 0x70, 0x6c, + 0x79, 0x12, 0x1c, 0x0a, 0x09, 0x64, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x64, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x12, + 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x58, 0x0a, + 0x1a, 0x47, 0x65, 0x74, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x44, 0x65, 0x74, + 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x1c, 0x0a, 0x09, 0x64, + 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, + 0x64, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0xcc, 0x01, 0x0a, 0x1f, 0x53, 0x65, 0x74, 0x45, + 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, + 0x74, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x5a, 0x0a, 0x0a, 0x70, + 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x3a, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x53, 0x65, 0x74, 0x45, + 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, + 0x74, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x50, 0x72, 0x6f, 0x70, + 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x70, 0x72, 0x6f, + 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x1a, 0x3d, 0x0a, 0x0f, 0x50, 0x72, 0x6f, 0x70, 0x65, + 0x72, 0x74, 0x69, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, + 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x1f, 0x0a, 0x1d, 0x53, 0x65, 0x74, 0x45, 0x6e, 0x76, + 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, + 0x65, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x73, 0x0a, 0x1f, 0x47, 0x65, 0x74, 0x45, 0x6e, + 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, + 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x71, 0x75, + 0x65, 0x72, 0x69, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x71, 0x75, 0x65, + 0x72, 0x69, 0x65, 0x73, 0x12, 0x26, 0x0a, 0x0e, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x47, + 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x65, 0x78, + 0x63, 0x6c, 0x75, 0x64, 0x65, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x73, 0x22, 0xb8, 0x01, 0x0a, + 0x1d, 0x47, 0x65, 0x74, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x50, + 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x58, + 0x0a, 0x0a, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x38, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x47, + 0x65, 0x74, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x50, 0x72, 0x6f, + 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x2e, 0x50, 0x72, 0x6f, + 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x70, 0x72, + 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x1a, 0x3d, 0x0a, 0x0f, 0x50, 0x72, 0x6f, 0x70, + 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, + 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xd8, 0x02, 0x0a, 0x0d, 0x53, 0x68, 0x6f, 0x72, + 0x74, 0x54, 0x61, 0x73, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, + 0x06, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x6c, + 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x74, 0x61, 0x73, 0x6b, 0x49, 0x64, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x61, 0x73, 0x6b, 0x49, 0x64, 0x12, 0x16, 0x0a, + 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x63, + 0x6c, 0x61, 0x73, 0x73, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, + 0x63, 0x6c, 0x61, 0x73, 0x73, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x45, 0x0a, 0x0e, 0x64, 0x65, 0x70, + 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x18, 0x07, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1d, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x54, 0x61, + 0x73, 0x6b, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, + 0x52, 0x0e, 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, + 0x12, 0x10, 0x0a, 0x03, 0x70, 0x69, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x70, + 0x69, 0x64, 0x12, 0x24, 0x0a, 0x0d, 0x73, 0x61, 0x6e, 0x64, 0x62, 0x6f, 0x78, 0x53, 0x74, 0x64, + 0x6f, 0x75, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x73, 0x61, 0x6e, 0x64, 0x62, + 0x6f, 0x78, 0x53, 0x74, 0x64, 0x6f, 0x75, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6c, 0x61, 0x69, + 0x6d, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x63, 0x6c, 0x61, + 0x69, 0x6d, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x72, 0x69, 0x74, 0x69, 0x63, + 0x61, 0x6c, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x63, 0x72, 0x69, 0x74, 0x69, 0x63, + 0x61, 0x6c, 0x22, 0x84, 0x01, 0x0a, 0x12, 0x54, 0x61, 0x73, 0x6b, 0x44, 0x65, 0x70, 0x6c, 0x6f, + 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1a, 0x0a, 0x08, 0x68, 0x6f, 0x73, + 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x68, 0x6f, 0x73, + 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x49, 0x64, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, + 0x18, 0x0a, 0x07, 0x6f, 0x66, 0x66, 0x65, 0x72, 0x49, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x07, 0x6f, 0x66, 0x66, 0x65, 0x72, 0x49, 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x65, 0x78, 0x65, + 0x63, 0x75, 0x74, 0x6f, 0x72, 0x49, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x65, + 0x78, 0x65, 0x63, 0x75, 0x74, 0x6f, 0x72, 0x49, 0x64, 0x22, 0x11, 0x0a, 0x0f, 0x47, 0x65, 0x74, + 0x54, 0x61, 0x73, 0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x5d, 0x0a, 0x0d, + 0x47, 0x65, 0x74, 0x54, 0x61, 0x73, 0x6b, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x2e, 0x0a, + 0x05, 0x74, 0x61, 0x73, 0x6b, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6f, + 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x53, 0x68, 0x6f, 0x72, 0x74, 0x54, 0x61, + 0x73, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x05, 0x74, 0x61, 0x73, 0x6b, 0x73, 0x12, 0x1c, 0x0a, + 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x28, 0x0a, 0x0e, 0x47, + 0x65, 0x74, 0x54, 0x61, 0x73, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, + 0x06, 0x74, 0x61, 0x73, 0x6b, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, + 0x61, 0x73, 0x6b, 0x49, 0x64, 0x22, 0x55, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x54, 0x61, 0x73, 0x6b, + 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x27, 0x0a, 0x04, 0x74, 0x61, 0x73, 0x6b, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, + 0x54, 0x61, 0x73, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x04, 0x74, 0x61, 0x73, 0x6b, 0x12, 0x1c, + 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x7d, 0x0a, 0x0b, + 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x10, 0x0a, 0x03, 0x65, + 0x6e, 0x76, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x03, 0x65, 0x6e, 0x76, 0x12, 0x14, 0x0a, + 0x05, 0x73, 0x68, 0x65, 0x6c, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x73, 0x68, + 0x65, 0x6c, 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x72, 0x67, + 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x61, 0x72, + 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x22, 0x4d, 0x0a, 0x0b, 0x43, + 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, + 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, + 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x22, 0xbe, 0x03, 0x0a, 0x08, 0x54, + 0x61, 0x73, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x36, 0x0a, 0x09, 0x73, 0x68, 0x6f, 0x72, 0x74, + 0x49, 0x6e, 0x66, 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6f, 0x32, 0x63, + 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x53, 0x68, 0x6f, 0x72, 0x74, 0x54, 0x61, 0x73, 0x6b, + 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x09, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, + 0x40, 0x0a, 0x0f, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, + 0x6c, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, + 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x49, 0x6e, 0x66, 0x6f, + 0x52, 0x0f, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, + 0x73, 0x12, 0x42, 0x0a, 0x10, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x43, 0x68, 0x61, + 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6f, 0x32, + 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x49, + 0x6e, 0x66, 0x6f, 0x52, 0x10, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x43, 0x68, 0x61, + 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x12, 0x38, 0x0a, 0x0b, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, + 0x49, 0x6e, 0x66, 0x6f, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6f, 0x32, 0x63, + 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x49, 0x6e, + 0x66, 0x6f, 0x52, 0x0b, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x49, 0x6e, 0x66, 0x6f, 0x12, + 0x1a, 0x0a, 0x08, 0x74, 0x61, 0x73, 0x6b, 0x50, 0x61, 0x74, 0x68, 0x18, 0x06, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x08, 0x74, 0x61, 0x73, 0x6b, 0x50, 0x61, 0x74, 0x68, 0x12, 0x14, 0x0a, 0x05, 0x65, + 0x6e, 0x76, 0x49, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x6e, 0x76, 0x49, + 0x64, 0x12, 0x43, 0x0a, 0x0a, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, + 0x09, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, + 0x6c, 0x2e, 0x54, 0x61, 0x73, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x50, 0x72, 0x6f, 0x70, 0x65, + 0x72, 0x74, 0x69, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x70, + 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x1a, 0x3d, 0x0a, 0x0f, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, + 0x74, 0x69, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x3a, 0x02, 0x38, 0x01, 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x22, 0x2f, 0x0a, 0x13, 0x43, + 0x6c, 0x65, 0x61, 0x6e, 0x75, 0x70, 0x54, 0x61, 0x73, 0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x74, 0x61, 0x73, 0x6b, 0x49, 0x64, 0x73, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x09, 0x52, 0x07, 0x74, 0x61, 0x73, 0x6b, 0x49, 0x64, 0x73, 0x22, 0xab, 0x01, 0x0a, + 0x11, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x75, 0x70, 0x54, 0x61, 0x73, 0x6b, 0x73, 0x52, 0x65, 0x70, + 0x6c, 0x79, 0x12, 0x3a, 0x0a, 0x0b, 0x6b, 0x69, 0x6c, 0x6c, 0x65, 0x64, 0x54, 0x61, 0x73, 0x6b, + 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, + 0x72, 0x6f, 0x6c, 0x2e, 0x53, 0x68, 0x6f, 0x72, 0x74, 0x54, 0x61, 0x73, 0x6b, 0x49, 0x6e, 0x66, + 0x6f, 0x52, 0x0b, 0x6b, 0x69, 0x6c, 0x6c, 0x65, 0x64, 0x54, 0x61, 0x73, 0x6b, 0x73, 0x12, 0x3c, + 0x0a, 0x0c, 0x72, 0x75, 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x54, 0x61, 0x73, 0x6b, 0x73, 0x18, 0x02, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, + 0x2e, 0x53, 0x68, 0x6f, 0x72, 0x74, 0x54, 0x61, 0x73, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0c, + 0x72, 0x75, 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x54, 0x61, 0x73, 0x6b, 0x73, 0x12, 0x1c, 0x0a, 0x09, + 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x43, 0x0a, 0x0f, 0x47, 0x65, + 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, + 0x05, 0x65, 0x6e, 0x76, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x6e, + 0x76, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x74, 0x68, 0x53, 0x70, 0x65, 0x63, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x74, 0x68, 0x53, 0x70, 0x65, 0x63, 0x22, + 0xd3, 0x05, 0x0a, 0x08, 0x52, 0x6f, 0x6c, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x12, 0x0a, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, + 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, + 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1a, + 0x0a, 0x08, 0x66, 0x75, 0x6c, 0x6c, 0x50, 0x61, 0x74, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x08, 0x66, 0x75, 0x6c, 0x6c, 0x50, 0x61, 0x74, 0x68, 0x12, 0x18, 0x0a, 0x07, 0x74, 0x61, + 0x73, 0x6b, 0x49, 0x64, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x74, 0x61, 0x73, + 0x6b, 0x49, 0x64, 0x73, 0x12, 0x29, 0x0a, 0x05, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x18, 0x06, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, + 0x52, 0x6f, 0x6c, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x05, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x12, + 0x3d, 0x0a, 0x08, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x21, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x52, 0x6f, + 0x6c, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x45, + 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x12, 0x31, + 0x0a, 0x04, 0x76, 0x61, 0x72, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6f, + 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x52, 0x6f, 0x6c, 0x65, 0x49, 0x6e, 0x66, + 0x6f, 0x2e, 0x56, 0x61, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x04, 0x76, 0x61, 0x72, + 0x73, 0x12, 0x3d, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x56, 0x61, 0x72, 0x73, 0x18, 0x09, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, + 0x52, 0x6f, 0x6c, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x56, 0x61, 0x72, + 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x56, 0x61, 0x72, 0x73, + 0x12, 0x58, 0x0a, 0x11, 0x63, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x64, + 0x53, 0x74, 0x61, 0x63, 0x6b, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x6f, 0x32, + 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x52, 0x6f, 0x6c, 0x65, 0x49, 0x6e, 0x66, 0x6f, + 0x2e, 0x43, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x64, 0x53, 0x74, 0x61, + 0x63, 0x6b, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x11, 0x63, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x69, + 0x64, 0x61, 0x74, 0x65, 0x64, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, + 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x3b, 0x0a, 0x0d, + 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, + 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, + 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x37, 0x0a, 0x09, 0x56, 0x61, 0x72, + 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, + 0x38, 0x01, 0x1a, 0x3b, 0x0a, 0x0d, 0x55, 0x73, 0x65, 0x72, 0x56, 0x61, 0x72, 0x73, 0x45, 0x6e, + 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, + 0x44, 0x0a, 0x16, 0x43, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x64, 0x53, + 0x74, 0x61, 0x63, 0x6b, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x58, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x6c, 0x65, + 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x29, 0x0a, 0x05, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x18, + 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, + 0x6c, 0x2e, 0x52, 0x6f, 0x6c, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x05, 0x72, 0x6f, 0x6c, 0x65, + 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, + 0xc9, 0x01, 0x0a, 0x1b, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x54, + 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x20, 0x0a, 0x0b, 0x72, 0x65, 0x70, 0x6f, 0x50, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x72, 0x65, 0x70, 0x6f, 0x50, 0x61, 0x74, 0x74, 0x65, 0x72, + 0x6e, 0x12, 0x28, 0x0a, 0x0f, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x74, + 0x74, 0x65, 0x72, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x72, 0x65, 0x76, 0x69, + 0x73, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x12, 0x20, 0x0a, 0x0b, 0x61, + 0x6c, 0x6c, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x0b, 0x61, 0x6c, 0x6c, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x65, 0x73, 0x12, 0x18, 0x0a, + 0x07, 0x61, 0x6c, 0x6c, 0x54, 0x61, 0x67, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, + 0x61, 0x6c, 0x6c, 0x54, 0x61, 0x67, 0x73, 0x12, 0x22, 0x0a, 0x0c, 0x61, 0x6c, 0x6c, 0x57, 0x6f, + 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x61, + 0x6c, 0x6c, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x73, 0x22, 0xae, 0x04, 0x0a, 0x0e, + 0x56, 0x61, 0x72, 0x53, 0x70, 0x65, 0x63, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x22, + 0x0a, 0x0c, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x56, 0x61, 0x6c, + 0x75, 0x65, 0x12, 0x32, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, + 0x32, 0x1e, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x56, 0x61, 0x72, + 0x53, 0x70, 0x65, 0x63, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x54, 0x79, 0x70, 0x65, + 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x12, 0x20, 0x0a, 0x0b, + 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3a, + 0x0a, 0x06, 0x77, 0x69, 0x64, 0x67, 0x65, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x22, + 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x56, 0x61, 0x72, 0x53, 0x70, + 0x65, 0x63, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x55, 0x69, 0x57, 0x69, 0x64, 0x67, + 0x65, 0x74, 0x52, 0x06, 0x77, 0x69, 0x64, 0x67, 0x65, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x61, + 0x6e, 0x65, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x70, 0x61, 0x6e, 0x65, 0x6c, + 0x12, 0x24, 0x0a, 0x0d, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x56, 0x61, 0x6c, 0x75, 0x65, + 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, + 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, + 0x08, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x1c, 0x0a, 0x09, + 0x76, 0x69, 0x73, 0x69, 0x62, 0x6c, 0x65, 0x49, 0x66, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x09, 0x76, 0x69, 0x73, 0x69, 0x62, 0x6c, 0x65, 0x49, 0x66, 0x12, 0x1c, 0x0a, 0x09, 0x65, 0x6e, + 0x61, 0x62, 0x6c, 0x65, 0x64, 0x49, 0x66, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x65, + 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x49, 0x66, 0x12, 0x12, 0x0a, 0x04, 0x72, 0x6f, 0x77, 0x73, + 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x72, 0x6f, 0x77, 0x73, 0x22, 0x71, 0x0a, 0x08, + 0x55, 0x69, 0x57, 0x69, 0x64, 0x67, 0x65, 0x74, 0x12, 0x0b, 0x0a, 0x07, 0x65, 0x64, 0x69, 0x74, + 0x42, 0x6f, 0x78, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x73, 0x6c, 0x69, 0x64, 0x65, 0x72, 0x10, + 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x6c, 0x69, 0x73, 0x74, 0x42, 0x6f, 0x78, 0x10, 0x02, 0x12, 0x0f, + 0x0a, 0x0b, 0x64, 0x72, 0x6f, 0x70, 0x44, 0x6f, 0x77, 0x6e, 0x42, 0x6f, 0x78, 0x10, 0x03, 0x12, + 0x0c, 0x0a, 0x08, 0x63, 0x6f, 0x6d, 0x62, 0x6f, 0x42, 0x6f, 0x78, 0x10, 0x04, 0x12, 0x12, 0x0a, + 0x0e, 0x72, 0x61, 0x64, 0x69, 0x6f, 0x42, 0x75, 0x74, 0x74, 0x6f, 0x6e, 0x42, 0x6f, 0x78, 0x10, + 0x05, 0x12, 0x0c, 0x0a, 0x08, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x42, 0x6f, 0x78, 0x10, 0x06, 0x22, + 0x3b, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0a, 0x0a, 0x06, 0x73, 0x74, 0x72, 0x69, 0x6e, + 0x67, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x10, 0x01, 0x12, + 0x08, 0x0a, 0x04, 0x62, 0x6f, 0x6f, 0x6c, 0x10, 0x02, 0x12, 0x08, 0x0a, 0x04, 0x6c, 0x69, 0x73, + 0x74, 0x10, 0x03, 0x12, 0x07, 0x0a, 0x03, 0x6d, 0x61, 0x70, 0x10, 0x04, 0x22, 0xaf, 0x02, 0x0a, + 0x14, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, + 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x72, 0x65, 0x70, 0x6f, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x72, 0x65, 0x70, 0x6f, 0x12, 0x1a, 0x0a, 0x08, 0x74, 0x65, 0x6d, + 0x70, 0x6c, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x65, 0x6d, + 0x70, 0x6c, 0x61, 0x74, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, + 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, + 0x6e, 0x12, 0x4f, 0x0a, 0x0a, 0x76, 0x61, 0x72, 0x53, 0x70, 0x65, 0x63, 0x4d, 0x61, 0x70, 0x18, + 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, + 0x6c, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, + 0x74, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x56, 0x61, 0x72, 0x53, 0x70, 0x65, 0x63, 0x4d, 0x61, + 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x76, 0x61, 0x72, 0x53, 0x70, 0x65, 0x63, 0x4d, + 0x61, 0x70, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, + 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x58, 0x0a, 0x0f, 0x56, 0x61, 0x72, 0x53, 0x70, 0x65, 0x63, 0x4d, + 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2f, 0x0a, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, + 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x56, 0x61, 0x72, 0x53, 0x70, 0x65, 0x63, 0x4d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x88, + 0x01, 0x0a, 0x19, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x54, 0x65, + 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x4d, 0x0a, 0x11, + 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, + 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, + 0x72, 0x6f, 0x6c, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x54, 0x65, 0x6d, 0x70, + 0x6c, 0x61, 0x74, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x11, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, + 0x6f, 0x77, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x74, + 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, + 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x36, 0x0a, 0x10, 0x4c, 0x69, 0x73, + 0x74, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x22, 0x0a, + 0x0c, 0x67, 0x65, 0x74, 0x52, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x0c, 0x67, 0x65, 0x74, 0x52, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, + 0x73, 0x22, 0x80, 0x01, 0x0a, 0x08, 0x52, 0x65, 0x70, 0x6f, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x12, + 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x07, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x12, 0x28, 0x0a, 0x0f, + 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x52, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x52, 0x65, + 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, + 0x6f, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x76, 0x69, 0x73, + 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x8f, 0x01, 0x0a, 0x0e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x70, + 0x6f, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x29, 0x0a, 0x05, 0x72, 0x65, 0x70, 0x6f, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, + 0x6f, 0x6c, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x05, 0x72, 0x65, 0x70, + 0x6f, 0x73, 0x12, 0x34, 0x0a, 0x15, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x44, 0x65, 0x66, 0x61, + 0x75, 0x6c, 0x74, 0x52, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x15, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, + 0x52, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x4e, 0x0a, 0x0e, 0x41, 0x64, 0x64, 0x52, 0x65, 0x70, + 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x28, 0x0a, 0x0f, + 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x52, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x52, 0x65, + 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x70, 0x0a, 0x0c, 0x41, 0x64, 0x64, 0x52, 0x65, 0x70, + 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x2e, 0x0a, 0x12, 0x6e, 0x65, 0x77, 0x44, 0x65, 0x66, + 0x61, 0x75, 0x6c, 0x74, 0x52, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x12, 0x6e, 0x65, 0x77, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x52, 0x65, + 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, + 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x29, 0x0a, 0x11, 0x52, 0x65, 0x6d, 0x6f, + 0x76, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, + 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x69, 0x6e, + 0x64, 0x65, 0x78, 0x22, 0x57, 0x0a, 0x0f, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x65, 0x70, + 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x26, 0x0a, 0x0e, 0x6e, 0x65, 0x77, 0x44, 0x65, 0x66, + 0x61, 0x75, 0x6c, 0x74, 0x52, 0x65, 0x70, 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, + 0x6e, 0x65, 0x77, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x52, 0x65, 0x70, 0x6f, 0x12, 0x1c, + 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x2b, 0x0a, 0x13, + 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x05, 0x52, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x22, 0x2d, 0x0a, 0x15, 0x53, 0x65, 0x74, + 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x52, 0x65, 0x70, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x22, 0x3d, 0x0a, 0x1f, 0x53, 0x65, 0x74, 0x47, + 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x52, 0x65, 0x76, 0x69, + 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x72, + 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, + 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x51, 0x0a, 0x1d, 0x53, 0x65, 0x74, 0x52, 0x65, + 0x70, 0x6f, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x52, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, + 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6e, 0x64, 0x65, + 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x1a, + 0x0a, 0x08, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x08, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x4f, 0x0a, 0x1b, 0x53, 0x65, + 0x74, 0x52, 0x65, 0x70, 0x6f, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x52, 0x65, 0x76, 0x69, + 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x6e, 0x66, + 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x1c, 0x0a, + 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x07, 0x0a, 0x05, 0x45, + 0x6d, 0x70, 0x74, 0x79, 0x22, 0xec, 0x01, 0x0a, 0x1b, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x6e, 0x74, + 0x65, 0x67, 0x72, 0x61, 0x74, 0x65, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, + 0x65, 0x70, 0x6c, 0x79, 0x12, 0x50, 0x0a, 0x08, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x34, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, + 0x6f, 0x6c, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x65, + 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x2e, 0x53, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x73, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, + 0x74, 0x61, 0x6d, 0x70, 0x1a, 0x5d, 0x0a, 0x0d, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x36, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, + 0x6f, 0x6c, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x65, 0x64, 0x53, 0x65, 0x72, + 0x76, 0x69, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, + 0x02, 0x38, 0x01, 0x22, 0x9f, 0x01, 0x0a, 0x15, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, + 0x65, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x12, 0x0a, + 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x65, + 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, + 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x28, 0x0a, 0x0f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x04, 0x64, 0x61, 0x74, 0x61, 0x32, 0xeb, 0x10, 0x0a, 0x07, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, + 0x6c, 0x12, 0x5a, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x77, 0x6f, 0x72, + 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x22, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, + 0x6c, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x6e, + 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6f, 0x32, 0x63, 0x6f, + 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x77, 0x6f, + 0x72, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x57, 0x0a, + 0x0f, 0x47, 0x65, 0x74, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x73, + 0x12, 0x21, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x47, 0x65, 0x74, + 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, + 0x47, 0x65, 0x74, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, + 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x60, 0x0a, 0x12, 0x4e, 0x65, 0x77, 0x41, 0x75, 0x74, + 0x6f, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x24, 0x2e, 0x6f, + 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x4e, 0x65, 0x77, 0x41, 0x75, 0x74, 0x6f, + 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x4e, + 0x65, 0x77, 0x41, 0x75, 0x74, 0x6f, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, + 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x54, 0x0a, 0x0e, 0x4e, 0x65, 0x77, 0x45, + 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x20, 0x2e, 0x6f, 0x32, 0x63, + 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x4e, 0x65, 0x77, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, + 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x6f, + 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x4e, 0x65, 0x77, 0x45, 0x6e, 0x76, 0x69, + 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x54, + 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, + 0x12, 0x20, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x47, 0x65, 0x74, + 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x47, + 0x65, 0x74, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x70, + 0x6c, 0x79, 0x22, 0x00, 0x12, 0x60, 0x0a, 0x12, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x45, + 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x24, 0x2e, 0x6f, 0x32, 0x63, + 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x45, 0x6e, + 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x22, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x43, 0x6f, 0x6e, + 0x74, 0x72, 0x6f, 0x6c, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x52, + 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x60, 0x0a, 0x12, 0x44, 0x65, 0x73, 0x74, 0x72, 0x6f, + 0x79, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x24, 0x2e, 0x6f, + 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x44, 0x65, 0x73, 0x74, 0x72, 0x6f, 0x79, + 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x44, + 0x65, 0x73, 0x74, 0x72, 0x6f, 0x79, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, + 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x4c, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x41, + 0x63, 0x74, 0x69, 0x76, 0x65, 0x44, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x12, 0x10, + 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, + 0x1a, 0x22, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x47, 0x65, 0x74, + 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x44, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x52, + 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x52, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x41, 0x76, 0x61, + 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x44, 0x65, 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x12, + 0x10, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x45, 0x6d, 0x70, 0x74, + 0x79, 0x1a, 0x25, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x47, 0x65, + 0x74, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x44, 0x65, 0x74, 0x65, 0x63, 0x74, + 0x6f, 0x72, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x59, 0x0a, 0x13, 0x4e, 0x65, + 0x77, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x41, 0x73, 0x79, 0x6e, + 0x63, 0x12, 0x20, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x4e, 0x65, + 0x77, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, + 0x4e, 0x65, 0x77, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, + 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x42, 0x0a, 0x08, 0x47, 0x65, 0x74, 0x54, 0x61, 0x73, 0x6b, + 0x73, 0x12, 0x1a, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x47, 0x65, + 0x74, 0x54, 0x61, 0x73, 0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, + 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x61, 0x73, + 0x6b, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x3f, 0x0a, 0x07, 0x47, 0x65, 0x74, + 0x54, 0x61, 0x73, 0x6b, 0x12, 0x19, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, + 0x2e, 0x47, 0x65, 0x74, 0x54, 0x61, 0x73, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x17, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x47, 0x65, 0x74, 0x54, + 0x61, 0x73, 0x6b, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x4e, 0x0a, 0x0c, 0x43, 0x6c, + 0x65, 0x61, 0x6e, 0x75, 0x70, 0x54, 0x61, 0x73, 0x6b, 0x73, 0x12, 0x1e, 0x2e, 0x6f, 0x32, 0x63, + 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x75, 0x70, 0x54, 0x61, + 0x73, 0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6f, 0x32, 0x63, + 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x75, 0x70, 0x54, 0x61, + 0x73, 0x6b, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x42, 0x0a, 0x08, 0x47, 0x65, + 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x12, 0x1a, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, + 0x6f, 0x6c, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x47, + 0x65, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x66, + 0x0a, 0x14, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x54, 0x65, 0x6d, + 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x12, 0x26, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, + 0x6f, 0x6c, 0x2e, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x54, 0x65, + 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, + 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x47, 0x65, 0x74, 0x57, 0x6f, + 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x52, + 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x45, 0x0a, 0x09, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, + 0x70, 0x6f, 0x73, 0x12, 0x1b, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, + 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x19, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x4c, 0x69, 0x73, + 0x74, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x3f, 0x0a, + 0x07, 0x41, 0x64, 0x64, 0x52, 0x65, 0x70, 0x6f, 0x12, 0x19, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, + 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x41, 0x64, 0x64, 0x52, 0x65, 0x70, 0x6f, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, + 0x41, 0x64, 0x64, 0x52, 0x65, 0x70, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x48, + 0x0a, 0x0a, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x12, 0x1c, 0x2e, 0x6f, + 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, + 0x65, 0x70, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6f, 0x32, 0x63, + 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x65, 0x70, + 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x42, 0x0a, 0x0c, 0x52, 0x65, 0x66, 0x72, + 0x65, 0x73, 0x68, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x12, 0x1e, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, + 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x52, 0x65, 0x70, 0x6f, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x10, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, + 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x46, 0x0a, 0x0e, + 0x53, 0x65, 0x74, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x52, 0x65, 0x70, 0x6f, 0x12, 0x20, + 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x53, 0x65, 0x74, 0x44, 0x65, + 0x66, 0x61, 0x75, 0x6c, 0x74, 0x52, 0x65, 0x70, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x10, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x45, 0x6d, 0x70, + 0x74, 0x79, 0x22, 0x00, 0x12, 0x5a, 0x0a, 0x18, 0x53, 0x65, 0x74, 0x47, 0x6c, 0x6f, 0x62, 0x61, + 0x6c, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x52, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, + 0x12, 0x2a, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x53, 0x65, 0x74, + 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x52, 0x65, 0x76, + 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x10, 0x2e, 0x6f, + 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, + 0x12, 0x6c, 0x0a, 0x16, 0x53, 0x65, 0x74, 0x52, 0x65, 0x70, 0x6f, 0x44, 0x65, 0x66, 0x61, 0x75, + 0x6c, 0x74, 0x52, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x28, 0x2e, 0x6f, 0x32, 0x63, + 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x53, 0x65, 0x74, 0x52, 0x65, 0x70, 0x6f, 0x44, 0x65, + 0x66, 0x61, 0x75, 0x6c, 0x74, 0x52, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, + 0x2e, 0x53, 0x65, 0x74, 0x52, 0x65, 0x70, 0x6f, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x52, + 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x3b, + 0x0a, 0x09, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x12, 0x1b, 0x2e, 0x6f, 0x32, + 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, + 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0d, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, + 0x73, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x22, 0x00, 0x30, 0x01, 0x12, 0x53, 0x0a, 0x15, 0x47, + 0x65, 0x74, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x65, 0x64, 0x53, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x73, 0x12, 0x10, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, + 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x26, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, + 0x6f, 0x6c, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x65, + 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, + 0x12, 0x42, 0x0a, 0x08, 0x54, 0x65, 0x61, 0x72, 0x64, 0x6f, 0x77, 0x6e, 0x12, 0x1a, 0x2e, 0x6f, + 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x54, 0x65, 0x61, 0x72, 0x64, 0x6f, 0x77, + 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, + 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x54, 0x65, 0x61, 0x72, 0x64, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x70, + 0x6c, 0x79, 0x22, 0x00, 0x12, 0x5d, 0x0a, 0x11, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x45, 0x6e, + 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x23, 0x2e, 0x6f, 0x32, 0x63, 0x6f, + 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x45, 0x6e, 0x76, 0x69, + 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, + 0x2e, 0x6f, 0x32, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x4d, 0x6f, 0x64, 0x69, 0x66, + 0x79, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x70, 0x6c, + 0x79, 0x22, 0x00, 0x42, 0x54, 0x0a, 0x22, 0x63, 0x68, 0x2e, 0x63, 0x65, 0x72, 0x6e, 0x2e, 0x61, + 0x6c, 0x69, 0x63, 0x65, 0x2e, 0x6f, 0x32, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, + 0x72, 0x70, 0x63, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5a, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, + 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x41, 0x6c, 0x69, 0x63, 0x65, 0x4f, 0x32, 0x47, 0x72, 0x6f, + 0x75, 0x70, 0x2f, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2f, 0x63, 0x6f, 0x72, 0x65, 0x2f, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x3b, 0x70, 0x62, 0x50, 0x00, 0x62, 0x06, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x33, } var ( - ErrInvalidLengthO2Control = fmt.Errorf("proto: negative length found during unmarshaling") - ErrIntOverflowO2Control = fmt.Errorf("proto: integer overflow") + file_protos_o2control_proto_rawDescOnce sync.Once + file_protos_o2control_proto_rawDescData = file_protos_o2control_proto_rawDesc ) + +func file_protos_o2control_proto_rawDescGZIP() []byte { + file_protos_o2control_proto_rawDescOnce.Do(func() { + file_protos_o2control_proto_rawDescData = protoimpl.X.CompressGZIP(file_protos_o2control_proto_rawDescData) + }) + return file_protos_o2control_proto_rawDescData +} + +var file_protos_o2control_proto_enumTypes = make([]protoimpl.EnumInfo, 4) +var file_protos_o2control_proto_msgTypes = make([]protoimpl.MessageInfo, 76) +var file_protos_o2control_proto_goTypes = []interface{}{ + (ControlEnvironmentRequest_Optype)(0), // 0: o2control.ControlEnvironmentRequest.Optype + (EnvironmentOperation_Optype)(0), // 1: o2control.EnvironmentOperation.Optype + (VarSpecMessage_UiWidget)(0), // 2: o2control.VarSpecMessage.UiWidget + (VarSpecMessage_Type)(0), // 3: o2control.VarSpecMessage.Type + (*SubscribeRequest)(nil), // 4: o2control.SubscribeRequest + (*GetFrameworkInfoRequest)(nil), // 5: o2control.GetFrameworkInfoRequest + (*Version)(nil), // 6: o2control.Version + (*GetFrameworkInfoReply)(nil), // 7: o2control.GetFrameworkInfoReply + (*TeardownRequest)(nil), // 8: o2control.TeardownRequest + (*TeardownReply)(nil), // 9: o2control.TeardownReply + (*GetEnvironmentsRequest)(nil), // 10: o2control.GetEnvironmentsRequest + (*GetEnvironmentsReply)(nil), // 11: o2control.GetEnvironmentsReply + (*EnvironmentInfo)(nil), // 12: o2control.EnvironmentInfo + (*NewEnvironmentRequest)(nil), // 13: o2control.NewEnvironmentRequest + (*NewEnvironmentReply)(nil), // 14: o2control.NewEnvironmentReply + (*NewAutoEnvironmentRequest)(nil), // 15: o2control.NewAutoEnvironmentRequest + (*NewAutoEnvironmentReply)(nil), // 16: o2control.NewAutoEnvironmentReply + (*GetEnvironmentRequest)(nil), // 17: o2control.GetEnvironmentRequest + (*GetEnvironmentReply)(nil), // 18: o2control.GetEnvironmentReply + (*ControlEnvironmentRequest)(nil), // 19: o2control.ControlEnvironmentRequest + (*ControlEnvironmentReply)(nil), // 20: o2control.ControlEnvironmentReply + (*ModifyEnvironmentRequest)(nil), // 21: o2control.ModifyEnvironmentRequest + (*EnvironmentOperation)(nil), // 22: o2control.EnvironmentOperation + (*ModifyEnvironmentReply)(nil), // 23: o2control.ModifyEnvironmentReply + (*DestroyEnvironmentRequest)(nil), // 24: o2control.DestroyEnvironmentRequest + (*DestroyEnvironmentReply)(nil), // 25: o2control.DestroyEnvironmentReply + (*GetActiveDetectorsReply)(nil), // 26: o2control.GetActiveDetectorsReply + (*GetAvailableDetectorsReply)(nil), // 27: o2control.GetAvailableDetectorsReply + (*SetEnvironmentPropertiesRequest)(nil), // 28: o2control.SetEnvironmentPropertiesRequest + (*SetEnvironmentPropertiesReply)(nil), // 29: o2control.SetEnvironmentPropertiesReply + (*GetEnvironmentPropertiesRequest)(nil), // 30: o2control.GetEnvironmentPropertiesRequest + (*GetEnvironmentPropertiesReply)(nil), // 31: o2control.GetEnvironmentPropertiesReply + (*ShortTaskInfo)(nil), // 32: o2control.ShortTaskInfo + (*TaskDeploymentInfo)(nil), // 33: o2control.TaskDeploymentInfo + (*GetTasksRequest)(nil), // 34: o2control.GetTasksRequest + (*GetTasksReply)(nil), // 35: o2control.GetTasksReply + (*GetTaskRequest)(nil), // 36: o2control.GetTaskRequest + (*GetTaskReply)(nil), // 37: o2control.GetTaskReply + (*CommandInfo)(nil), // 38: o2control.CommandInfo + (*ChannelInfo)(nil), // 39: o2control.ChannelInfo + (*TaskInfo)(nil), // 40: o2control.TaskInfo + (*CleanupTasksRequest)(nil), // 41: o2control.CleanupTasksRequest + (*CleanupTasksReply)(nil), // 42: o2control.CleanupTasksReply + (*GetRolesRequest)(nil), // 43: o2control.GetRolesRequest + (*RoleInfo)(nil), // 44: o2control.RoleInfo + (*GetRolesReply)(nil), // 45: o2control.GetRolesReply + (*GetWorkflowTemplatesRequest)(nil), // 46: o2control.GetWorkflowTemplatesRequest + (*VarSpecMessage)(nil), // 47: o2control.VarSpecMessage + (*WorkflowTemplateInfo)(nil), // 48: o2control.WorkflowTemplateInfo + (*GetWorkflowTemplatesReply)(nil), // 49: o2control.GetWorkflowTemplatesReply + (*ListReposRequest)(nil), // 50: o2control.ListReposRequest + (*RepoInfo)(nil), // 51: o2control.RepoInfo + (*ListReposReply)(nil), // 52: o2control.ListReposReply + (*AddRepoRequest)(nil), // 53: o2control.AddRepoRequest + (*AddRepoReply)(nil), // 54: o2control.AddRepoReply + (*RemoveRepoRequest)(nil), // 55: o2control.RemoveRepoRequest + (*RemoveRepoReply)(nil), // 56: o2control.RemoveRepoReply + (*RefreshReposRequest)(nil), // 57: o2control.RefreshReposRequest + (*SetDefaultRepoRequest)(nil), // 58: o2control.SetDefaultRepoRequest + (*SetGlobalDefaultRevisionRequest)(nil), // 59: o2control.SetGlobalDefaultRevisionRequest + (*SetRepoDefaultRevisionRequest)(nil), // 60: o2control.SetRepoDefaultRevisionRequest + (*SetRepoDefaultRevisionReply)(nil), // 61: o2control.SetRepoDefaultRevisionReply + (*Empty)(nil), // 62: o2control.Empty + (*ListIntegratedServicesReply)(nil), // 63: o2control.ListIntegratedServicesReply + (*IntegratedServiceInfo)(nil), // 64: o2control.IntegratedServiceInfo + nil, // 65: o2control.EnvironmentInfo.DefaultsEntry + nil, // 66: o2control.EnvironmentInfo.VarsEntry + nil, // 67: o2control.EnvironmentInfo.UserVarsEntry + nil, // 68: o2control.EnvironmentInfo.IntegratedServicesDataEntry + nil, // 69: o2control.NewEnvironmentRequest.VarsEntry + nil, // 70: o2control.NewAutoEnvironmentRequest.VarsEntry + nil, // 71: o2control.SetEnvironmentPropertiesRequest.PropertiesEntry + nil, // 72: o2control.GetEnvironmentPropertiesReply.PropertiesEntry + nil, // 73: o2control.TaskInfo.PropertiesEntry + nil, // 74: o2control.RoleInfo.DefaultsEntry + nil, // 75: o2control.RoleInfo.VarsEntry + nil, // 76: o2control.RoleInfo.UserVarsEntry + nil, // 77: o2control.RoleInfo.ConsolidatedStackEntry + nil, // 78: o2control.WorkflowTemplateInfo.VarSpecMapEntry + nil, // 79: o2control.ListIntegratedServicesReply.ServicesEntry + (*protos.User)(nil), // 80: common.User + (*protos.Event)(nil), // 81: events.Event +} +var file_protos_o2control_proto_depIdxs = []int32{ + 6, // 0: o2control.GetFrameworkInfoReply.version:type_name -> o2control.Version + 12, // 1: o2control.GetEnvironmentsReply.environments:type_name -> o2control.EnvironmentInfo + 32, // 2: o2control.EnvironmentInfo.tasks:type_name -> o2control.ShortTaskInfo + 65, // 3: o2control.EnvironmentInfo.defaults:type_name -> o2control.EnvironmentInfo.DefaultsEntry + 66, // 4: o2control.EnvironmentInfo.vars:type_name -> o2control.EnvironmentInfo.VarsEntry + 67, // 5: o2control.EnvironmentInfo.userVars:type_name -> o2control.EnvironmentInfo.UserVarsEntry + 68, // 6: o2control.EnvironmentInfo.integratedServicesData:type_name -> o2control.EnvironmentInfo.IntegratedServicesDataEntry + 69, // 7: o2control.NewEnvironmentRequest.vars:type_name -> o2control.NewEnvironmentRequest.VarsEntry + 80, // 8: o2control.NewEnvironmentRequest.requestUser:type_name -> common.User + 12, // 9: o2control.NewEnvironmentReply.environment:type_name -> o2control.EnvironmentInfo + 70, // 10: o2control.NewAutoEnvironmentRequest.vars:type_name -> o2control.NewAutoEnvironmentRequest.VarsEntry + 80, // 11: o2control.NewAutoEnvironmentRequest.requestUser:type_name -> common.User + 12, // 12: o2control.GetEnvironmentReply.environment:type_name -> o2control.EnvironmentInfo + 44, // 13: o2control.GetEnvironmentReply.workflow:type_name -> o2control.RoleInfo + 0, // 14: o2control.ControlEnvironmentRequest.type:type_name -> o2control.ControlEnvironmentRequest.Optype + 80, // 15: o2control.ControlEnvironmentRequest.requestUser:type_name -> common.User + 22, // 16: o2control.ModifyEnvironmentRequest.operations:type_name -> o2control.EnvironmentOperation + 1, // 17: o2control.EnvironmentOperation.type:type_name -> o2control.EnvironmentOperation.Optype + 22, // 18: o2control.ModifyEnvironmentReply.failedOperations:type_name -> o2control.EnvironmentOperation + 80, // 19: o2control.DestroyEnvironmentRequest.requestUser:type_name -> common.User + 42, // 20: o2control.DestroyEnvironmentReply.cleanupTasksReply:type_name -> o2control.CleanupTasksReply + 71, // 21: o2control.SetEnvironmentPropertiesRequest.properties:type_name -> o2control.SetEnvironmentPropertiesRequest.PropertiesEntry + 72, // 22: o2control.GetEnvironmentPropertiesReply.properties:type_name -> o2control.GetEnvironmentPropertiesReply.PropertiesEntry + 33, // 23: o2control.ShortTaskInfo.deploymentInfo:type_name -> o2control.TaskDeploymentInfo + 32, // 24: o2control.GetTasksReply.tasks:type_name -> o2control.ShortTaskInfo + 40, // 25: o2control.GetTaskReply.task:type_name -> o2control.TaskInfo + 32, // 26: o2control.TaskInfo.shortInfo:type_name -> o2control.ShortTaskInfo + 39, // 27: o2control.TaskInfo.inboundChannels:type_name -> o2control.ChannelInfo + 39, // 28: o2control.TaskInfo.outboundChannels:type_name -> o2control.ChannelInfo + 38, // 29: o2control.TaskInfo.commandInfo:type_name -> o2control.CommandInfo + 73, // 30: o2control.TaskInfo.properties:type_name -> o2control.TaskInfo.PropertiesEntry + 32, // 31: o2control.CleanupTasksReply.killedTasks:type_name -> o2control.ShortTaskInfo + 32, // 32: o2control.CleanupTasksReply.runningTasks:type_name -> o2control.ShortTaskInfo + 44, // 33: o2control.RoleInfo.roles:type_name -> o2control.RoleInfo + 74, // 34: o2control.RoleInfo.defaults:type_name -> o2control.RoleInfo.DefaultsEntry + 75, // 35: o2control.RoleInfo.vars:type_name -> o2control.RoleInfo.VarsEntry + 76, // 36: o2control.RoleInfo.userVars:type_name -> o2control.RoleInfo.UserVarsEntry + 77, // 37: o2control.RoleInfo.consolidatedStack:type_name -> o2control.RoleInfo.ConsolidatedStackEntry + 44, // 38: o2control.GetRolesReply.roles:type_name -> o2control.RoleInfo + 3, // 39: o2control.VarSpecMessage.type:type_name -> o2control.VarSpecMessage.Type + 2, // 40: o2control.VarSpecMessage.widget:type_name -> o2control.VarSpecMessage.UiWidget + 78, // 41: o2control.WorkflowTemplateInfo.varSpecMap:type_name -> o2control.WorkflowTemplateInfo.VarSpecMapEntry + 48, // 42: o2control.GetWorkflowTemplatesReply.workflowTemplates:type_name -> o2control.WorkflowTemplateInfo + 51, // 43: o2control.ListReposReply.repos:type_name -> o2control.RepoInfo + 79, // 44: o2control.ListIntegratedServicesReply.services:type_name -> o2control.ListIntegratedServicesReply.ServicesEntry + 47, // 45: o2control.WorkflowTemplateInfo.VarSpecMapEntry.value:type_name -> o2control.VarSpecMessage + 64, // 46: o2control.ListIntegratedServicesReply.ServicesEntry.value:type_name -> o2control.IntegratedServiceInfo + 5, // 47: o2control.Control.GetFrameworkInfo:input_type -> o2control.GetFrameworkInfoRequest + 10, // 48: o2control.Control.GetEnvironments:input_type -> o2control.GetEnvironmentsRequest + 15, // 49: o2control.Control.NewAutoEnvironment:input_type -> o2control.NewAutoEnvironmentRequest + 13, // 50: o2control.Control.NewEnvironment:input_type -> o2control.NewEnvironmentRequest + 17, // 51: o2control.Control.GetEnvironment:input_type -> o2control.GetEnvironmentRequest + 19, // 52: o2control.Control.ControlEnvironment:input_type -> o2control.ControlEnvironmentRequest + 24, // 53: o2control.Control.DestroyEnvironment:input_type -> o2control.DestroyEnvironmentRequest + 62, // 54: o2control.Control.GetActiveDetectors:input_type -> o2control.Empty + 62, // 55: o2control.Control.GetAvailableDetectors:input_type -> o2control.Empty + 13, // 56: o2control.Control.NewEnvironmentAsync:input_type -> o2control.NewEnvironmentRequest + 34, // 57: o2control.Control.GetTasks:input_type -> o2control.GetTasksRequest + 36, // 58: o2control.Control.GetTask:input_type -> o2control.GetTaskRequest + 41, // 59: o2control.Control.CleanupTasks:input_type -> o2control.CleanupTasksRequest + 43, // 60: o2control.Control.GetRoles:input_type -> o2control.GetRolesRequest + 46, // 61: o2control.Control.GetWorkflowTemplates:input_type -> o2control.GetWorkflowTemplatesRequest + 50, // 62: o2control.Control.ListRepos:input_type -> o2control.ListReposRequest + 53, // 63: o2control.Control.AddRepo:input_type -> o2control.AddRepoRequest + 55, // 64: o2control.Control.RemoveRepo:input_type -> o2control.RemoveRepoRequest + 57, // 65: o2control.Control.RefreshRepos:input_type -> o2control.RefreshReposRequest + 58, // 66: o2control.Control.SetDefaultRepo:input_type -> o2control.SetDefaultRepoRequest + 59, // 67: o2control.Control.SetGlobalDefaultRevision:input_type -> o2control.SetGlobalDefaultRevisionRequest + 60, // 68: o2control.Control.SetRepoDefaultRevision:input_type -> o2control.SetRepoDefaultRevisionRequest + 4, // 69: o2control.Control.Subscribe:input_type -> o2control.SubscribeRequest + 62, // 70: o2control.Control.GetIntegratedServices:input_type -> o2control.Empty + 8, // 71: o2control.Control.Teardown:input_type -> o2control.TeardownRequest + 21, // 72: o2control.Control.ModifyEnvironment:input_type -> o2control.ModifyEnvironmentRequest + 7, // 73: o2control.Control.GetFrameworkInfo:output_type -> o2control.GetFrameworkInfoReply + 11, // 74: o2control.Control.GetEnvironments:output_type -> o2control.GetEnvironmentsReply + 16, // 75: o2control.Control.NewAutoEnvironment:output_type -> o2control.NewAutoEnvironmentReply + 14, // 76: o2control.Control.NewEnvironment:output_type -> o2control.NewEnvironmentReply + 18, // 77: o2control.Control.GetEnvironment:output_type -> o2control.GetEnvironmentReply + 20, // 78: o2control.Control.ControlEnvironment:output_type -> o2control.ControlEnvironmentReply + 25, // 79: o2control.Control.DestroyEnvironment:output_type -> o2control.DestroyEnvironmentReply + 26, // 80: o2control.Control.GetActiveDetectors:output_type -> o2control.GetActiveDetectorsReply + 27, // 81: o2control.Control.GetAvailableDetectors:output_type -> o2control.GetAvailableDetectorsReply + 14, // 82: o2control.Control.NewEnvironmentAsync:output_type -> o2control.NewEnvironmentReply + 35, // 83: o2control.Control.GetTasks:output_type -> o2control.GetTasksReply + 37, // 84: o2control.Control.GetTask:output_type -> o2control.GetTaskReply + 42, // 85: o2control.Control.CleanupTasks:output_type -> o2control.CleanupTasksReply + 45, // 86: o2control.Control.GetRoles:output_type -> o2control.GetRolesReply + 49, // 87: o2control.Control.GetWorkflowTemplates:output_type -> o2control.GetWorkflowTemplatesReply + 52, // 88: o2control.Control.ListRepos:output_type -> o2control.ListReposReply + 54, // 89: o2control.Control.AddRepo:output_type -> o2control.AddRepoReply + 56, // 90: o2control.Control.RemoveRepo:output_type -> o2control.RemoveRepoReply + 62, // 91: o2control.Control.RefreshRepos:output_type -> o2control.Empty + 62, // 92: o2control.Control.SetDefaultRepo:output_type -> o2control.Empty + 62, // 93: o2control.Control.SetGlobalDefaultRevision:output_type -> o2control.Empty + 61, // 94: o2control.Control.SetRepoDefaultRevision:output_type -> o2control.SetRepoDefaultRevisionReply + 81, // 95: o2control.Control.Subscribe:output_type -> events.Event + 63, // 96: o2control.Control.GetIntegratedServices:output_type -> o2control.ListIntegratedServicesReply + 9, // 97: o2control.Control.Teardown:output_type -> o2control.TeardownReply + 23, // 98: o2control.Control.ModifyEnvironment:output_type -> o2control.ModifyEnvironmentReply + 73, // [73:99] is the sub-list for method output_type + 47, // [47:73] is the sub-list for method input_type + 47, // [47:47] is the sub-list for extension type_name + 47, // [47:47] is the sub-list for extension extendee + 0, // [0:47] is the sub-list for field type_name +} + +func init() { file_protos_o2control_proto_init() } +func file_protos_o2control_proto_init() { + if File_protos_o2control_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_protos_o2control_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SubscribeRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetFrameworkInfoRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Version); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetFrameworkInfoReply); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TeardownRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TeardownReply); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetEnvironmentsRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetEnvironmentsReply); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*EnvironmentInfo); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NewEnvironmentRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NewEnvironmentReply); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NewAutoEnvironmentRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NewAutoEnvironmentReply); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetEnvironmentRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetEnvironmentReply); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ControlEnvironmentRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ControlEnvironmentReply); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ModifyEnvironmentRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*EnvironmentOperation); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ModifyEnvironmentReply); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DestroyEnvironmentRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DestroyEnvironmentReply); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetActiveDetectorsReply); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetAvailableDetectorsReply); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SetEnvironmentPropertiesRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SetEnvironmentPropertiesReply); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetEnvironmentPropertiesRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetEnvironmentPropertiesReply); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[28].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ShortTaskInfo); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[29].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TaskDeploymentInfo); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[30].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetTasksRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[31].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetTasksReply); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[32].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetTaskRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[33].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetTaskReply); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[34].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CommandInfo); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[35].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ChannelInfo); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[36].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TaskInfo); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[37].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CleanupTasksRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[38].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CleanupTasksReply); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[39].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetRolesRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[40].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RoleInfo); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[41].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetRolesReply); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[42].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetWorkflowTemplatesRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[43].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*VarSpecMessage); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[44].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*WorkflowTemplateInfo); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[45].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetWorkflowTemplatesReply); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[46].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ListReposRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[47].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RepoInfo); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[48].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ListReposReply); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[49].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AddRepoRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[50].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AddRepoReply); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[51].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RemoveRepoRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[52].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RemoveRepoReply); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[53].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RefreshReposRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[54].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SetDefaultRepoRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[55].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SetGlobalDefaultRevisionRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[56].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SetRepoDefaultRevisionRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[57].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SetRepoDefaultRevisionReply); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[58].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Empty); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[59].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ListIntegratedServicesReply); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_o2control_proto_msgTypes[60].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*IntegratedServiceInfo); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_protos_o2control_proto_rawDesc, + NumEnums: 4, + NumMessages: 76, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_protos_o2control_proto_goTypes, + DependencyIndexes: file_protos_o2control_proto_depIdxs, + EnumInfos: file_protos_o2control_proto_enumTypes, + MessageInfos: file_protos_o2control_proto_msgTypes, + }.Build() + File_protos_o2control_proto = out.File + file_protos_o2control_proto_rawDesc = nil + file_protos_o2control_proto_goTypes = nil + file_protos_o2control_proto_depIdxs = nil +} diff --git a/core/protos/o2control.proto b/core/protos/o2control.proto index af592559f..9f9d0c544 100644 --- a/core/protos/o2control.proto +++ b/core/protos/o2control.proto @@ -26,27 +26,34 @@ syntax = "proto3"; package o2control; option java_package = "ch.cern.alice.o2.control.rpcserver"; -option go_package = "pb"; +option go_package = "github.com/AliceO2Group/Control/core/protos;pb"; -//////////////// Common event messages /////////////// - -message Event_MesosHeartbeat { -} - -////////////////////////////////////////////////////// +import public "protos/events.proto"; +// The Control service is the main interface to AliECS service Control { - rpc TrackStatus (StatusRequest) returns (stream StatusReply) {} - rpc GetFrameworkInfo (GetFrameworkInfoRequest) returns (GetFrameworkInfoReply) {} - rpc Teardown (TeardownRequest) returns (TeardownReply) {} rpc GetEnvironments (GetEnvironmentsRequest) returns (GetEnvironmentsReply) {} + // Creates a new environment which automatically follows one STANDBY->RUNNING->DONE cycle in the state machine. + // It returns only once the environment reaches the CONFIGURED state or upon any earlier failure. + rpc NewAutoEnvironment (NewAutoEnvironmentRequest) returns (NewAutoEnvironmentReply) {} + // Creates a new environment. + // It returns only once the environment reaches the CONFIGURED state or upon any earlier failure. rpc NewEnvironment (NewEnvironmentRequest) returns (NewEnvironmentReply) {} rpc GetEnvironment (GetEnvironmentRequest) returns (GetEnvironmentReply) {} rpc ControlEnvironment (ControlEnvironmentRequest) returns (ControlEnvironmentReply) {} - rpc ModifyEnvironment (ModifyEnvironmentRequest) returns (ModifyEnvironmentReply) {} rpc DestroyEnvironment (DestroyEnvironmentRequest) returns (DestroyEnvironmentReply) {} + rpc GetActiveDetectors (Empty) returns (GetActiveDetectorsReply) {} + rpc GetAvailableDetectors (Empty) returns (GetAvailableDetectorsReply) {} + + // Creates a new environment. + // It returns once an environment ID is created and continues the creation asynchronously to the call. + // The environment will be listed in GetEnvironments() only once the workflow is loaded and deployment starts. + rpc NewEnvironmentAsync (NewEnvironmentRequest) returns (NewEnvironmentReply) {} + +// rpc SetEnvironmentProperties (SetEnvironmentPropertiesRequest) returns (SetEnvironmentPropertiesReply) {} +// rpc GetEnvironmentProperties (GetEnvironmentPropertiesRequest) returns (GetEnvironmentPropertiesReply) {} rpc GetTasks (GetTasksRequest) returns (GetTasksReply) {} rpc GetTask(GetTaskRequest) returns (GetTaskReply) {} @@ -59,32 +66,24 @@ service Control { rpc ListRepos(ListReposRequest) returns (ListReposReply) {} rpc AddRepo(AddRepoRequest) returns (AddRepoReply) {} rpc RemoveRepo(RemoveRepoRequest) returns (RemoveRepoReply) {} - rpc RefreshRepos(RefreshReposRequest) returns (RefreshReposReply) {} - rpc SetDefaultRepo(SetDefaultRepoRequest) returns (SetDefaultRepoReply) {} + rpc RefreshRepos(RefreshReposRequest) returns (Empty) {} + rpc SetDefaultRepo(SetDefaultRepoRequest) returns (Empty) {} + rpc SetGlobalDefaultRevision(SetGlobalDefaultRevisionRequest) returns (Empty) {} + rpc SetRepoDefaultRevision(SetRepoDefaultRevisionRequest) returns (SetRepoDefaultRevisionReply) {} + rpc Subscribe(SubscribeRequest) returns (stream events.Event) {} + + rpc GetIntegratedServices(Empty) returns (ListIntegratedServicesReply) {} + + // Reserved and not implemented: + rpc Teardown (TeardownRequest) returns (TeardownReply) {} + rpc ModifyEnvironment (ModifyEnvironmentRequest) returns (ModifyEnvironmentReply) {} } -//////////////////////////////////////// -// Global status -//////////////////////////////////////// -message StatusRequest {} -message StatusReply { - string state = 1; - repeated StatusUpdate statusUpdates = 2; -} -message StatusUpdate { - enum Level { - DEBUG = 0; - INFO = 1; - WARNING = 2; - ERROR = 3; - } - Level level = 1; - oneof Event { - Event_MesosHeartbeat mesosHeartbeat = 2; - //TODO add other events here and in events.proto - } +message SubscribeRequest{ + string id = 1; } + //////////////////////////////////////// // Framework //////////////////////////////////////// @@ -105,6 +104,11 @@ message GetFrameworkInfoReply { int32 hostsCount = 5; string instanceName = 6; Version version = 7; + string configurationEndpoint = 8; + repeated string detectorsInInstance = 9; + repeated string activeDetectors = 10; + repeated string availableDetectors = 11; + int64 timestamp = 12; // timestamp of when this object was sent in unix milliseconds } // Not implemented yet @@ -116,33 +120,68 @@ message TeardownReply {} //////////////////////////////////////// // Environment //////////////////////////////////////// -message GetEnvironmentsRequest {} +message GetEnvironmentsRequest { + bool showAll = 1; + bool showTaskInfos = 2; + bool showDetailedIntegratedServices = 3; // integratedServices are returned everytime, setting this flag gives detailed report +} message GetEnvironmentsReply { string frameworkId = 1; repeated EnvironmentInfo environments = 2; + int64 timestamp = 3; // timestamp of when this object was sent in unix milliseconds } message EnvironmentInfo { string id = 1; - string createdWhen = 2; + int64 createdWhen = 2; // msec string state = 3; repeated ShortTaskInfo tasks = 4; string rootRole = 5; uint32 currentRunNumber = 6; + map defaults = 7; + map vars = 8; + map userVars = 9; + int32 numberOfFlps = 10; + repeated string includedDetectors = 11; + string description = 12; + int32 numberOfHosts = 13; + map integratedServicesData = 14; + int32 numberOfTasks = 15; + string currentTransition = 16; + int32 numberOfActiveTasks = 17; + int32 numberOfInactiveTasks = 18; } message NewEnvironmentRequest { string workflowTemplate = 1; + map vars = 2; + bool public = 3; + bool autoTransition = 4; + common.User requestUser = 5; } message NewEnvironmentReply { EnvironmentInfo environment = 1; + bool public = 2; + int64 timestamp = 3; // timestamp of when this object was sent in unix milliseconds +} +message NewAutoEnvironmentRequest { + string workflowTemplate = 1; + map vars = 2; + string id = 3; + common.User requestUser = 4; +} +message NewAutoEnvironmentReply { + int64 timestamp = 1; // timestamp of when this object was sent in unix milliseconds } message GetEnvironmentRequest { string id = 1; + bool showWorkflowTree = 2; } message GetEnvironmentReply { EnvironmentInfo environment = 1; RoleInfo workflow = 2; + bool public = 3; + int64 timestamp = 4; // timestamp of when this object was sent in unix milliseconds } message ControlEnvironmentRequest { @@ -154,13 +193,20 @@ message ControlEnvironmentRequest { CONFIGURE = 3; RESET = 4; GO_ERROR = 5; + DEPLOY = 6; } Optype type = 2; + common.User requestUser = 3; } message ControlEnvironmentReply { string id = 1; string state = 2; uint32 currentRunNumber = 3; + // All times are in milliseconds + int64 startOfTransition = 4; + int64 endOfTransition = 5; + int64 transitionDuration = 6; + int64 timestamp = 7; // timestamp of when this object was sent in unix milliseconds } message ModifyEnvironmentRequest { @@ -186,11 +232,49 @@ message ModifyEnvironmentReply { message DestroyEnvironmentRequest { string id = 1; bool keepTasks = 2; + bool allowInRunningState = 3; + bool force = 4; + common.User requestUser = 5; } message DestroyEnvironmentReply { CleanupTasksReply cleanupTasksReply = 1; + int64 timestamp = 2; // timestamp of when this object was sent in unix milliseconds +} + +message GetActiveDetectorsReply { + repeated string detectors = 1; + int64 timestamp = 2; // timestamp of when this object was sent in unix milliseconds +} + +message GetAvailableDetectorsReply { + repeated string detectors = 1; + int64 timestamp = 2; // timestamp of when this object was sent in unix milliseconds } +//////////////////////////////////////// +// Environment, GET/SET properties +//////////////////////////////////////// +message SetEnvironmentPropertiesRequest { + string id = 1; + // If properties == nil, the core sets nothing + // and reply ok + map properties = 2; +} +message SetEnvironmentPropertiesReply {} + +message GetEnvironmentPropertiesRequest { + string id = 1; + // If len(queries) == 0, we return an + // empty map. + // To retrieve all KVs, use query '*' + repeated string queries = 2; + bool excludeGlobals = 3; +} +message GetEnvironmentPropertiesReply { + map properties = 1; +} + + //////////////////////////////////////// // Tasks //////////////////////////////////////// @@ -202,6 +286,10 @@ message ShortTaskInfo { string state = 5; string className = 6; TaskDeploymentInfo deploymentInfo = 7; + string pid = 8; + string sandboxStdout = 9; + bool claimable = 10; + bool critical = 11; } message TaskDeploymentInfo { string hostname = 1; @@ -213,18 +301,16 @@ message TaskDeploymentInfo { message GetTasksRequest {} message GetTasksReply { repeated ShortTaskInfo tasks = 1; + int64 timestamp = 2; // timestamp of when this object was sent in unix milliseconds } message GetTaskRequest { string taskId = 1; } message GetTaskReply { TaskInfo task = 1; + int64 timestamp = 2; // timestamp of when this object was sent in unix milliseconds } -message TaskClassInfo { - string name = 1; - string controlMode = 2; -} message CommandInfo { repeated string env = 1; bool shell = 2; @@ -239,12 +325,13 @@ message ChannelInfo { } message TaskInfo { ShortTaskInfo shortInfo = 1; - TaskClassInfo classInfo = 2; + reserved 2; repeated ChannelInfo inboundChannels = 3; repeated ChannelInfo outboundChannels = 4; CommandInfo commandInfo = 5; string taskPath = 6; string envId = 7; + map properties = 9; } message CleanupTasksRequest { @@ -253,6 +340,7 @@ message CleanupTasksRequest { message CleanupTasksReply { repeated ShortTaskInfo killedTasks = 1; repeated ShortTaskInfo runningTasks = 2; + int64 timestamp = 3; // timestamp of when this object was sent in unix milliseconds } //////////////////////////////////////// @@ -270,44 +358,101 @@ message RoleInfo { string fullPath = 4; repeated string taskIds = 5; repeated RoleInfo roles = 6; + map defaults = 7; + map vars = 8; + map userVars = 9; + map consolidatedStack = 10; + string description = 11; } message GetRolesReply { repeated RoleInfo roles = 1; -} + int64 timestamp = 2; // timestamp of when this object was sent in unix milliseconds +} + +message GetWorkflowTemplatesRequest{ + string repoPattern = 1; + string revisionPattern = 2; + bool allBranches = 3; + bool allTags = 4; + bool allWorkflows = 5; +} + +message VarSpecMessage { + enum UiWidget { + editBox = 0; // plain string input line, can accept types number (like a spinBox) and string + slider = 1; // input widget exclusively for numbers, range allowedValues[0]-[1] + listBox = 2; // displays a list of items, can accept types number, string or list; if number/string ==> single selection, otherwise multiple selection allowed + dropDownBox = 3; + comboBox = 4; + radioButtonBox = 5; + checkBox = 6; + } -message GetWorkflowTemplatesRequest{} + enum Type { + string = 0; + number = 1; + bool = 2; + list = 3; + map = 4; + } + + string defaultValue = 1; + Type type = 2; + string label = 3; + string description = 4; + UiWidget widget = 5; + string panel = 6; // hint for the UI on where to put or group the given variable input + repeated string allowedValues = 7; // list of offered values from which to choose (only for some UiWidgets) + int32 index = 8; + string visibleIf = 9; // JS expression that evaluates to bool + string enabledIf = 10; // JS expression that evaluates to bool + uint32 rows = 11; // this field is used only if widget == editBox +} message WorkflowTemplateInfo { string repo = 1; string template = 2; + string revision = 3; + map varSpecMap = 4; + string description = 5; } message GetWorkflowTemplatesReply{ repeated WorkflowTemplateInfo workflowTemplates = 1; + int64 timestamp = 2; // timestamp of when this object was sent in unix milliseconds } //////////////////////////////////////// // Repos //////////////////////////////////////// -message ListReposRequest {} +message ListReposRequest { + bool getRevisions = 1; +} message RepoInfo { string name = 1; bool default = 2; + string defaultRevision = 3; + repeated string revisions = 4; } message ListReposReply { repeated RepoInfo repos = 1; + string globalDefaultRevision = 2; + int64 timestamp = 3; // timestamp of when this object was sent in unix milliseconds } message AddRepoRequest { string name = 1; + string defaultRevision = 2; } message AddRepoReply { - string errorString = 1; + string newDefaultRevision = 1; + string info = 2; + int64 timestamp = 3; // timestamp of when this object was sent in unix milliseconds } message RemoveRepoRequest { @@ -315,22 +460,45 @@ message RemoveRepoRequest { } message RemoveRepoReply { - bool ok = 1; - string newDefaultRepo = 2; + string newDefaultRepo = 1; + int64 timestamp = 2; // timestamp of when this object was sent in unix milliseconds } message RefreshReposRequest { int32 index = 1; } -message RefreshReposReply { - string errorString = 1; +message SetDefaultRepoRequest { + int32 index = 1; } -message SetDefaultRepoRequest { +message SetGlobalDefaultRevisionRequest { + string revision = 1; +} + +message SetRepoDefaultRevisionRequest { int32 index = 1; + string revision = 2; +} + +message SetRepoDefaultRevisionReply { + string info = 1; + int64 timestamp = 2; // timestamp of when this object was sent in unix milliseconds +} + +message Empty { + } -message SetDefaultRepoReply { - string errorString = 1; -} \ No newline at end of file +message ListIntegratedServicesReply { + map services = 1; // keys are IDs (e.g. "ddsched"), the service name should be displayed to users instead + int64 timestamp = 2; // timestamp of when this object was sent in unix milliseconds +} + +message IntegratedServiceInfo { + string name = 1; // user-visible service name, e.g. "DD scheduler" + bool enabled = 2; + string endpoint = 3; + string connectionState = 4; // allowed values: READY, CONNECTING, TRANSIENT_FAILURE, IDLE, SHUTDOWN + string data = 5; // always a JSON payload with a map inside. +} diff --git a/core/protos/o2control_grpc.pb.go b/core/protos/o2control_grpc.pb.go new file mode 100644 index 000000000..1208a45bc --- /dev/null +++ b/core/protos/o2control_grpc.pb.go @@ -0,0 +1,1100 @@ +// +// === This file is part of ALICE O² === +// +// Copyright 2018 CERN and copyright holders of ALICE O². +// Author: Teo Mrnjavac +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// In applying this license CERN does not waive the privileges and +// immunities granted to it by virtue of its status as an +// Intergovernmental Organization or submit itself to any jurisdiction. + +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.3.0 +// - protoc v5.29.3 +// source: protos/o2control.proto + +package pb + +import ( + context "context" + protos "github.com/AliceO2Group/Control/common/protos" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +const ( + Control_GetFrameworkInfo_FullMethodName = "/o2control.Control/GetFrameworkInfo" + Control_GetEnvironments_FullMethodName = "/o2control.Control/GetEnvironments" + Control_NewAutoEnvironment_FullMethodName = "/o2control.Control/NewAutoEnvironment" + Control_NewEnvironment_FullMethodName = "/o2control.Control/NewEnvironment" + Control_GetEnvironment_FullMethodName = "/o2control.Control/GetEnvironment" + Control_ControlEnvironment_FullMethodName = "/o2control.Control/ControlEnvironment" + Control_DestroyEnvironment_FullMethodName = "/o2control.Control/DestroyEnvironment" + Control_GetActiveDetectors_FullMethodName = "/o2control.Control/GetActiveDetectors" + Control_GetAvailableDetectors_FullMethodName = "/o2control.Control/GetAvailableDetectors" + Control_NewEnvironmentAsync_FullMethodName = "/o2control.Control/NewEnvironmentAsync" + Control_GetTasks_FullMethodName = "/o2control.Control/GetTasks" + Control_GetTask_FullMethodName = "/o2control.Control/GetTask" + Control_CleanupTasks_FullMethodName = "/o2control.Control/CleanupTasks" + Control_GetRoles_FullMethodName = "/o2control.Control/GetRoles" + Control_GetWorkflowTemplates_FullMethodName = "/o2control.Control/GetWorkflowTemplates" + Control_ListRepos_FullMethodName = "/o2control.Control/ListRepos" + Control_AddRepo_FullMethodName = "/o2control.Control/AddRepo" + Control_RemoveRepo_FullMethodName = "/o2control.Control/RemoveRepo" + Control_RefreshRepos_FullMethodName = "/o2control.Control/RefreshRepos" + Control_SetDefaultRepo_FullMethodName = "/o2control.Control/SetDefaultRepo" + Control_SetGlobalDefaultRevision_FullMethodName = "/o2control.Control/SetGlobalDefaultRevision" + Control_SetRepoDefaultRevision_FullMethodName = "/o2control.Control/SetRepoDefaultRevision" + Control_Subscribe_FullMethodName = "/o2control.Control/Subscribe" + Control_GetIntegratedServices_FullMethodName = "/o2control.Control/GetIntegratedServices" + Control_Teardown_FullMethodName = "/o2control.Control/Teardown" + Control_ModifyEnvironment_FullMethodName = "/o2control.Control/ModifyEnvironment" +) + +// ControlClient is the client API for Control service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type ControlClient interface { + GetFrameworkInfo(ctx context.Context, in *GetFrameworkInfoRequest, opts ...grpc.CallOption) (*GetFrameworkInfoReply, error) + GetEnvironments(ctx context.Context, in *GetEnvironmentsRequest, opts ...grpc.CallOption) (*GetEnvironmentsReply, error) + // Creates a new environment which automatically follows one STANDBY->RUNNING->DONE cycle in the state machine. + // It returns only once the environment reaches the CONFIGURED state or upon any earlier failure. + NewAutoEnvironment(ctx context.Context, in *NewAutoEnvironmentRequest, opts ...grpc.CallOption) (*NewAutoEnvironmentReply, error) + // Creates a new environment. + // It returns only once the environment reaches the CONFIGURED state or upon any earlier failure. + NewEnvironment(ctx context.Context, in *NewEnvironmentRequest, opts ...grpc.CallOption) (*NewEnvironmentReply, error) + GetEnvironment(ctx context.Context, in *GetEnvironmentRequest, opts ...grpc.CallOption) (*GetEnvironmentReply, error) + ControlEnvironment(ctx context.Context, in *ControlEnvironmentRequest, opts ...grpc.CallOption) (*ControlEnvironmentReply, error) + DestroyEnvironment(ctx context.Context, in *DestroyEnvironmentRequest, opts ...grpc.CallOption) (*DestroyEnvironmentReply, error) + GetActiveDetectors(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*GetActiveDetectorsReply, error) + GetAvailableDetectors(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*GetAvailableDetectorsReply, error) + // Creates a new environment. + // It returns once an environment ID is created and continues the creation asynchronously to the call. + // The environment will be listed in GetEnvironments() only once the workflow is loaded and deployment starts. + NewEnvironmentAsync(ctx context.Context, in *NewEnvironmentRequest, opts ...grpc.CallOption) (*NewEnvironmentReply, error) + GetTasks(ctx context.Context, in *GetTasksRequest, opts ...grpc.CallOption) (*GetTasksReply, error) + GetTask(ctx context.Context, in *GetTaskRequest, opts ...grpc.CallOption) (*GetTaskReply, error) + CleanupTasks(ctx context.Context, in *CleanupTasksRequest, opts ...grpc.CallOption) (*CleanupTasksReply, error) + GetRoles(ctx context.Context, in *GetRolesRequest, opts ...grpc.CallOption) (*GetRolesReply, error) + GetWorkflowTemplates(ctx context.Context, in *GetWorkflowTemplatesRequest, opts ...grpc.CallOption) (*GetWorkflowTemplatesReply, error) + ListRepos(ctx context.Context, in *ListReposRequest, opts ...grpc.CallOption) (*ListReposReply, error) + AddRepo(ctx context.Context, in *AddRepoRequest, opts ...grpc.CallOption) (*AddRepoReply, error) + RemoveRepo(ctx context.Context, in *RemoveRepoRequest, opts ...grpc.CallOption) (*RemoveRepoReply, error) + RefreshRepos(ctx context.Context, in *RefreshReposRequest, opts ...grpc.CallOption) (*Empty, error) + SetDefaultRepo(ctx context.Context, in *SetDefaultRepoRequest, opts ...grpc.CallOption) (*Empty, error) + SetGlobalDefaultRevision(ctx context.Context, in *SetGlobalDefaultRevisionRequest, opts ...grpc.CallOption) (*Empty, error) + SetRepoDefaultRevision(ctx context.Context, in *SetRepoDefaultRevisionRequest, opts ...grpc.CallOption) (*SetRepoDefaultRevisionReply, error) + Subscribe(ctx context.Context, in *SubscribeRequest, opts ...grpc.CallOption) (Control_SubscribeClient, error) + GetIntegratedServices(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*ListIntegratedServicesReply, error) + // Reserved and not implemented: + Teardown(ctx context.Context, in *TeardownRequest, opts ...grpc.CallOption) (*TeardownReply, error) + ModifyEnvironment(ctx context.Context, in *ModifyEnvironmentRequest, opts ...grpc.CallOption) (*ModifyEnvironmentReply, error) +} + +type controlClient struct { + cc grpc.ClientConnInterface +} + +func NewControlClient(cc grpc.ClientConnInterface) ControlClient { + return &controlClient{cc} +} + +func (c *controlClient) GetFrameworkInfo(ctx context.Context, in *GetFrameworkInfoRequest, opts ...grpc.CallOption) (*GetFrameworkInfoReply, error) { + out := new(GetFrameworkInfoReply) + err := c.cc.Invoke(ctx, Control_GetFrameworkInfo_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *controlClient) GetEnvironments(ctx context.Context, in *GetEnvironmentsRequest, opts ...grpc.CallOption) (*GetEnvironmentsReply, error) { + out := new(GetEnvironmentsReply) + err := c.cc.Invoke(ctx, Control_GetEnvironments_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *controlClient) NewAutoEnvironment(ctx context.Context, in *NewAutoEnvironmentRequest, opts ...grpc.CallOption) (*NewAutoEnvironmentReply, error) { + out := new(NewAutoEnvironmentReply) + err := c.cc.Invoke(ctx, Control_NewAutoEnvironment_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *controlClient) NewEnvironment(ctx context.Context, in *NewEnvironmentRequest, opts ...grpc.CallOption) (*NewEnvironmentReply, error) { + out := new(NewEnvironmentReply) + err := c.cc.Invoke(ctx, Control_NewEnvironment_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *controlClient) GetEnvironment(ctx context.Context, in *GetEnvironmentRequest, opts ...grpc.CallOption) (*GetEnvironmentReply, error) { + out := new(GetEnvironmentReply) + err := c.cc.Invoke(ctx, Control_GetEnvironment_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *controlClient) ControlEnvironment(ctx context.Context, in *ControlEnvironmentRequest, opts ...grpc.CallOption) (*ControlEnvironmentReply, error) { + out := new(ControlEnvironmentReply) + err := c.cc.Invoke(ctx, Control_ControlEnvironment_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *controlClient) DestroyEnvironment(ctx context.Context, in *DestroyEnvironmentRequest, opts ...grpc.CallOption) (*DestroyEnvironmentReply, error) { + out := new(DestroyEnvironmentReply) + err := c.cc.Invoke(ctx, Control_DestroyEnvironment_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *controlClient) GetActiveDetectors(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*GetActiveDetectorsReply, error) { + out := new(GetActiveDetectorsReply) + err := c.cc.Invoke(ctx, Control_GetActiveDetectors_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *controlClient) GetAvailableDetectors(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*GetAvailableDetectorsReply, error) { + out := new(GetAvailableDetectorsReply) + err := c.cc.Invoke(ctx, Control_GetAvailableDetectors_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *controlClient) NewEnvironmentAsync(ctx context.Context, in *NewEnvironmentRequest, opts ...grpc.CallOption) (*NewEnvironmentReply, error) { + out := new(NewEnvironmentReply) + err := c.cc.Invoke(ctx, Control_NewEnvironmentAsync_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *controlClient) GetTasks(ctx context.Context, in *GetTasksRequest, opts ...grpc.CallOption) (*GetTasksReply, error) { + out := new(GetTasksReply) + err := c.cc.Invoke(ctx, Control_GetTasks_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *controlClient) GetTask(ctx context.Context, in *GetTaskRequest, opts ...grpc.CallOption) (*GetTaskReply, error) { + out := new(GetTaskReply) + err := c.cc.Invoke(ctx, Control_GetTask_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *controlClient) CleanupTasks(ctx context.Context, in *CleanupTasksRequest, opts ...grpc.CallOption) (*CleanupTasksReply, error) { + out := new(CleanupTasksReply) + err := c.cc.Invoke(ctx, Control_CleanupTasks_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *controlClient) GetRoles(ctx context.Context, in *GetRolesRequest, opts ...grpc.CallOption) (*GetRolesReply, error) { + out := new(GetRolesReply) + err := c.cc.Invoke(ctx, Control_GetRoles_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *controlClient) GetWorkflowTemplates(ctx context.Context, in *GetWorkflowTemplatesRequest, opts ...grpc.CallOption) (*GetWorkflowTemplatesReply, error) { + out := new(GetWorkflowTemplatesReply) + err := c.cc.Invoke(ctx, Control_GetWorkflowTemplates_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *controlClient) ListRepos(ctx context.Context, in *ListReposRequest, opts ...grpc.CallOption) (*ListReposReply, error) { + out := new(ListReposReply) + err := c.cc.Invoke(ctx, Control_ListRepos_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *controlClient) AddRepo(ctx context.Context, in *AddRepoRequest, opts ...grpc.CallOption) (*AddRepoReply, error) { + out := new(AddRepoReply) + err := c.cc.Invoke(ctx, Control_AddRepo_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *controlClient) RemoveRepo(ctx context.Context, in *RemoveRepoRequest, opts ...grpc.CallOption) (*RemoveRepoReply, error) { + out := new(RemoveRepoReply) + err := c.cc.Invoke(ctx, Control_RemoveRepo_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *controlClient) RefreshRepos(ctx context.Context, in *RefreshReposRequest, opts ...grpc.CallOption) (*Empty, error) { + out := new(Empty) + err := c.cc.Invoke(ctx, Control_RefreshRepos_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *controlClient) SetDefaultRepo(ctx context.Context, in *SetDefaultRepoRequest, opts ...grpc.CallOption) (*Empty, error) { + out := new(Empty) + err := c.cc.Invoke(ctx, Control_SetDefaultRepo_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *controlClient) SetGlobalDefaultRevision(ctx context.Context, in *SetGlobalDefaultRevisionRequest, opts ...grpc.CallOption) (*Empty, error) { + out := new(Empty) + err := c.cc.Invoke(ctx, Control_SetGlobalDefaultRevision_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *controlClient) SetRepoDefaultRevision(ctx context.Context, in *SetRepoDefaultRevisionRequest, opts ...grpc.CallOption) (*SetRepoDefaultRevisionReply, error) { + out := new(SetRepoDefaultRevisionReply) + err := c.cc.Invoke(ctx, Control_SetRepoDefaultRevision_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *controlClient) Subscribe(ctx context.Context, in *SubscribeRequest, opts ...grpc.CallOption) (Control_SubscribeClient, error) { + stream, err := c.cc.NewStream(ctx, &Control_ServiceDesc.Streams[0], Control_Subscribe_FullMethodName, opts...) + if err != nil { + return nil, err + } + x := &controlSubscribeClient{stream} + if err := x.ClientStream.SendMsg(in); err != nil { + return nil, err + } + if err := x.ClientStream.CloseSend(); err != nil { + return nil, err + } + return x, nil +} + +type Control_SubscribeClient interface { + Recv() (*protos.Event, error) + grpc.ClientStream +} + +type controlSubscribeClient struct { + grpc.ClientStream +} + +func (x *controlSubscribeClient) Recv() (*protos.Event, error) { + m := new(protos.Event) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +func (c *controlClient) GetIntegratedServices(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*ListIntegratedServicesReply, error) { + out := new(ListIntegratedServicesReply) + err := c.cc.Invoke(ctx, Control_GetIntegratedServices_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *controlClient) Teardown(ctx context.Context, in *TeardownRequest, opts ...grpc.CallOption) (*TeardownReply, error) { + out := new(TeardownReply) + err := c.cc.Invoke(ctx, Control_Teardown_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *controlClient) ModifyEnvironment(ctx context.Context, in *ModifyEnvironmentRequest, opts ...grpc.CallOption) (*ModifyEnvironmentReply, error) { + out := new(ModifyEnvironmentReply) + err := c.cc.Invoke(ctx, Control_ModifyEnvironment_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// ControlServer is the server API for Control service. +// All implementations should embed UnimplementedControlServer +// for forward compatibility +type ControlServer interface { + GetFrameworkInfo(context.Context, *GetFrameworkInfoRequest) (*GetFrameworkInfoReply, error) + GetEnvironments(context.Context, *GetEnvironmentsRequest) (*GetEnvironmentsReply, error) + // Creates a new environment which automatically follows one STANDBY->RUNNING->DONE cycle in the state machine. + // It returns only once the environment reaches the CONFIGURED state or upon any earlier failure. + NewAutoEnvironment(context.Context, *NewAutoEnvironmentRequest) (*NewAutoEnvironmentReply, error) + // Creates a new environment. + // It returns only once the environment reaches the CONFIGURED state or upon any earlier failure. + NewEnvironment(context.Context, *NewEnvironmentRequest) (*NewEnvironmentReply, error) + GetEnvironment(context.Context, *GetEnvironmentRequest) (*GetEnvironmentReply, error) + ControlEnvironment(context.Context, *ControlEnvironmentRequest) (*ControlEnvironmentReply, error) + DestroyEnvironment(context.Context, *DestroyEnvironmentRequest) (*DestroyEnvironmentReply, error) + GetActiveDetectors(context.Context, *Empty) (*GetActiveDetectorsReply, error) + GetAvailableDetectors(context.Context, *Empty) (*GetAvailableDetectorsReply, error) + // Creates a new environment. + // It returns once an environment ID is created and continues the creation asynchronously to the call. + // The environment will be listed in GetEnvironments() only once the workflow is loaded and deployment starts. + NewEnvironmentAsync(context.Context, *NewEnvironmentRequest) (*NewEnvironmentReply, error) + GetTasks(context.Context, *GetTasksRequest) (*GetTasksReply, error) + GetTask(context.Context, *GetTaskRequest) (*GetTaskReply, error) + CleanupTasks(context.Context, *CleanupTasksRequest) (*CleanupTasksReply, error) + GetRoles(context.Context, *GetRolesRequest) (*GetRolesReply, error) + GetWorkflowTemplates(context.Context, *GetWorkflowTemplatesRequest) (*GetWorkflowTemplatesReply, error) + ListRepos(context.Context, *ListReposRequest) (*ListReposReply, error) + AddRepo(context.Context, *AddRepoRequest) (*AddRepoReply, error) + RemoveRepo(context.Context, *RemoveRepoRequest) (*RemoveRepoReply, error) + RefreshRepos(context.Context, *RefreshReposRequest) (*Empty, error) + SetDefaultRepo(context.Context, *SetDefaultRepoRequest) (*Empty, error) + SetGlobalDefaultRevision(context.Context, *SetGlobalDefaultRevisionRequest) (*Empty, error) + SetRepoDefaultRevision(context.Context, *SetRepoDefaultRevisionRequest) (*SetRepoDefaultRevisionReply, error) + Subscribe(*SubscribeRequest, Control_SubscribeServer) error + GetIntegratedServices(context.Context, *Empty) (*ListIntegratedServicesReply, error) + // Reserved and not implemented: + Teardown(context.Context, *TeardownRequest) (*TeardownReply, error) + ModifyEnvironment(context.Context, *ModifyEnvironmentRequest) (*ModifyEnvironmentReply, error) +} + +// UnimplementedControlServer should be embedded to have forward compatible implementations. +type UnimplementedControlServer struct { +} + +func (UnimplementedControlServer) GetFrameworkInfo(context.Context, *GetFrameworkInfoRequest) (*GetFrameworkInfoReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetFrameworkInfo not implemented") +} +func (UnimplementedControlServer) GetEnvironments(context.Context, *GetEnvironmentsRequest) (*GetEnvironmentsReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetEnvironments not implemented") +} +func (UnimplementedControlServer) NewAutoEnvironment(context.Context, *NewAutoEnvironmentRequest) (*NewAutoEnvironmentReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method NewAutoEnvironment not implemented") +} +func (UnimplementedControlServer) NewEnvironment(context.Context, *NewEnvironmentRequest) (*NewEnvironmentReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method NewEnvironment not implemented") +} +func (UnimplementedControlServer) GetEnvironment(context.Context, *GetEnvironmentRequest) (*GetEnvironmentReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetEnvironment not implemented") +} +func (UnimplementedControlServer) ControlEnvironment(context.Context, *ControlEnvironmentRequest) (*ControlEnvironmentReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method ControlEnvironment not implemented") +} +func (UnimplementedControlServer) DestroyEnvironment(context.Context, *DestroyEnvironmentRequest) (*DestroyEnvironmentReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method DestroyEnvironment not implemented") +} +func (UnimplementedControlServer) GetActiveDetectors(context.Context, *Empty) (*GetActiveDetectorsReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetActiveDetectors not implemented") +} +func (UnimplementedControlServer) GetAvailableDetectors(context.Context, *Empty) (*GetAvailableDetectorsReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetAvailableDetectors not implemented") +} +func (UnimplementedControlServer) NewEnvironmentAsync(context.Context, *NewEnvironmentRequest) (*NewEnvironmentReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method NewEnvironmentAsync not implemented") +} +func (UnimplementedControlServer) GetTasks(context.Context, *GetTasksRequest) (*GetTasksReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetTasks not implemented") +} +func (UnimplementedControlServer) GetTask(context.Context, *GetTaskRequest) (*GetTaskReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetTask not implemented") +} +func (UnimplementedControlServer) CleanupTasks(context.Context, *CleanupTasksRequest) (*CleanupTasksReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method CleanupTasks not implemented") +} +func (UnimplementedControlServer) GetRoles(context.Context, *GetRolesRequest) (*GetRolesReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetRoles not implemented") +} +func (UnimplementedControlServer) GetWorkflowTemplates(context.Context, *GetWorkflowTemplatesRequest) (*GetWorkflowTemplatesReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetWorkflowTemplates not implemented") +} +func (UnimplementedControlServer) ListRepos(context.Context, *ListReposRequest) (*ListReposReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method ListRepos not implemented") +} +func (UnimplementedControlServer) AddRepo(context.Context, *AddRepoRequest) (*AddRepoReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method AddRepo not implemented") +} +func (UnimplementedControlServer) RemoveRepo(context.Context, *RemoveRepoRequest) (*RemoveRepoReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method RemoveRepo not implemented") +} +func (UnimplementedControlServer) RefreshRepos(context.Context, *RefreshReposRequest) (*Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method RefreshRepos not implemented") +} +func (UnimplementedControlServer) SetDefaultRepo(context.Context, *SetDefaultRepoRequest) (*Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method SetDefaultRepo not implemented") +} +func (UnimplementedControlServer) SetGlobalDefaultRevision(context.Context, *SetGlobalDefaultRevisionRequest) (*Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method SetGlobalDefaultRevision not implemented") +} +func (UnimplementedControlServer) SetRepoDefaultRevision(context.Context, *SetRepoDefaultRevisionRequest) (*SetRepoDefaultRevisionReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method SetRepoDefaultRevision not implemented") +} +func (UnimplementedControlServer) Subscribe(*SubscribeRequest, Control_SubscribeServer) error { + return status.Errorf(codes.Unimplemented, "method Subscribe not implemented") +} +func (UnimplementedControlServer) GetIntegratedServices(context.Context, *Empty) (*ListIntegratedServicesReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetIntegratedServices not implemented") +} +func (UnimplementedControlServer) Teardown(context.Context, *TeardownRequest) (*TeardownReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method Teardown not implemented") +} +func (UnimplementedControlServer) ModifyEnvironment(context.Context, *ModifyEnvironmentRequest) (*ModifyEnvironmentReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method ModifyEnvironment not implemented") +} + +// UnsafeControlServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to ControlServer will +// result in compilation errors. +type UnsafeControlServer interface { + mustEmbedUnimplementedControlServer() +} + +func RegisterControlServer(s grpc.ServiceRegistrar, srv ControlServer) { + s.RegisterService(&Control_ServiceDesc, srv) +} + +func _Control_GetFrameworkInfo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetFrameworkInfoRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ControlServer).GetFrameworkInfo(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Control_GetFrameworkInfo_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).GetFrameworkInfo(ctx, req.(*GetFrameworkInfoRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Control_GetEnvironments_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetEnvironmentsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ControlServer).GetEnvironments(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Control_GetEnvironments_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).GetEnvironments(ctx, req.(*GetEnvironmentsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Control_NewAutoEnvironment_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(NewAutoEnvironmentRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ControlServer).NewAutoEnvironment(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Control_NewAutoEnvironment_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).NewAutoEnvironment(ctx, req.(*NewAutoEnvironmentRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Control_NewEnvironment_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(NewEnvironmentRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ControlServer).NewEnvironment(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Control_NewEnvironment_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).NewEnvironment(ctx, req.(*NewEnvironmentRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Control_GetEnvironment_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetEnvironmentRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ControlServer).GetEnvironment(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Control_GetEnvironment_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).GetEnvironment(ctx, req.(*GetEnvironmentRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Control_ControlEnvironment_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ControlEnvironmentRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ControlServer).ControlEnvironment(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Control_ControlEnvironment_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).ControlEnvironment(ctx, req.(*ControlEnvironmentRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Control_DestroyEnvironment_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DestroyEnvironmentRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ControlServer).DestroyEnvironment(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Control_DestroyEnvironment_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).DestroyEnvironment(ctx, req.(*DestroyEnvironmentRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Control_GetActiveDetectors_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(Empty) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ControlServer).GetActiveDetectors(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Control_GetActiveDetectors_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).GetActiveDetectors(ctx, req.(*Empty)) + } + return interceptor(ctx, in, info, handler) +} + +func _Control_GetAvailableDetectors_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(Empty) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ControlServer).GetAvailableDetectors(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Control_GetAvailableDetectors_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).GetAvailableDetectors(ctx, req.(*Empty)) + } + return interceptor(ctx, in, info, handler) +} + +func _Control_NewEnvironmentAsync_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(NewEnvironmentRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ControlServer).NewEnvironmentAsync(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Control_NewEnvironmentAsync_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).NewEnvironmentAsync(ctx, req.(*NewEnvironmentRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Control_GetTasks_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetTasksRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ControlServer).GetTasks(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Control_GetTasks_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).GetTasks(ctx, req.(*GetTasksRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Control_GetTask_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetTaskRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ControlServer).GetTask(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Control_GetTask_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).GetTask(ctx, req.(*GetTaskRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Control_CleanupTasks_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CleanupTasksRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ControlServer).CleanupTasks(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Control_CleanupTasks_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).CleanupTasks(ctx, req.(*CleanupTasksRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Control_GetRoles_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetRolesRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ControlServer).GetRoles(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Control_GetRoles_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).GetRoles(ctx, req.(*GetRolesRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Control_GetWorkflowTemplates_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetWorkflowTemplatesRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ControlServer).GetWorkflowTemplates(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Control_GetWorkflowTemplates_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).GetWorkflowTemplates(ctx, req.(*GetWorkflowTemplatesRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Control_ListRepos_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListReposRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ControlServer).ListRepos(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Control_ListRepos_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).ListRepos(ctx, req.(*ListReposRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Control_AddRepo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(AddRepoRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ControlServer).AddRepo(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Control_AddRepo_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).AddRepo(ctx, req.(*AddRepoRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Control_RemoveRepo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RemoveRepoRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ControlServer).RemoveRepo(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Control_RemoveRepo_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).RemoveRepo(ctx, req.(*RemoveRepoRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Control_RefreshRepos_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RefreshReposRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ControlServer).RefreshRepos(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Control_RefreshRepos_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).RefreshRepos(ctx, req.(*RefreshReposRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Control_SetDefaultRepo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SetDefaultRepoRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ControlServer).SetDefaultRepo(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Control_SetDefaultRepo_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).SetDefaultRepo(ctx, req.(*SetDefaultRepoRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Control_SetGlobalDefaultRevision_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SetGlobalDefaultRevisionRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ControlServer).SetGlobalDefaultRevision(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Control_SetGlobalDefaultRevision_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).SetGlobalDefaultRevision(ctx, req.(*SetGlobalDefaultRevisionRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Control_SetRepoDefaultRevision_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SetRepoDefaultRevisionRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ControlServer).SetRepoDefaultRevision(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Control_SetRepoDefaultRevision_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).SetRepoDefaultRevision(ctx, req.(*SetRepoDefaultRevisionRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Control_Subscribe_Handler(srv interface{}, stream grpc.ServerStream) error { + m := new(SubscribeRequest) + if err := stream.RecvMsg(m); err != nil { + return err + } + return srv.(ControlServer).Subscribe(m, &controlSubscribeServer{stream}) +} + +type Control_SubscribeServer interface { + Send(*protos.Event) error + grpc.ServerStream +} + +type controlSubscribeServer struct { + grpc.ServerStream +} + +func (x *controlSubscribeServer) Send(m *protos.Event) error { + return x.ServerStream.SendMsg(m) +} + +func _Control_GetIntegratedServices_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(Empty) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ControlServer).GetIntegratedServices(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Control_GetIntegratedServices_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).GetIntegratedServices(ctx, req.(*Empty)) + } + return interceptor(ctx, in, info, handler) +} + +func _Control_Teardown_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(TeardownRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ControlServer).Teardown(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Control_Teardown_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).Teardown(ctx, req.(*TeardownRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Control_ModifyEnvironment_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ModifyEnvironmentRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ControlServer).ModifyEnvironment(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Control_ModifyEnvironment_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).ModifyEnvironment(ctx, req.(*ModifyEnvironmentRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// Control_ServiceDesc is the grpc.ServiceDesc for Control service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var Control_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "o2control.Control", + HandlerType: (*ControlServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "GetFrameworkInfo", + Handler: _Control_GetFrameworkInfo_Handler, + }, + { + MethodName: "GetEnvironments", + Handler: _Control_GetEnvironments_Handler, + }, + { + MethodName: "NewAutoEnvironment", + Handler: _Control_NewAutoEnvironment_Handler, + }, + { + MethodName: "NewEnvironment", + Handler: _Control_NewEnvironment_Handler, + }, + { + MethodName: "GetEnvironment", + Handler: _Control_GetEnvironment_Handler, + }, + { + MethodName: "ControlEnvironment", + Handler: _Control_ControlEnvironment_Handler, + }, + { + MethodName: "DestroyEnvironment", + Handler: _Control_DestroyEnvironment_Handler, + }, + { + MethodName: "GetActiveDetectors", + Handler: _Control_GetActiveDetectors_Handler, + }, + { + MethodName: "GetAvailableDetectors", + Handler: _Control_GetAvailableDetectors_Handler, + }, + { + MethodName: "NewEnvironmentAsync", + Handler: _Control_NewEnvironmentAsync_Handler, + }, + { + MethodName: "GetTasks", + Handler: _Control_GetTasks_Handler, + }, + { + MethodName: "GetTask", + Handler: _Control_GetTask_Handler, + }, + { + MethodName: "CleanupTasks", + Handler: _Control_CleanupTasks_Handler, + }, + { + MethodName: "GetRoles", + Handler: _Control_GetRoles_Handler, + }, + { + MethodName: "GetWorkflowTemplates", + Handler: _Control_GetWorkflowTemplates_Handler, + }, + { + MethodName: "ListRepos", + Handler: _Control_ListRepos_Handler, + }, + { + MethodName: "AddRepo", + Handler: _Control_AddRepo_Handler, + }, + { + MethodName: "RemoveRepo", + Handler: _Control_RemoveRepo_Handler, + }, + { + MethodName: "RefreshRepos", + Handler: _Control_RefreshRepos_Handler, + }, + { + MethodName: "SetDefaultRepo", + Handler: _Control_SetDefaultRepo_Handler, + }, + { + MethodName: "SetGlobalDefaultRevision", + Handler: _Control_SetGlobalDefaultRevision_Handler, + }, + { + MethodName: "SetRepoDefaultRevision", + Handler: _Control_SetRepoDefaultRevision_Handler, + }, + { + MethodName: "GetIntegratedServices", + Handler: _Control_GetIntegratedServices_Handler, + }, + { + MethodName: "Teardown", + Handler: _Control_Teardown_Handler, + }, + { + MethodName: "ModifyEnvironment", + Handler: _Control_ModifyEnvironment_Handler, + }, + }, + Streams: []grpc.StreamDesc{ + { + StreamName: "Subscribe", + Handler: _Control_Subscribe_Handler, + ServerStreams: true, + }, + }, + Metadata: "protos/o2control.proto", +} diff --git a/core/repos/httpsrepo.go b/core/repos/httpsrepo.go new file mode 100644 index 000000000..d997f267c --- /dev/null +++ b/core/repos/httpsrepo.go @@ -0,0 +1,60 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2021 CERN and copyright holders of ALICE O². + * Author: Kostas Alexopoulos + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package repos + +import ( + "net/url" + "path" +) + +type httpsRepo struct { + Repo +} + +func (r *httpsRepo) getUri() string { + u, _ := url.Parse(r.GetProtocol() + "://") + u.Path = path.Join(u.Path, r.HostingSite) + + u.Path = path.Join(u.Path, + r.Path, + r.RepoName) + + uri := u.String() + + return uri + ".git" +} + +func (r *httpsRepo) GetProtocol() string { + return "https" +} + +/*func (r *httpsRepo) GetDplCommand(dplCommandUri string) (string, error) { + dplCommandPath := filepath.Join(r.GetCloneDir(), jitScriptsDir, dplCommandUri) + dplCommandPayload, err := os.ReadFile(dplCommandPath) + if err != nil { + return "", err + } + return string(dplCommandPayload), nil +}*/ diff --git a/core/repos/httpsrepo_test.go b/core/repos/httpsrepo_test.go new file mode 100644 index 000000000..f655ce5e9 --- /dev/null +++ b/core/repos/httpsrepo_test.go @@ -0,0 +1,177 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2021 CERN and copyright holders of ALICE O². + * Author: Kostas Alexopoulos + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package repos + +import ( + "net/url" + "path" + "testing" +) + +var httpTs = ts{ + Protocol: "https", + HostingSite: "github.com", + Path: "AliceO2Group", + RepoName: "ControlWorkflows", + DefaultRevision: "main", + RepoPath: "github.com/AliceO2Group/ControlWorkflows", + RepoPathDotGit: "github.com/AliceO2Group/ControlWorkflows.git", + ReposPath: "/var/lib/o2/aliecs/repos", +} + +func TestNewRepoInvalidUrl(t *testing.T) { + repoPathInvalid := "site.com/path" + _, _, err := NewRepo(repoPathInvalid, "", httpTs.ReposPath) + if err == nil { + t.Errorf("NewRepo() should fail if repo URL (non-file) can't be sliced into at least 3 parts") + } +} + +func TestNewRepoDotGit(t *testing.T) { + _, repo, err := NewRepo(httpTs.RepoPathDotGit, "", httpTs.ReposPath) + if err != nil { + t.Errorf("NewRepo() shouldn't fail with valid inputs") + return + } + + if repo.getRepoName() != httpTs.RepoName { + t.Errorf("Repo's name was %s instead of %s", repo.getRepoName(), httpTs.RepoName) + } +} + +func TestNewRepoWithRevision(t *testing.T) { + revision := "dummy-revision" + repoPath := httpTs.RepoPath + "@" + revision + + _, repo, err := NewRepo(repoPath, httpTs.DefaultRevision, httpTs.ReposPath) + if err != nil { + t.Errorf("Creating a new valid repo shouldn't error out") + return + } + + if repo.getRepoName() != httpTs.RepoName { + t.Errorf("Repo name was %s instead of %s", repo.getRepoName(), httpTs.RepoName) + return + } + + if repo.GetDefaultRevision() != httpTs.DefaultRevision { + t.Errorf("Repo's default revision was %s instead of %s", repo.getRevision(), httpTs.DefaultRevision) + return + } + + if repo.getRevision() != revision { + t.Errorf("Repo's revision should fall back to default revision, when latter specified. Got %s instead of %s", repo.getRevision(), revision) + } +} + +func TestNewRepoWithInvalidRevision(t *testing.T) { + revision := "dummy-revision" + repoPath := httpTs.RepoPath + "@" + revision + "@" + revision + + _, _, err := NewRepo(repoPath, httpTs.DefaultRevision, httpTs.ReposPath) + if err == nil { + t.Errorf("Creating a new repo with an invalid revision should error out") + return + } +} + +func TestNewRepoHttps(t *testing.T) { + _, repo, err := NewRepo(httpTs.RepoPath, httpTs.DefaultRevision, httpTs.ReposPath) + if err != nil { + t.Errorf("Creating a new valid repo shouldn't error out") + return + } + + if repo.GetProtocol() != httpTs.Protocol { + t.Errorf("Repo's default protocol was %s instead of %s", repo.GetProtocol(), httpTs.Protocol) + return + } + + if repo.getHostingSite() != httpTs.HostingSite { + t.Errorf("Repo's hosting site was %s instead of %s", repo.getHostingSite(), httpTs.HostingSite) + return + } + + if repo.getPath() != httpTs.Path { + t.Errorf("Repo's path was %s instead of %s", repo.getPath(), httpTs.Path) + return + } + + if repo.getRepoName() != httpTs.RepoName { + t.Errorf("Repo name was %s instead of %s", repo.getRepoName(), httpTs.RepoName) + return + } + + if repo.GetDefaultRevision() != httpTs.DefaultRevision { + t.Errorf("Repo's default revision was %s instead of %s", repo.getRevision(), httpTs.DefaultRevision) + return + } + + if repo.getRevision() != repo.GetDefaultRevision() { + t.Errorf("Repo's revision should fall back to default revision, when latter specified. Got %s instead of %s", repo.getRevision(), repo.GetDefaultRevision()) + } +} + +func TestGetUriHttps(t *testing.T) { + _, repo, _ := NewRepo(httpTs.RepoPath, "", httpTs.ReposPath) + expectedUri := "https://" + httpTs.HostingSite + "/" + httpTs.Path + "/" + httpTs.RepoName + ".git" + + if repo.getUri() != expectedUri { + t.Errorf("Got %s URI instead of %s", repo.getUri(), expectedUri) + } +} + +func TestGetUriHttpsWithDotGit(t *testing.T) { + _, repo, _ := NewRepo(httpTs.RepoPathDotGit, "", httpTs.ReposPath) + u, _ := url.Parse(httpTs.Protocol + "://") + u.Path = path.Join(u.Path, + httpTs.HostingSite, + httpTs.Path, + httpTs.RepoName) + expectedUri := u.String() + ".git" + + if repo.getUri() != expectedUri { + t.Errorf("Got %s URI instead of %s", repo.getUri(), expectedUri) + } + +} + +func TestGetIdentifierHttps(t *testing.T) { + _, repo, _ := NewRepo(httpTs.RepoPath, "", httpTs.ReposPath) + expectedIdentifier := path.Join(httpTs.HostingSite, httpTs.Path, httpTs.RepoName) + + if repo.GetIdentifier() != expectedIdentifier { + t.Errorf("Got identifier %s instead of %s", repo.GetIdentifier(), expectedIdentifier) + } +} + +func TestGetIdentifierHttpsDotGit(t *testing.T) { + _, repo, _ := NewRepo(httpTs.RepoPathDotGit, "", httpTs.ReposPath) + expectedIdentifier := path.Join(httpTs.HostingSite, httpTs.Path, httpTs.RepoName) + + if repo.GetIdentifier() != expectedIdentifier { + t.Errorf("Got identifier %s instead of %s", repo.GetIdentifier(), expectedIdentifier) + } +} diff --git a/core/repos/localrepo.go b/core/repos/localrepo.go new file mode 100644 index 000000000..2e4998b6d --- /dev/null +++ b/core/repos/localrepo.go @@ -0,0 +1,184 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2021 CERN and copyright holders of ALICE O². + * Author: Kostas Alexopoulos + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package repos + +import ( + "fmt" + "github.com/google/uuid" + "io/ioutil" + "net/url" + "os" + "path" + "path/filepath" + "strings" + "time" +) + +type localRepo struct { + Repo +} + +func (r *localRepo) GetCloneDir() string { + cloneDir := filepath.Join(r.Path, r.RepoName) + return cloneDir +} + +func (r *localRepo) getUri() string { + u, _ := url.Parse(r.GetProtocol() + "://") + u.Path = path.Join(u.Path, r.HostingSite) + + u.Path = path.Join(u.Path, + r.Path, + r.RepoName) + + uri := u.String() + + return uri + ".git" +} + +func (r *localRepo) getWorkflowDir() string { + return filepath.Join(r.GetCloneDir(), "workflows") +} + +func (r *localRepo) ResolveTaskClassIdentifier(loadTaskClass string) (taskClassIdentifier string) { + if !strings.Contains(loadTaskClass, "/") { + taskClassIdentifier = filepath.Join(r.GetIdentifier(), + "tasks", + loadTaskClass) + } else { + taskClassIdentifier = loadTaskClass + } + + taskClassIdentifier += "@" + r.GetHash() + + return +} + +func (r *localRepo) checkoutRevision(string) error { + // noop for local repo + return nil +} + +func (r *localRepo) refresh() error { + // noop for local repo + return nil +} + +func (r *localRepo) GetProtocol() string { + return "local" +} + +const timeout = 10 * time.Second + +var timer = time.NewTimer(timeout) +var uuidHash string + +// GetHash returns a random UUID so that tasks may never be reused +func (r *localRepo) GetHash() string { + + // Generate a UUID and keep it generated for some time + // This is to allow task matching while creating an environment + // If a request comes while the timer is running, reset the timer + // New generation happens on-demand + + select { + case <-timer.C: + // timer is finished generate a new UUID + nuuid, _ := uuid.NewUUID() + uuidHash = nuuid.String() + timer.Reset(timeout) + default: + // simply reset timeout, keeping existing UUID + timer.Reset(timeout) + } + + return uuidHash +} + +// getRevisions should make sure that the revisions always contain only "local" +// the placeholder for the localRepo revision +func (r *localRepo) getRevisions(string, []string) ([]string, error) { + r.Revisions = []string{"local"} + return r.Revisions, nil +} + +func (r *localRepo) GetTaskTemplatePath(taskClassFile string) string { + return taskClassFile + +} + +// getWorkflows should always scan for templates, the concept of a template cache is not applicable here +// moreover, revision should always be "local", which is a placeholder for saying no revision applies +func (r *localRepo) getWorkflows(revisionPattern string, _ []string, _ bool) (TemplatesByRevision, error) { + if revisionPattern != r.GetDefaultRevision() || revisionPattern != "local" { + return nil, fmt.Errorf("only 'local' revision accepted for local repos") + } + + revision := "local" + templates := make(TemplatesByRevision) + + // Go through the filesystem to locate available templates + files, err := ioutil.ReadDir(r.getWorkflowDir()) + if err != nil { + return nil, err + } + for _, file := range files { + // Only return .yaml files + if strings.HasSuffix(file.Name(), ".yaml") { + templateName := strings.TrimSuffix(file.Name(), ".yaml") + workflowPath := filepath.Join(r.getWorkflowDir(), file.Name()) + isPublic, description, varSpecMap, err := ParseWorkflowPublicVariableInfo(workflowPath) + if err != nil { + return nil, err + } + if isPublic { + templates[RevisionKey(revision)] = append(templates[RevisionKey(revision)], + Template{templateName, description, isPublic, varSpecMap}) + } + } + } + + return templates, nil +} + +func (r *localRepo) GetDplCommand(dplCommandUri string) (string, error) { + dplCommandPath := filepath.Join(r.GetCloneDir(), jitScriptsDir, dplCommandUri) + dplCommandPayload, err := os.ReadFile(dplCommandPath) + if err != nil { + return "", err + } + return string(dplCommandPayload), nil +} + +func (r *localRepo) ResolveSubworkflowTemplateIdentifier(workflowTemplateExpr string) string { + expr := workflowTemplateExpr + if !strings.Contains(expr, "/") { + expr = filepath.Join(r.GetIdentifier(), + "workflows", + expr) + } + + return expr +} diff --git a/core/repos/localrepo_test.go b/core/repos/localrepo_test.go new file mode 100644 index 000000000..0ad643170 --- /dev/null +++ b/core/repos/localrepo_test.go @@ -0,0 +1,102 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2021 CERN and copyright holders of ALICE O². + * Author: Kostas Alexopoulos + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package repos + +import ( + "net/url" + "path" + "testing" +) + +var localTs = ts{ + Protocol: "local", + HostingSite: "", + Path: "/home/user/git", + RepoName: "ControlWorkflows", + DefaultRevision: "local", + RepoPath: "/home/user/git/ControlWorkflows", + RepoPathDotGit: "/home/user/git/ControlWorkflows.git", + ReposPath: "/var/lib/o2/aliecs/repos", +} + +func TestNewRepoLocal(t *testing.T) { + _, repo, err := NewRepo(localTs.RepoPath, localTs.DefaultRevision, httpTs.ReposPath) + if err != nil { + t.Errorf("Creating a new valid repo shouldn't error out") + return + } + + if repo.GetProtocol() != localTs.Protocol { + t.Errorf("Repo's default protocol was %s instead of %s", repo.GetProtocol(), localTs.Protocol) + return + } + + if repo.getHostingSite() != localTs.HostingSite { + t.Errorf("Repo's hosting site was %s instead of %s", repo.getHostingSite(), localTs.HostingSite) + return + } + + if repo.getPath() != localTs.Path { + t.Errorf("Repo's path was %s instead of %s", repo.getPath(), localTs.Path) + return + } + + if repo.getRepoName() != localTs.RepoName { + t.Errorf("Repo name was %s instead of %s", repo.getRepoName(), localTs.RepoName) + return + } + + if repo.GetDefaultRevision() != localTs.DefaultRevision { + t.Errorf("Repo's default revision was %s instead of %s", repo.getRevision(), localTs.DefaultRevision) + return + } + + if repo.getRevision() != repo.GetDefaultRevision() { + t.Errorf("Repo's revision should fall back to default revision, when latter specified. Got %s instead of %s", repo.getRevision(), repo.GetDefaultRevision()) + } +} + +func TestGetIdentifierLocal(t *testing.T) { + _, repo, _ := NewRepo(localTs.RepoPath, "", httpTs.ReposPath) + expectedIdentifier := localTs.Path + "/" + localTs.RepoName + + if repo.GetIdentifier() != expectedIdentifier { + t.Errorf("Got identifier %s instead of %s", repo.GetIdentifier(), expectedIdentifier) + } +} + +func TestGetUriLocal(t *testing.T) { + _, repo, _ := NewRepo(localTs.RepoPath, "", httpTs.ReposPath) + u, _ := url.Parse(localTs.Protocol + "://") + u.Path = path.Join(u.Path, + localTs.HostingSite, + localTs.Path, + localTs.RepoName) + expectedUri := u.String() + ".git" + + if repo.getUri() != expectedUri { + t.Errorf("Got %s URI instead of %s", repo.getUri(), expectedUri) + } +} diff --git a/core/repos/repo.go b/core/repos/repo.go index 01ca79e59..55608de98 100644 --- a/core/repos/repo.go +++ b/core/repos/repo.go @@ -26,121 +26,262 @@ package repos import ( "errors" - "github.com/spf13/viper" - "gopkg.in/src-d/go-git.v4" - "gopkg.in/src-d/go-git.v4/plumbing" "io/ioutil" + "net/url" + "os" + "os/exec" + "path" + "path/filepath" + "regexp" "strings" + + "github.com/go-git/go-git/v5" + "github.com/go-git/go-git/v5/plumbing" + "github.com/gobwas/glob" ) +type iRepo interface { + IRepo + getCloneParentDirs() []string + getUri() string + getWorkflowDir() string + checkoutRevision(string) error + refresh() error + gatherRevisions(*git.Repository) error + populateWorkflows(string, bool) error + getWorkflows(string, []string, bool) (TemplatesByRevision, error) + getHostingSite() string + + getPath() string + getRepoName() string + getRevision() string + setRevision(string) + getRevisions(string, []string) ([]string, error) + //getAndSetDefaultRevisionFromRs() string + setDefaultRevision(string) + updateDefaultRevision(string) (string, error) + setDefault(bool) +} + +type IRepo interface { + GetIdentifier() string + GetCloneDir() string + ResolveTaskClassIdentifier(string) string + ResolveSubworkflowTemplateIdentifier(string) string + GetProtocol() string + GetHash() string + GetRevisions() []string + GetDefaultRevision() string + IsDefault() bool + GetTaskTemplatePath(string) string + GetDplCommand(string) (string, error) +} + type Repo struct { - HostingSite string - User string - RepoName string - Revision string - Default bool + HostingSite string + Path string + RepoName string + Revision string + DefaultRevision string + Hash string + Default bool + Revisions []string + ReposPath string + Protocol string } -func NewRepo(repoPath string) (*Repo, error) { +const jitScriptsDir = "jit" + +func resolveProtocolFromPath(repoPath string) string { + // https + // starts with github.com/gitlab.cern.ch + // i.e. has a '.' and no `:` before the first '/' + //[^.\/]*\.[^\/:]*\/.* + httpsRegex := `[^.\/]*\.[^\/:]*\/.*` + re := regexp.MustCompile(httpsRegex) + if re.Match([]byte(repoPath)) { + return "https" + } - revSlice := strings.Split(repoPath, "@") + // ssh + // starts with "hostname:" + // i.e. has a single ":" before the path + //[^:]*:[^:]* + sshRegex := `[^:]*:[^:]*` + re = regexp.MustCompile(sshRegex) + if re.Match([]byte(repoPath)) { + return "ssh" + } + + // local + return "local" +} + +func newRepo(repoPath string, defaultRevision string, reposPath string) (iRepo, error) { + + protocol, newRepo, err := NewRepo(repoPath, defaultRevision, reposPath) + if err != nil { + return nil, err + } + + if protocol == "ssh" { + sshRepo := &sshRepo{ + Repo: newRepo, + } + return sshRepo, nil + } else if protocol == "https" { + httpsRepo := &httpsRepo{ + Repo: newRepo, + } + return httpsRepo, nil + } else if protocol == "local" { + localRepo := &localRepo{ + Repo: newRepo, + } + return localRepo, nil + } + + return &newRepo, nil +} + +func NewRepo(repoPath string, defaultRevision string, reposPath string) (string, Repo, error) { + + // Repo url resolution uses splitAfter(), so if the repoPath ends with "/", it will be split to an extra "" element + // messing up the resolution. Trim the potential suffix to mitigate the issue + repoPath = strings.TrimSuffix(repoPath, "/") + + protocol := resolveProtocolFromPath(repoPath) + if protocol == "local" { + defaultRevision = "local" + } var repoUrlSlice []string var revision string - if len(revSlice) == 2 { //revision specified + + revSlice := strings.Split(repoPath, "@") + + if len(revSlice) == 2 { //revision specified in the repo path repoUrlSlice = strings.Split(revSlice[0], "/") revision = revSlice[1] - } else if len(revSlice) == 1{ //no revision specified + } else if len(revSlice) == 1 { //no revision specified in the repo path repoUrlSlice = strings.Split(revSlice[0], "/") - revision = "master" + revision = defaultRevision } else { - return &Repo{}, errors.New("Repo path resolution failed") + return "", Repo{}, errors.New("repo path resolution failed") + } + + // Discard the "/tasks*" part of the repoPath if this comes from a taskClass + tasksClassSlice := strings.Split(revSlice[0], "/tasks") + + if protocol == "https" { + repoUrlSlice = strings.Split(tasksClassSlice[0], "/") + } else if protocol == "ssh" { + sshSlice := strings.SplitAfter(tasksClassSlice[0], ":") + repoUrlSlice = strings.SplitAfter(sshSlice[1], "/") + repoUrlSlice = append([]string{sshSlice[0]}, repoUrlSlice...) + } else { // protocol == "local" + repoUrlSlice = strings.SplitAfter(tasksClassSlice[0], "/") + repoUrlSlice = append([]string{""}, repoUrlSlice...) } if len(repoUrlSlice) < 3 { - return &Repo{}, errors.New("Repo path resolution failed") + return "", Repo{}, errors.New("repo path resolution failed") + } + + newRepo := Repo{ + HostingSite: repoUrlSlice[0], + Path: path.Join(repoUrlSlice[1 : len(repoUrlSlice)-1]...), + RepoName: strings.TrimSuffix(repoUrlSlice[len(repoUrlSlice)-1], ".git"), + Revision: revision, + DefaultRevision: defaultRevision, + ReposPath: reposPath, + Protocol: protocol, } - return &Repo{repoUrlSlice[0], repoUrlSlice[1], - repoUrlSlice[2], revision, false}, nil + return protocol, newRepo, nil } func (r *Repo) GetIdentifier() string { - identifier := r.HostingSite + "/" + r.User + "/" + r.RepoName + "/" - - return identifier + return path.Join(r.HostingSite, r.Path, r.RepoName) } -func (r *Repo) getCloneDir() string { - cloneDir := viper.GetString("repositoriesPath") - if cloneDir[len(cloneDir)-1:] != "/" { - cloneDir += "/" - } - - cloneDir += r.HostingSite + "/" + r.User + "/" +r.RepoName - +func (r *Repo) GetCloneDir() string { + var cloneDir string + cloneDir = r.ReposPath + cloneDir = filepath.Join(cloneDir, r.HostingSite, r.Path, r.RepoName) return cloneDir } func (r *Repo) getCloneParentDirs() []string { - cleanDir := viper.GetString("repositoriesPath") - if cleanDir[len(cleanDir)-1:] != "/" { - cleanDir += "/" - } + cleanDir := r.ReposPath + + cleanDirHostingSite := filepath.Join(cleanDir, r.HostingSite) - cleanDirUser := cleanDir + - r.HostingSite + "/" + - r.User + cleanDir = cleanDirHostingSite + dirs := strings.Split(strings.TrimPrefix(r.Path, "/"), "/") + var cleanDirs []string - cleanDirHostingSite := cleanDir + - r.HostingSite + for _, d := range dirs { + cleanDir = filepath.Join(cleanDir, d) + cleanDirs = append([]string{cleanDir}, cleanDirs...) + } + cleanDirs = append(cleanDirs, cleanDirHostingSite) - ret := make([]string, 2) - ret[0] = cleanDirUser - ret[1] = cleanDirHostingSite - return ret + return cleanDirs } -func (r *Repo) getUrl() string { - return "https://" + - r.HostingSite + "/" + - r.User + "/" + - r.RepoName + ".git" +func (r *Repo) getUri() string { + u, _ := url.Parse(r.GetProtocol() + "://") + u.Path = path.Join(u.Path, r.HostingSite) + + u.Path = path.Join(u.Path, + r.Path, + r.RepoName) + + uri := u.String() + + return uri + ".git" } func (r *Repo) getWorkflowDir() string { - return r.getCloneDir() + "/workflows/" + return filepath.Join(r.GetCloneDir(), "workflows") } func (r *Repo) ResolveTaskClassIdentifier(loadTaskClass string) (taskClassIdentifier string) { if !strings.Contains(loadTaskClass, "/") { - taskClassIdentifier = r.HostingSite + "/" + r.User + "/" + r.RepoName + "/tasks/" + loadTaskClass + taskClassIdentifier = filepath.Join(r.GetIdentifier(), + "tasks", + loadTaskClass) } else { taskClassIdentifier = loadTaskClass } - if !strings.Contains(loadTaskClass, "@") { - if r.Revision == "" { - taskClassIdentifier += "@master" - } else { - taskClassIdentifier += "@" + r.Revision - } - } + taskClassIdentifier += "@" + r.Hash return } +func (r *Repo) ResolveSubworkflowTemplateIdentifier(workflowTemplateExpr string) string { + expr := workflowTemplateExpr + if !strings.Contains(expr, "/") { + expr = filepath.Join(r.GetIdentifier(), + "workflows", + expr) + } + + if !strings.Contains(expr, "@") { + expr += "@" + r.Hash + } + + return expr +} + func (r *Repo) checkoutRevision(revision string) error { if revision == "" { revision = r.Revision } - ref, err := git.PlainOpen(r.getCloneDir()) - if err != nil { - return err - } - - w, err := ref.Worktree() + ref, err := git.PlainOpen(r.GetCloneDir()) if err != nil { return err } @@ -148,59 +289,323 @@ func (r *Repo) checkoutRevision(revision string) error { //Try remotely as a priority (branches) so that we don't check out old, dangling branch refs (e.g. master) newHash, err := ref.ResolveRevision(plumbing.Revision("origin/" + revision)) if err != nil { - //Try locally (tags + hashes) + //Try locally (tags + hashes) newHash, err = ref.ResolveRevision(plumbing.Revision(revision)) if err != nil { return errors.New("checkoutRevision: " + err.Error()) } } - err = w.Checkout(&git.CheckoutOptions{ - Hash: *newHash, - Force: true, - }) + coCmd := exec.Command("git", "-C", r.GetCloneDir(), "checkout", newHash.String()) + err = coCmd.Run() if err != nil { - return err + //try force in the (unlikely) case that something went wrong + coCmd = exec.Command("git", "-C", r.GetCloneDir(), "checkout", "-f", newHash.String()) + err = coCmd.Run() + if err != nil { + return err + } } - r.Revision = revision //Update repo revision + r.Hash = newHash.String() //Update repo hash + r.Revision = revision //Update repo revision return nil } func (r *Repo) refresh() error { + ref, err := git.PlainOpen(r.GetCloneDir()) + if err != nil { + return errors.New(err.Error() + ": " + r.GetIdentifier()) + } - ref, err := git.PlainOpen(r.getCloneDir()) + // clean the repo before doing anything + // this removes the untracked JIT-produced tasks and workflows + clnCmd := exec.Command("git", "-C", r.GetCloneDir(), "clean", "-f") + err = clnCmd.Run() if err != nil { return errors.New(err.Error() + ": " + r.GetIdentifier()) } err = ref.Fetch(&git.FetchOptions{ RemoteName: "origin", - Force: true, + Force: true, }) - if err != nil && err.Error() != "already up-to-date" { + if err != nil && err != git.NoErrAlreadyUpToDate { return errors.New(err.Error() + ": " + r.GetIdentifier() + " | revision: " + r.Revision) } - err = r.checkoutRevision("master") + // gather revisions on update or if empty + if err != git.NoErrAlreadyUpToDate || r.Revisions == nil { + err = r.gatherRevisions(ref) + if err != nil { + return err + } + } + + // populate workflows on update or if empty + if err != git.NoErrAlreadyUpToDate || len(templatesCache) == 0 { + err = r.populateWorkflows(r.GetDefaultRevision(), true) + if err != nil { + return err + } + } + + return nil +} + +func (r *Repo) gatherRevisions(ref *git.Repository) error { + + var err error + if ref == nil { + ref, err = git.PlainOpen(r.GetCloneDir()) + if err != nil { + return errors.New(err.Error() + ": " + r.GetIdentifier()) + } + } + + var revs []string + refs, err := ref.References() + if err != nil { + return err + } + err = refs.ForEach(func(ref *plumbing.Reference) error { + // The HEAD is omitted in a `git show-ref` so we ignore the symbolic + // references, the HEAD + if ref.Type() == plumbing.SymbolicReference { + return nil + } + + nameSlice := strings.Split(ref.Name().String(), "/") + if len(nameSlice) >= 2 && nameSlice[len(nameSlice)-2] == "origin" { + revs = append(revs, nameSlice[len(nameSlice)-1]) + } + + return nil + }) + if err != nil { + return err + } + + r.Revisions = revs + return nil +} + +// cache holding the repo's templates +var templatesCache TemplatesByRevision + +// updates templatesCache +// triggered with a repo refresh() +func (r *Repo) populateWorkflows(revisionPattern string, clear bool) error { + // Initialize or clear(!) our cache + if clear || len(templatesCache) == 0 { + templatesCache = make(TemplatesByRevision) + } + + // Include all types of git references + var gitRefs []string + gitRefs = append(gitRefs, refRemotePrefix, refTagPrefix) + + // Include all revisions + revisionsMatched, err := r.getRevisions(revisionPattern, gitRefs) if err != nil { return err } + for _, revision := range revisionsMatched { + // Checkout the revision + err := r.checkoutRevision(revision) + if err != nil { + return err + } + + // Go through the filesystem to locate available templates + var files []os.FileInfo + files, err = ioutil.ReadDir(r.getWorkflowDir()) + if err != nil { + return err + } + for _, file := range files { + // Only return .yaml files + if strings.HasSuffix(file.Name(), ".yaml") { + templateName := strings.TrimSuffix(file.Name(), ".yaml") + workflowPath := filepath.Join(r.getWorkflowDir(), file.Name()) + isPublic, description, varSpecMap, err := ParseWorkflowPublicVariableInfo(workflowPath) + if err != nil { + return err + } + templatesCache[RevisionKey(revision)] = append(templatesCache[RevisionKey(revision)], + Template{templateName, description, isPublic, varSpecMap}) + } + } + } return nil } -func (r *Repo) getWorkflows() ([]string, error) { - var workflows []string - files, err := ioutil.ReadDir(r.getWorkflowDir()) +// Returns a map of revision->[]templates for the repo +func (r *Repo) getWorkflows(revisionPattern string, gitRefs []string, allWorkflows bool) (TemplatesByRevision, error) { + // Get a list of revisions (branches/tags/hash) that are matched by the revisionPattern; gitRefs filter branches and/or tags + revisionsMatched, err := r.getRevisions(revisionPattern, gitRefs) + if err != nil { + return nil, err + } + + templates := make(TemplatesByRevision) + for _, revision := range revisionsMatched { + if len(templatesCache[RevisionKey(revision)]) == 0 { + err = r.populateWorkflows(revision, false) + if err != nil { + return nil, err + } + } + for _, template := range templatesCache[RevisionKey(revision)] { + // Check if workflow is public in case not allWorkflows requested + // and skip it if it isn't + if allWorkflows || (!allWorkflows && template.Public) { + templates[RevisionKey(revision)] = append(templates[RevisionKey(revision)], template) + } + } + } + return templates, nil +} + +func (r *Repo) getHostingSite() string { + return r.HostingSite +} + +func (r *Repo) GetProtocol() string { + return r.Protocol +} + +func (r *Repo) getPath() string { + return r.Path +} + +func (r *Repo) getRepoName() string { + return r.RepoName +} + +func (r *Repo) GetHash() string { + return r.Hash +} + +func (r *Repo) getRevision() string { + return r.Revision +} + +func (r *Repo) setRevision(revision string) { + r.Revision = revision +} + +func (r *Repo) getRevisions(revisionPattern string, refPrefixes []string) ([]string, error) { + var revisions []string + + // get a handle of the repo for go-git + ref, err := git.PlainOpen(r.GetCloneDir()) if err != nil { - return workflows, err + return nil, errors.New(err.Error() + ": " + r.GetIdentifier()) } - for _, file := range files { - if strings.HasSuffix(file.Name(), ".yaml") { // Only return .yaml files - workflows = append(workflows, strings.TrimSuffix(file.Name(), ".yaml")) + + // Check if the revision pattern is actually a hash. If so return a single revision + hashMaybe := plumbing.NewHash(revisionPattern) + resolvedHash, err := ref.ResolveRevision(plumbing.Revision(revisionPattern)) + if err == nil && *resolvedHash == hashMaybe { + revisions := append(revisions, resolvedHash.String()) + return revisions, nil + } + + // If not get a list of git references and loop through them to try and find a match + refs, err := ref.References() + if err != nil { + return nil, errors.New(err.Error() + ": " + r.GetIdentifier()) + } + + // Function that uses the go-git interface for iterating through the references and will populate the revisions slice + err = refs.ForEach(func(ref *plumbing.Reference) error { + if ref.Type() == plumbing.SymbolicReference { // go-git docs suggests this to skip HEAD, but HEAD makes it anyway + return nil } + + refString := ref.Name().String() + g := glob.MustCompile(revisionPattern) + + // Function to check the prefix of a git reference to filter branches and/or tags + resolveRef := func(refString, prefix string) (string, bool) { + if strings.HasPrefix(refString, prefix) { + return strings.Split(refString, prefix)[1], true + } + return refString, false + } + + // Loop through the desirable reference prefixes {/refs/tags, refs/remotes/origin} and look for a match + for _, refPrefix := range refPrefixes { + if resolvedRefString, ok := resolveRef(refString, refPrefix); ok { + // In case of a match check the resolved reference string (stripped of the /refs/* prefix) + // against the revision pattern provided + if g.Match(resolvedRefString) { + revisions = append(revisions, resolvedRefString) + break + } + } + } + + return nil + }) + + return revisions, nil +} + +func (r *Repo) GetRevisions() []string { + return r.Revisions +} + +func (r *Repo) GetDefaultRevision() string { + + return r.DefaultRevision +} + +// setDefaultRevision simply assigns, doesn't check +func (r *Repo) setDefaultRevision(revision string) { + r.DefaultRevision = revision +} + +func (r *Repo) updateDefaultRevision(revision string) (string, error) { + var refs []string + refs = append(refs, refRemotePrefix, refTagPrefix) + revisionsMatched, err := r.getRevisions(revision, refs) + if err != nil { + return "", err + } else if len(revisionsMatched) == 0 { + revisionsMatched, err = r.getRevisions("*", refs) + var availableRevs string + for _, rev := range revisionsMatched { + availableRevs += rev + "\n" + } + return availableRevs, errors.New("revision not found") + } + + r.DefaultRevision = revision + return "", nil +} + +func (r *Repo) IsDefault() bool { + return r.Default +} + +func (r *Repo) setDefault(def bool) { + r.Default = def +} + +func (r *Repo) GetTaskTemplatePath(taskClassFile string) string { + return filepath.Join(r.ReposPath, taskClassFile) + +} + +func (r *Repo) GetDplCommand(dplCommandUri string) (string, error) { + dplCommandPath := filepath.Join(r.GetCloneDir(), jitScriptsDir, dplCommandUri) + dplCommandPayload, err := os.ReadFile(dplCommandPath) + if err != nil { + return "", err } - return workflows, nil -} \ No newline at end of file + return string(dplCommandPayload), nil +} diff --git a/core/repos/repo_test.go b/core/repos/repo_test.go new file mode 100644 index 000000000..5636a4339 --- /dev/null +++ b/core/repos/repo_test.go @@ -0,0 +1,37 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2021 CERN and copyright holders of ALICE O². + * Author: Kostas Alexopoulos + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package repos + +// ts is a helper testing struct for the repos package +type ts struct { + Protocol string + HostingSite string + Path string + RepoName string + DefaultRevision string + RepoPath string + RepoPathDotGit string + ReposPath string +} diff --git a/core/repos/repomanager.go b/core/repos/repomanager.go index 3a5d9b098..afed8ccb3 100644 --- a/core/repos/repomanager.go +++ b/core/repos/repomanager.go @@ -26,28 +26,40 @@ package repos import ( "errors" + "fmt" "github.com/AliceO2Group/Control/common/logger" "github.com/AliceO2Group/Control/common/utils" - "github.com/AliceO2Group/Control/core/confsys" + "github.com/AliceO2Group/Control/configuration" + "github.com/go-git/go-git/v5" + "github.com/go-git/go-git/v5/config" + "github.com/go-git/go-git/v5/plumbing/transport/ssh" + "github.com/gobwas/glob" "github.com/sirupsen/logrus" "github.com/spf13/viper" - "gopkg.in/src-d/go-git.v4" - "gopkg.in/src-d/go-git.v4/plumbing" + ssh2 "golang.org/x/crypto/ssh" + "golang.org/x/sys/unix" "os" "path/filepath" "sort" + "strconv" "strings" "sync" ) -var log = logger.New(logrus.StandardLogger(),"repos") +const ( + refPrefix = "refs/" + refTagPrefix = refPrefix + "tags/" + refRemotePrefix = refPrefix + "remotes/origin/" +) + +var log = logger.New(logrus.StandardLogger(), "repos") var ( - once sync.Once + once sync.Once instance *RepoManager ) -func Instance(service *confsys.Service) *RepoManager { +func Instance(service configuration.Service) *RepoManager { once.Do(func() { instance = initializeRepos(service) }) @@ -55,25 +67,47 @@ func Instance(service *confsys.Service) *RepoManager { } type RepoManager struct { - repoList map[string]*Repo - defaultRepo *Repo - mutex sync.Mutex - cService *confsys.Service + repoList map[string]iRepo + defaultRepo iRepo + defaultRevision string + defaultRevisions map[string]string + mutex sync.Mutex + cService configuration.Service + rService *RepoService } -func initializeRepos(service *confsys.Service) *RepoManager { - rm := RepoManager{repoList: map[string]*Repo {}} +func initializeRepos(service configuration.Service) *RepoManager { + rm := RepoManager{repoList: map[string]iRepo{}} rm.cService = service + rm.rService = &RepoService{Svc: service} + + var err error + // Get global default revision + rm.defaultRevision, err = rm.rService.GetDefaultRevision() + if err != nil || rm.defaultRevision == "" { + log.Debug("Failed to parse default_revision from backend") + rm.defaultRevision = viper.GetString("globalDefaultRevision") + } else { + viper.Set("globalDefaultRevision", rm.defaultRevision) + } - // Get default repo - defaultRepo, err := rm.cService.GetDefaultRepo() + // Get default revisions + revsMap, err := rm.rService.GetRepoDefaultRevisions() if err != nil { + log.Debug("Failed to parse default_revisions from backend") + rm.defaultRevisions = make(map[string]string) + } else { + rm.defaultRevisions = revsMap + } + + // Get default repo + defaultRepo, err := rm.rService.GetDefaultRepo() + if err != nil || defaultRepo == "" { log.Warning("Failed to parse default_repo from backend") defaultRepo = viper.GetString("defaultRepo") } - // Add default repo - err = rm.AddRepo(defaultRepo) + _, _, err = rm.AddRepo(defaultRepo, rm.defaultRevisions[defaultRepo]) if err != nil { log.Fatal("Could not open default repo: ", err) } @@ -86,83 +120,208 @@ func initializeRepos(service *confsys.Service) *RepoManager { } for _, repo := range discoveredRepos { - err = rm.AddRepo(repo) + _, _, err = rm.AddRepo(repo, rm.defaultRevisions[repo]) if err != nil && err.Error() != "Repo already present" { //Skip error for default repo log.Warning("Failed to add persistent repo: ", repo, " | ", err) } } + // Update all repos + err = rm.RefreshRepos() + if err != nil { + log.Warning("Could not refresh repos: ", err) + } + return &rm } -func (manager *RepoManager) discoverRepos() (repos []string, err error){ +func (manager *RepoManager) discoverRepos() (repos []string, err error) { var hostingSites []string - var usernames []string - var someRepos []string - hostingSites, err = filepath.Glob(viper.GetString("RepositoriesPath") + "*") + hostingSites, err = filepath.Glob(filepath.Join(manager.rService.GetReposPath(), "*")) if err != nil { return } + // A path is valid in this context if it is a dir and isn't hidden (e.g. .git) + isValidPath := func(path string) bool { + fileInfo, err := os.Stat(path) + if err == nil && fileInfo.IsDir() && fileInfo.Name()[0:1] != "." { + return true + } + return false + } + for _, hostingSite := range hostingSites { - usernames, err = filepath.Glob(hostingSite + "/*") + // Get rid of invalid paths + if !isValidPath(hostingSite) { + continue + } + + // find .git dir + // everything above is "path", dir containing ".git" is repo + err = filepath.Walk(hostingSite, func(path string, info os.FileInfo, err error) error { + if info.IsDir() && info.Name() == ".git" { + // hack to "sanitize" the path + // i.e. don't include repos path + // don't include trailing "/.git" + repos = append(repos, strings.TrimPrefix(strings.TrimSuffix(path, "/.git"), + manager.rService.GetReposPath()+"/")) + } + + return nil + }) + if err != nil { return } - for _, username := range usernames { - someRepos, err = filepath.Glob(username + "/*") + } + + return +} + +func (manager *RepoManager) checkAndSetDefaultRevision(repo iRepo) error { + // Decide if requested revision = defaultRevision -> if yes lock them together + // We do this because, in the case where the revision was not specified, it would fall back to the default revision + // As a result, subsequent changes to the default revision, within the scope of this function, should be followed by revision + revIsDefault := false + if repo.GetDefaultRevision() == repo.getRevision() { + revIsDefault = true + } + + // Check that the defaultRevision is valid, i.e. can be checked out + var prefixes []string + prefixes = append(prefixes, refRemotePrefix, refTagPrefix) + matchedRevs, err := repo.getRevisions(repo.GetDefaultRevision(), prefixes) + if err != nil { + return err + } else if len(matchedRevs) == 0 { + log.Warning("Default revision " + repo.GetDefaultRevision() + " invalid for " + repo.GetIdentifier()) + if repo.GetDefaultRevision() != manager.defaultRevision { + log.Warning("Defaulting to global default revision: " + manager.defaultRevision) + repo.setDefaultRevision(manager.defaultRevision) + matchedRevs, err = repo.getRevisions(repo.GetDefaultRevision(), prefixes) if err != nil { - return - } + return err + } else if len(matchedRevs) == 0 { + log.Warning("Global default revision " + repo.GetDefaultRevision() + " invalid for " + repo.GetIdentifier()) - for _, repo := range someRepos { //sanitize path - repo = strings.TrimPrefix(repo, viper.GetString("RepositoriesPath")) - repos = append(repos, repo) + } else { + // if the revision was the default revision, make sure we now follow the updated default revision + if revIsDefault { + repo.setRevision(repo.GetDefaultRevision()) + } + return nil } } + + // No revisions matched, fall back to master or main + matchedRevs, err = repo.getRevisions("master", prefixes) + if err == nil && len(matchedRevs) != 0 { + log.Warning("Defaulting to master") + repo.setDefaultRevision("master") + return nil + } + + matchedRevs, err = repo.getRevisions("main", prefixes) + if err == nil && len(matchedRevs) != 0 { + log.Warning("Defaulting to main") + repo.setDefaultRevision("main") + return nil + } + + log.Warning("Cannot fall back to any default revision for ", repo.GetIdentifier()) + return fmt.Errorf("cannot fall back to any default revision for %s", repo.GetIdentifier()) } - return + return nil } -func (manager *RepoManager) AddRepo(repoPath string) error { +func (manager *RepoManager) AddRepo(repoPath string, defaultRevision string) (string, bool, error) { manager.mutex.Lock() defer manager.mutex.Unlock() - utils.EnsureTrailingSlash(&repoPath) + if defaultRevision == "" { + defaultRevision = manager.defaultRevision + } - repo, err := NewRepo(repoPath) + repo, err := newRepo(repoPath, defaultRevision, manager.rService.GetReposPath()) if err != nil { - return err + return "", false, err } _, exists := manager.repoList[repo.GetIdentifier()] if !exists { //Try to clone it + var gitRepo *git.Repository + if repo.GetProtocol() == "local" { // local repo needs only be opened + // We first need to make sure that the local repo is accessible, if not `git.PlainOpen` will crash us with a segfault + // It should also be writable for JIT + err := unix.Access(repo.GetCloneDir(), unix.W_OK) + if err != nil { + return "", false, err + } + + // TODO: persistent local repos? + gitRepo, err = git.PlainOpen(repo.GetCloneDir()) + } else { // https and ssh repos need to be cloned + var co git.CloneOptions - _, err = git.PlainClone(repo.getCloneDir(), false, &git.CloneOptions{ - URL: repo.getUrl(), - ReferenceName: plumbing.NewBranchReferenceName(repo.Revision), - }) + // prepare clone options for ssh authorization + if repo.GetProtocol() == "ssh" { + auth, err := ssh.NewPublicKeysFromFile("git", viper.GetString("reposSshKey"), "") + if err != nil { + return "", false, err + } + // Disable strict host checking which may block the clone op without manual intervention + auth.HostKeyCallback = ssh2.InsecureIgnoreHostKey() + co.Auth = auth + } + + co.URL = repo.getUri() + co.NoCheckout = true + + gitRepo, err = git.PlainClone(repo.GetCloneDir(), false, &co) + } + + if err != nil && err != git.ErrRepositoryAlreadyExists { + // Something went wrong, clean up + cleanErr := cleanCloneParentDirs(repo.getCloneParentDirs()) + if cleanErr != nil { + return "", false, errors.New(err.Error() + " Failed to clean directories: " + cleanErr.Error()) + } + return "", false, err + } + // Make sure the repo is up to date and its structs are populated + err = repo.refresh() if err != nil { - if err == git.ErrRepositoryAlreadyExists { //Make sure master is checked out - checkoutErr := repo.checkoutRevision(repo.Revision) - if checkoutErr != nil { - return errors.New(err.Error() + " " + checkoutErr.Error()) - } - } else { - cleanErr := cleanCloneParentDirs(repo.getCloneParentDirs()) - if cleanErr != nil { - return errors.New(err.Error() + " Failed to clean directories: " + cleanErr.Error()) - } - return err + return "", false, err + } + + if gitRepo != nil { + // Update git config to be case insensitive + var gitConfig *config.Config + gitConfig, err = gitRepo.Config() + if err != nil { + return "", false, errors.New(err.Error() + " Failed to get git config") + } + + gitConfig.Raw.AddOption("core", "", "ignorecase", "true") + err = gitRepo.SetConfig(gitConfig) + if err != nil { + return "", false, errors.New(err.Error() + " Failed to update git config") } } + // Check that the repo's default revision is valid, update if not + err = manager.checkAndSetDefaultRevision(repo) + if err != nil { + return "", false, err + } + manager.repoList[repo.GetIdentifier()] = repo // Set default repo @@ -170,10 +329,17 @@ func (manager *RepoManager) AddRepo(repoPath string) error { manager.setDefaultRepo(repo) } } else { - return errors.New("Repo already present") + return "", false, errors.New("Repo already present") } - return nil + // Update default revisions + manager.defaultRevisions[repo.GetIdentifier()] = repo.GetDefaultRevision() + err = manager.rService.SetRepoDefaultRevisions(manager.defaultRevisions) + if err != nil { + return "", false, err + } + + return repo.GetDefaultRevision(), repo.GetDefaultRevision() == manager.defaultRevision, nil } func cleanCloneParentDirs(parentDirs []string) error { @@ -192,43 +358,82 @@ func cleanCloneParentDirs(parentDirs []string) error { return nil } -func (manager *RepoManager) GetRepos() (repoList map[string]*Repo) { +func (manager *RepoManager) GetAllRepos() (repoList map[string]iRepo) { manager.mutex.Lock() defer manager.mutex.Unlock() return manager.repoList } -func (manager *RepoManager) RemoveRepoByIndex(index int) (ok bool, newDefaultRepo string) { +func (manager *RepoManager) getRepos(repoPattern string) (repoList map[string]iRepo) { manager.mutex.Lock() defer manager.mutex.Unlock() + if repoPattern == "*" { // Skip unnecessary pattern matching + return manager.repoList + } + + matchedRepoList := make(map[string]iRepo) + g := glob.MustCompile(repoPattern) + + for _, repo := range manager.repoList { + if g.Match(repo.GetIdentifier()) { + matchedRepoList[repo.GetIdentifier()] = repo + } + } + + return matchedRepoList +} + +func (manager *RepoManager) getRepoByIndex(index int) (iRepo, error) { keys := manager.GetOrderedRepolistKeys() + if len(keys)-1 >= index { // Verify that index is not out of bounds + return manager.repoList[keys[index]], nil + } else { + return nil, errors.New("getRepoByIndex: repo not found for index :" + strconv.Itoa(index)) + } +} - for i, repoName := range keys { - if i != index { - continue +func (manager *RepoManager) RemoveRepoByIndex(index int) (string, error) { + manager.mutex.Lock() + defer manager.mutex.Unlock() + + newDefaultRepoString := "" + + repo, err := manager.getRepoByIndex(index) + if err != nil { + return "", err + } + + wasDefault := repo.IsDefault() + + _ = os.RemoveAll(repo.GetCloneDir()) // Try, but don't crash if we fail + _ = cleanCloneParentDirs(repo.getCloneParentDirs()) // Try, but don't crash if we fail + + delete(manager.repoList, repo.GetIdentifier()) + // Set as default the repo sitting on top of the list + if wasDefault && len(manager.repoList) > 0 { + newDefaultRepo, err := manager.getRepoByIndex(0) + if err != nil { + return newDefaultRepoString, err } - wasDefault := manager.repoList[repoName].Default - - _ = os.RemoveAll(manager.repoList[repoName].getCloneDir()) // Try, but don't crash if we fail - - delete(manager.repoList, repoName) - // Set as default the repo sitting on top of the list - if wasDefault && len(manager.repoList) > 0 { - manager.setDefaultRepo(manager.repoList[manager.GetOrderedRepolistKeys()[0]]) //Keys have to be reparsed since there was a removal - keys = manager.GetOrderedRepolistKeys() //Update keys after deletion - newDefaultRepo = keys[0] - } else if len(manager.repoList) == 0 { - err := manager.cService.NewDefaultRepo(viper.GetString("defaultRepo")) - if err != nil { - log.Warning("Failed to update default_repo backend") - } + manager.setDefaultRepo(newDefaultRepo) + newDefaultRepoString = newDefaultRepo.GetIdentifier() + } else if len(manager.repoList) == 0 { + err := manager.rService.NewDefaultRepo(viper.GetString("defaultRepo")) + if err != nil { + log.Warning("Failed to update default_repo backend") } - return true, newDefaultRepo } - return false, newDefaultRepo + // Update default revisions + delete(manager.defaultRevisions, repo.GetIdentifier()) + err = manager.rService.SetRepoDefaultRevisions(manager.defaultRevisions) + if err != nil { + return "", err + } + + return newDefaultRepoString, nil } func (manager *RepoManager) RefreshRepos() error { @@ -237,6 +442,7 @@ func (manager *RepoManager) RefreshRepos() error { for _, repo := range manager.repoList { + manager.defaultRevisions[repo.GetIdentifier()] = repo.GetDefaultRevision() err := repo.refresh() if err != nil { return errors.New("refresh repo for " + repo.GetIdentifier() + ":" + err.Error()) @@ -250,8 +456,6 @@ func (manager *RepoManager) RefreshRepo(repoPath string) error { manager.mutex.Lock() defer manager.mutex.Unlock() - utils.EnsureTrailingSlash(&repoPath) - repo := manager.repoList[repoPath] return repo.refresh() @@ -261,20 +465,15 @@ func (manager *RepoManager) RefreshRepoByIndex(index int) error { manager.mutex.Lock() defer manager.mutex.Unlock() - keys := manager.GetOrderedRepolistKeys() - - for i, repoName := range keys { - if i != index { - continue - } - repo := manager.repoList[repoName] - return repo.refresh() + repo, err := manager.getRepoByIndex(index) + if err != nil { + return err } - return errors.New("RefreshRepoByIndex: repo not found for index: " + string(index)) + return repo.refresh() } -func (manager *RepoManager) GetWorkflow(workflowPath string) (resolvedWorkflowPath string, workflowRepo *Repo, err error) { +func (manager *RepoManager) GetWorkflow(workflowPath string) (resolvedWorkflowPath string, workflowRepo IRepo, err error) { manager.mutex.Lock() defer manager.mutex.Unlock() @@ -288,13 +487,14 @@ func (manager *RepoManager) GetWorkflow(workflowPath string) (resolvedWorkflowP // Resolve repo var workflowFile string - workflowInfo := strings.Split(workflowPath, "workflows/") + workflowInfo := strings.Split(workflowPath, "/workflows/") + var wfRepo iRepo if len(workflowInfo) == 1 { // Repo not specified - workflowRepo = manager.defaultRepo + wfRepo = manager.defaultRepo workflowFile = workflowInfo[0] } else if len(workflowInfo) == 2 { // Repo specified - try to find it - workflowRepo= manager.repoList[workflowInfo[0]] - if workflowRepo == nil { + wfRepo = manager.repoList[workflowInfo[0]] + if wfRepo == nil { err = errors.New("Workflow comes from an unknown repo") return } @@ -305,12 +505,14 @@ func (manager *RepoManager) GetWorkflow(workflowPath string) (resolvedWorkflowP return } - if revision != "" { //If a revision has been specified, update the Repo - workflowRepo.Revision = revision + if revision != "" { // If a revision has been specified, update the Repo + wfRepo.setRevision(revision) + } else { // Otherwise use the default revision of the repo + wfRepo.setDefaultRevision(wfRepo.GetDefaultRevision()) } // Make sure that HEAD is on the expected revision - err = workflowRepo.checkoutRevision(workflowRepo.Revision) + err = wfRepo.checkoutRevision(wfRepo.getRevision()) if err != nil { return } @@ -318,28 +520,34 @@ func (manager *RepoManager) GetWorkflow(workflowPath string) (resolvedWorkflowP if !strings.HasSuffix(workflowFile, ".yaml") { //Add trailing ".yaml" workflowFile += ".yaml" } - resolvedWorkflowPath = workflowRepo.getWorkflowDir() + workflowFile + resolvedWorkflowPath = filepath.Join(wfRepo.getWorkflowDir(), workflowFile) + + workflowRepo = IRepo(wfRepo) return } -func (manager *RepoManager) setDefaultRepo(repo *Repo) { +func (manager *RepoManager) setDefaultRepo(repo iRepo) { if manager.defaultRepo != nil { - manager.defaultRepo.Default = false // Update old default repo + manager.defaultRepo.setDefault(false) // Update old default repo } // Update default_repo backend - err := manager.cService.NewDefaultRepo(repo.GetIdentifier()) + err := manager.rService.NewDefaultRepo(repo.GetIdentifier()) if err != nil { log.Warning("Failed to update default_repo backend: ", err) } manager.defaultRepo = repo - repo.Default = true + repo.setDefault(true) } func (manager *RepoManager) UpdateDefaultRepoByIndex(index int) error { - newDefaultRepo := manager.repoList[manager.GetOrderedRepolistKeys()[index]] + orderedRepoList := manager.GetOrderedRepolistKeys() + if index >= len(orderedRepoList) { + return errors.New("Default repo index out of range") + } + newDefaultRepo := manager.repoList[orderedRepoList[index]] if newDefaultRepo == nil { return errors.New("Repo not found") } else if newDefaultRepo == manager.defaultRepo { @@ -352,8 +560,6 @@ func (manager *RepoManager) UpdateDefaultRepoByIndex(index int) error { } func (manager *RepoManager) UpdateDefaultRepo(repoPath string) error { //unused - utils.EnsureTrailingSlash(&repoPath) - newDefaultRepo := manager.repoList[repoPath] if newDefaultRepo == nil { return errors.New("Repo not found") @@ -366,28 +572,63 @@ func (manager *RepoManager) UpdateDefaultRepo(repoPath string) error { //unused return nil } +func (manager *RepoManager) SetGlobalDefaultRevision(revision string) error { + // Update default_revision backend + err := manager.rService.NewDefaultRevision(revision) + if err != nil { + log.Warning("Failed to update default_revision backend: ", err) + return err + } + + viper.Set("globalDefaultRevision", revision) + manager.defaultRevision = revision + return nil +} + +func (manager *RepoManager) UpdateDefaultRevisionByIndex(index int, revision string) (string, error) { + orderedRepoList := manager.GetOrderedRepolistKeys() + if index >= len(orderedRepoList) { + return "", errors.New("repository index out of range") + } + repo := manager.repoList[orderedRepoList[index]] + + info, err := repo.updateDefaultRevision(revision) + if err != nil { + return info, errors.New("Could not update default revision for " + repo.GetIdentifier() + " : " + err.Error()) + } + + // Update default revisions + manager.defaultRevisions[repo.GetIdentifier()] = repo.GetDefaultRevision() + err = manager.rService.SetRepoDefaultRevisions(manager.defaultRevisions) + if err != nil { + return "", err + } + + return "", nil +} + func (manager *RepoManager) EnsureReposPresent(taskClassesRequired []string) (err error) { - reposRequired := make(map[Repo]bool) + reposRequired := make(map[string]iRepo) for _, taskClass := range taskClassesRequired { - var newRepo *Repo - newRepo, err = NewRepo(taskClass) + var taskRepo iRepo + taskRepo, err = newRepo(taskClass, manager.defaultRevision, manager.rService.GetReposPath()) if err != nil { return } - reposRequired[*newRepo] = true + reposRequired[taskRepo.GetIdentifier()] = taskRepo } // Make sure that the relevant repos are present and checked out on the expected revision - for repo := range reposRequired { + for _, repo := range reposRequired { existingRepo, ok := manager.repoList[repo.GetIdentifier()] if !ok { - err = manager.AddRepo(repo.GetIdentifier()) + _, _, err = manager.AddRepo(repo.GetIdentifier(), repo.GetDefaultRevision()) if err != nil { return } } else { - if existingRepo.Revision != repo.Revision { - err = existingRepo.checkoutRevision(repo.Revision) + if existingRepo.getRevision() != repo.getRevision() { + err = existingRepo.checkoutRevision(repo.getRevision()) if err != nil { return } @@ -408,18 +649,88 @@ func (manager *RepoManager) GetOrderedRepolistKeys() []string { return keys } -func (manager *RepoManager) GetWorkflowTemplates() (map[string][]string, int, error) { - templateList := make(map[string][]string) +type RepoKey string +type RevisionKey string +type Template struct { + Name string + Description string + Public bool + VarInfo VarSpecMap +} +type Templates []Template +type TemplatesByRevision map[RevisionKey]Templates +type TemplatesByRepo map[RepoKey]TemplatesByRevision + +// GetWorkflowTemplates Returns a map of templates: repo -> revision -> []templates +func (manager *RepoManager) GetWorkflowTemplates(repoPattern string, revisionPattern string, allBranches bool, allTags bool, allWorkflows bool) (TemplatesByRepo, int, error) { + templateList := make(TemplatesByRepo) numTemplates := 0 - for _, repo := range manager.GetRepos() { - templates, err := repo.getWorkflows() + + if repoPattern == "" { // all repos if unspecified + repoPattern = "*" + } + + if allBranches || allTags { + if revisionPattern != "" { // If the revision pattern is specified and an all{Branches,Tags} flag is used return error + return nil, 0, errors.New("cannot use all{Branches,Tags} with a revision specified") + } + } + + // Prepare the gitRefs slice which will filter the git references + var gitRefs []string + if !allBranches && !allTags { + gitRefs = append(gitRefs, refRemotePrefix, refTagPrefix) + } else { + revisionPattern = "*" + if allBranches { + gitRefs = append(gitRefs, refRemotePrefix) + } + + if allTags { + gitRefs = append(gitRefs, refTagPrefix) + } + } + + // Build list of repos to iterate through by pattern or by index(single repo, if pattern is an int) + repos := make(map[string]iRepo) + if repoIndex, err := strconv.Atoi(repoPattern); err == nil { + repo, err := manager.getRepoByIndex(repoIndex) + if err != nil { + return nil, 0, err + } + repos[repo.GetIdentifier()] = repo + } else { + repos = manager.getRepos(repoPattern) + } + + for _, repo := range repos { + // For every repo get the templates for the revisions matching the revisionPattern; gitRefs to filter tags and/or branches + var templates TemplatesByRevision + var err error + if revisionPattern == "" { // If the revision pattern is empty, use the default revision + templates, err = repo.getWorkflows(repo.GetDefaultRevision(), gitRefs, allWorkflows) + } else { + templates, err = repo.getWorkflows(revisionPattern, gitRefs, allWorkflows) + } + if err != nil { return nil, 0, err } - templateList[repo.GetIdentifier()] = templates - numTemplates += len(templates) + templateList[RepoKey(repo.GetIdentifier())] = templates + + for _, revTemplate := range templates { + numTemplates += len(revTemplate) // numTemplates is needed for protobuf to know the number of messages to go through + } } return templateList, numTemplates, nil -} \ No newline at end of file +} + +func (manager *RepoManager) GetDefaultRevision() string { + return manager.defaultRevision +} + +func (manager *RepoManager) GetReposPath() string { + return manager.rService.GetReposPath() +} diff --git a/core/repos/reposervice.go b/core/repos/reposervice.go new file mode 100644 index 000000000..91e3f7d2c --- /dev/null +++ b/core/repos/reposervice.go @@ -0,0 +1,101 @@ +package repos + +import ( + "encoding/json" + "io/ioutil" + "path/filepath" + "strings" + + "github.com/AliceO2Group/Control/configuration" + "github.com/spf13/viper" +) + +// TODO: remove support for FILE backend in this one +type RepoService struct { + Svc configuration.RuntimeService +} + +func (s *RepoService) GetReposPath() string { + return filepath.Join(viper.GetString("coreWorkingDir"), "repos") +} + +func (s *RepoService) NewDefaultRepo(defaultRepo string) error { + if s.Svc != nil { + return s.Svc.SetRuntimeEntry("aliecs", "default_repo", defaultRepo) + } else { + data := []byte(defaultRepo) + return ioutil.WriteFile(filepath.Join(s.GetReposPath(), "default_repo"), data, 0644) + } +} + +func (s *RepoService) GetDefaultRepo() (defaultRepo string, err error) { + if s.Svc != nil { + return s.Svc.GetRuntimeEntry("aliecs", "default_repo") + } else { + var defaultRepoData []byte + defaultRepoData, err = ioutil.ReadFile(filepath.Join(s.GetReposPath(), "default_repo")) + if err != nil { + return + } + defaultRepo = strings.TrimSuffix(string(defaultRepoData), "\n") + return + } +} + +func (s *RepoService) NewDefaultRevision(defaultRevision string) error { + if s.Svc != nil { + return s.Svc.SetRuntimeEntry("aliecs", "default_revision", defaultRevision) + } else { + data := []byte(defaultRevision) + return ioutil.WriteFile(filepath.Join(s.GetReposPath(), "default_revision"), data, 0644) + } +} + +func (s *RepoService) GetDefaultRevision() (defaultRevision string, err error) { + if s.Svc != nil { + return s.Svc.GetRuntimeEntry("aliecs", "default_revision") + } else { + var defaultRevisionData []byte + defaultRevisionData, err = ioutil.ReadFile(filepath.Join(s.GetReposPath(), "default_revision")) + if err != nil { + return + } + defaultRevision = strings.TrimSuffix(string(defaultRevisionData), "\n") + return + } +} + +func (s *RepoService) GetRepoDefaultRevisions() (map[string]string, error) { + var defaultRevisions map[string]string + if s.Svc != nil { + data, err := s.Svc.GetRuntimeEntry("aliecs", "default_revisions") + if err != nil { + return nil, err + } + err = json.Unmarshal([]byte(data), &defaultRevisions) + if err != nil { + return nil, err + } + } else { + defaultRevisionData, err := ioutil.ReadFile(filepath.Join(s.GetReposPath(), "default_revisions.json")) + if err != nil { + return nil, err + } + err = json.Unmarshal(defaultRevisionData, &defaultRevisions) + } + return defaultRevisions, nil +} + +func (s *RepoService) SetRepoDefaultRevisions(defaultRevisions map[string]string) error { + data, err := json.MarshalIndent(defaultRevisions, "", " ") + if err != nil { + return err + } + + if s.Svc != nil { + err = s.Svc.SetRuntimeEntry("aliecs", "default_revisions", string(data)) + } else { + err = ioutil.WriteFile(filepath.Join(s.GetReposPath(), "default_revisions.json"), data, 0644) + } + return err +} diff --git a/core/repos/repoutils.go b/core/repos/repoutils.go new file mode 100644 index 000000000..71039c72e --- /dev/null +++ b/core/repos/repoutils.go @@ -0,0 +1,151 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2021 CERN and copyright holders of ALICE O². + * Author: Kostas Alexopoulos + * Teo Mrnjavac + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package repos + +import ( + "fmt" + "io/ioutil" + + "github.com/AliceO2Group/Control/core/repos/varsource" + "gopkg.in/yaml.v3" +) + +func ParseWorkflowPublicVariableInfo(fileName string) (bool, string, VarSpecMap, error) { + yamlFile, err := ioutil.ReadFile(fileName) + if err != nil { + return false, "", nil, err + } + + nodes := make(map[string]yaml.Node) + err = yaml.Unmarshal(yamlFile, &nodes) + if err != nil { + return false, "", nil, err + } + + isPublic := nodes["name"].Tag == "!public" + + description := "" + if nodes["description"].Tag == "!public" { + description = nodes["description"].Value + } + + workflowVarInfo := make(VarSpecMap) + for k, v := range nodes { + if err = parseYamlPublicVars(&AuxNode{k, &v}, &workflowVarInfo); err != nil { + return false, "", nil, err + } + } + + return isPublic, description, workflowVarInfo, nil +} + +// VarSpecMap holds a map of variable names to their variable information struct +type VarSpecMap map[string]VarSpec + +// VarSpec is the type of struct into which public variable information from workflows may be parsed +type VarSpec struct { + Source varsource.Source `yaml:"-"` + DefaultValue string `yaml:"value"` + VarType string `yaml:"type"` + Label string `yaml:"label"` + Description string `yaml:"description"` + Widget string `yaml:"widget"` + Panel string `yaml:"panel" ` + AllowedValues []string `yaml:"values"` + Index int32 `yaml:"index"` + Rows uint32 `yaml:"rows"` + VisibleIf string `yaml:"visibleif"` + EnabledIf string `yaml:"enabledif"` +} + +// AuxNode Use an auxiliary node struct that also carries its parent Name +type AuxNode struct { + parentName string + node *yaml.Node +} + +func parseYamlPublicVars(auxNode *AuxNode, workflowVarInfo *VarSpecMap) error { + node := auxNode.node + + // Recursion stops if node is nil, or isn't a mapping or a sequence node + if node == nil || + node.Kind != yaml.MappingNode && node.Kind != yaml.SequenceNode { + return nil + } + + if node.Kind == yaml.SequenceNode { // If it's a sequence node, continue searching for a map within it + for _, v := range node.Content { + err := parseYamlPublicVars(&AuxNode{"", v}, workflowVarInfo) + if err != nil { + return err + } + } + } else if node.Kind == yaml.MappingNode { // If it's a mapping node, iterate through it + // We do this decoding to have a sane key -> node map + // otherwise, we get two yaml nodes for a single element + // with the first one holding the Name and the second one holding the tag + m := make(map[string]yaml.Node) + err := node.Decode(&m) + if err != nil { + return err + } + + parentName := auxNode.parentName + // Search within the node contents for a "!public" mapping node, + // which is also the ancestor of a "defaults" or "vars" parent + for k, v := range m { + var varSpec VarSpec + + if (parentName == "defaults" || parentName == "vars") && v.Kind == yaml.MappingNode && v.Tag == "!public" { + err = v.Decode(&varSpec) + if err != nil { + fmt.Println(err) + continue + } + + // If the key already exists we have come upon a duplicate! + if _, exists := (*workflowVarInfo)[k]; exists { + duplicateError := fmt.Errorf("duplicate public variable \"%s\" parsed, input workflow file invalid", k) + return duplicateError + } + if parentName == "vars" { + varSpec.Source = varsource.WorkflowVars + } else { + varSpec.Source = varsource.WorkflowDefaults + } + + // Update the map + (*workflowVarInfo)[k] = varSpec + } else { + err = parseYamlPublicVars(&AuxNode{k, &v}, workflowVarInfo) + if err != nil { + return err + } + } + } + } + return nil +} diff --git a/core/repos/sshrepo.go b/core/repos/sshrepo.go new file mode 100644 index 000000000..a47f3aaa5 --- /dev/null +++ b/core/repos/sshrepo.go @@ -0,0 +1,119 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2021 CERN and copyright holders of ALICE O². + * Author: Kostas Alexopoulos + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +// Package repos provides repository management functionality for accessing +// and synchronizing Git repositories containing workflow templates and configurations. +package repos + +import ( + "errors" + "github.com/go-git/go-git/v5" + "github.com/go-git/go-git/v5/plumbing/transport/ssh" + "github.com/spf13/viper" + ssh2 "golang.org/x/crypto/ssh" + "net/url" + "os/exec" + "path" +) + +type sshRepo struct { + Repo +} + +func (r *sshRepo) getUri() string { + u, _ := url.Parse(r.GetProtocol() + "://") + u.Path = path.Join(u.Path, "git@"+r.HostingSite) + + u.Path = path.Join(u.Path, + r.Path, + r.RepoName) + + uri := u.String() + + return uri + ".git" +} + +func (r *sshRepo) GetProtocol() string { + return "ssh" +} + +func (r *sshRepo) refresh() error { + ref, err := git.PlainOpen(r.GetCloneDir()) + if err != nil { + return errors.New(err.Error() + ": " + r.GetIdentifier()) + } + + auth, err := ssh.NewPublicKeysFromFile("git", viper.GetString("reposSshKey"), "") + if err != nil { + return errors.New(err.Error() + ": " + r.GetIdentifier()) + } + + // Disable strict host checking without which may block the fetch op without manual intervention + auth.HostKeyCallback = ssh2.InsecureIgnoreHostKey() + + // clean the repo before doing anything + // this removes the untracked JIT-produced tasks and workflows + clnCmd := exec.Command("git", "-C", r.GetCloneDir(), "clean", "-f") + err = clnCmd.Run() + if err != nil { + return errors.New(err.Error() + ": " + r.GetIdentifier()) + } + + err = ref.Fetch(&git.FetchOptions{ + RemoteName: "origin", + Force: true, + Auth: auth, + }) + + if err != nil && err != git.NoErrAlreadyUpToDate { + return errors.New(err.Error() + ": " + r.GetIdentifier() + " | revision: " + r.Revision) + } + + // gather revisions on update or if empty + if err != git.NoErrAlreadyUpToDate || r.Revisions == nil { + err = r.gatherRevisions(ref) + if err != nil { + return err + } + } + + // populate workflows on update or if empty + if err != git.NoErrAlreadyUpToDate || len(templatesCache) == 0 { + err = r.populateWorkflows(r.GetDefaultRevision(), true) + if err != nil { + return err + } + } + + return nil +} + +/*func (r *sshRepo) GetDplCommand(dplCommandUri string) (string, error) { + dplCommandPath := filepath.Join(r.GetCloneDir(), jitScriptsDir, dplCommandUri) + dplCommandPayload, err := os.ReadFile(dplCommandPath) + if err != nil { + return "", err + } + return string(dplCommandPayload), nil +}*/ diff --git a/core/repos/sshrepo_test.go b/core/repos/sshrepo_test.go new file mode 100644 index 000000000..0213da3da --- /dev/null +++ b/core/repos/sshrepo_test.go @@ -0,0 +1,101 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2021 CERN and copyright holders of ALICE O². + * Author: Kostas Alexopoulos + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package repos + +import ( + "net/url" + "path" + "testing" +) + +var sshTs = ts{ + Protocol: "ssh", + HostingSite: "git-server:", + Path: "/opt/git", + RepoName: "ControlWorkflows", + DefaultRevision: "main", + RepoPath: "git-server:/opt/git/ControlWorkflows", + RepoPathDotGit: "git-server:/opt/git/ControlWorkflows.git", + ReposPath: "/var/lib/o2/aliecs/repos", +} + +func TestGetUriSsh(t *testing.T) { + _, repo, _ := NewRepo(sshTs.RepoPath, "", httpTs.ReposPath) + u, _ := url.Parse(sshTs.Protocol + "://" + sshTs.HostingSite) + u.Path = path.Join(u.Path, + sshTs.Path, + sshTs.RepoName) + expectedUri := u.String() + ".git" + + if repo.getUri() != expectedUri { + t.Errorf("Got %s URI instead of %s", repo.getUri(), expectedUri) + } +} + +func TestGetIdentifierSsh(t *testing.T) { + _, repo, _ := NewRepo(sshTs.RepoPath, "", httpTs.ReposPath) + expectedIdentifier := path.Join(sshTs.HostingSite, sshTs.Path, sshTs.RepoName) + + if repo.GetIdentifier() != expectedIdentifier { + t.Errorf("Got identifier %s instead of %s", repo.GetIdentifier(), expectedIdentifier) + } +} + +func TestNewRepoSsh(t *testing.T) { + _, repo, err := NewRepo(sshTs.RepoPath, sshTs.DefaultRevision, httpTs.ReposPath) + if err != nil { + t.Errorf("Creating a new valid repo shouldn't error out") + return + } + + if repo.GetProtocol() != sshTs.Protocol { + t.Errorf("Repo's default protocol was %s instead of %s", repo.GetProtocol(), sshTs.Protocol) + return + } + + if repo.getHostingSite() != sshTs.HostingSite { + t.Errorf("Repo's hosting site was %s instead of %s", repo.getHostingSite(), sshTs.HostingSite) + return + } + + if repo.getPath() != sshTs.Path { + t.Errorf("Repo's path was %s instead of %s", repo.getPath(), sshTs.Path) + return + } + + if repo.getRepoName() != sshTs.RepoName { + t.Errorf("Repo name was %s instead of %s", repo.getRepoName(), sshTs.RepoName) + return + } + + if repo.GetDefaultRevision() != sshTs.DefaultRevision { + t.Errorf("Repo's default revision was %s instead of %s", repo.getRevision(), sshTs.DefaultRevision) + return + } + + if repo.getRevision() != repo.GetDefaultRevision() { + t.Errorf("Repo's revision should fall back to default revision, when latter specified. Got %s instead of %s", repo.getRevision(), repo.GetDefaultRevision()) + } +} diff --git a/core/repos/varsource/varsource.go b/core/repos/varsource/varsource.go new file mode 100644 index 000000000..8551ed739 --- /dev/null +++ b/core/repos/varsource/varsource.go @@ -0,0 +1,34 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2021 CERN and copyright holders of ALICE O². + * Author: Teo Mrnjavac + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +// Package varsource provides variable source management functionality for +// tracking the origin and precedence of configuration variables in workflows. +package varsource + +type Source int + +const ( + WorkflowDefaults Source = iota + WorkflowVars +) diff --git a/core/scheduler.go b/core/scheduler.go deleted file mode 100644 index f9b2f3255..000000000 --- a/core/scheduler.go +++ /dev/null @@ -1,878 +0,0 @@ -/* - * === This file is part of ALICE O² === - * - * Copyright 2017-2018 CERN and copyright holders of ALICE O². - * Author: Teo Mrnjavac - * - * Portions from examples in : - * Copyright 2013-2015, Mesosphere, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * In applying this license CERN does not waive the privileges and - * immunities granted to it by virtue of its status as an - * Intergovernmental Organization or submit itself to any jurisdiction. - */ - -package core - -import ( - "context" - "encoding/json" - "errors" - "fmt" - "github.com/spf13/viper" - "io" - "strconv" - "strings" - "time" - - "github.com/AliceO2Group/Control/common/event" - "github.com/AliceO2Group/Control/core/controlcommands" - "github.com/AliceO2Group/Control/core/environment" - "github.com/AliceO2Group/Control/core/task" - "github.com/AliceO2Group/Control/core/task/constraint" - "github.com/AliceO2Group/Control/executor/protos" - "github.com/gogo/protobuf/proto" - "github.com/mesos/mesos-go/api/v1/lib" - "github.com/mesos/mesos-go/api/v1/lib/backoff" - xmetrics "github.com/mesos/mesos-go/api/v1/lib/extras/metrics" - "github.com/mesos/mesos-go/api/v1/lib/extras/scheduler/callrules" - "github.com/mesos/mesos-go/api/v1/lib/extras/scheduler/controller" - "github.com/mesos/mesos-go/api/v1/lib/extras/scheduler/eventrules" - "github.com/mesos/mesos-go/api/v1/lib/extras/store" - "github.com/mesos/mesos-go/api/v1/lib/resources" - "github.com/mesos/mesos-go/api/v1/lib/scheduler" - "github.com/mesos/mesos-go/api/v1/lib/scheduler/calls" - "github.com/mesos/mesos-go/api/v1/lib/scheduler/events" - "github.com/pborman/uuid" - "github.com/sirupsen/logrus" -) - -var ( - RegistrationMinBackoff = 1 * time.Second - RegistrationMaxBackoff = 15 * time.Second -) - -// StateError is returned when the system encounters an unresolvable state transition error and -// should likely exit. -type StateError string - -func (err StateError) Error() string { return string(err) } - -var schedEventsCh = make(chan scheduler.Event_Type) - -func runSchedulerController(ctx context.Context, - state *internalState, - fidStore store.Singleton) error { - // Set up communication from controller to state machine. - go func() { - for { - receivedEvent := <-schedEventsCh - switch { - case receivedEvent == scheduler.Event_SUBSCRIBED: - if state.sm.Is("INITIAL") { - state.sm.Event("CONNECT") - } - } - } - }() - - // Set up communication from state machine to controller - go func() { - for { - <- state.reviveOffersTrg - doReviveOffers(ctx, state) - state.reviveOffersTrg <- struct{}{} - } - }() - - // The controller starts here, it takes care of connecting to Mesos and subscribing - // as well as resubscribing if the connection is dropped. - // It also handles incoming events on the subscription connection. - // - // buildFrameworkInfo returns a *mesos.FrameworkInfo which includes the framework - // ID, as well as additional information such as Roles, WebUI URL, etc. - return controller.Run( - ctx, - buildFrameworkInfo(), - state.cli, /* controller.Option...: */ - controller.WithEventHandler(buildEventHandler(state, fidStore)), - controller.WithFrameworkID(store.GetIgnoreErrors(fidStore)), - controller.WithRegistrationTokens( - // Limit the rate of reregistration. - // When the Done chan closes, the Run controller loop terminates. The - // Done chan is closed by the context when its cancel func is called. - backoff.Notifier(RegistrationMinBackoff, RegistrationMaxBackoff, ctx.Done()), - ), - controller.WithSubscriptionTerminated(func(err error) { - // Sets a handler that runs at the end of every subscription cycle. - if err != nil { - if err != io.EOF { - log.WithPrefix("scheduler").WithField("error", err.Error()). - Error("subscription terminated") - } - if _, ok := err.(StateError); ok { - state.shutdown() - } - return - } - log.WithPrefix("scheduler").Info("disconnected") - }), - ) -} - - -// buildEventHandler generates and returns a handler to process events received -// from the subscription. The handler is then passed as controller.Option to -// controller.Run. -func buildEventHandler(state *internalState, fidStore store.Singleton) events.Handler { - // disable brief logs when verbose logs are enabled (there's no sense logging twice!) - logger := controller.LogEvents(nil).Unless(viper.GetBool("verbose")) - - return eventrules.New( /* eventrules.Rule... */ - logAllEvents().If(viper.GetBool("verbose")), - eventMetrics(state.metricsAPI, time.Now, viper.GetBool("summaryMetrics")), - controller.LiftErrors().DropOnError(), - eventrules.HandleF(notifyStateMachine(state)), - ).Handle(events.Handlers{ - // scheduler.Event_Type: events.Handler - scheduler.Event_FAILURE: logger.HandleF(failure), // wrapper + print error - scheduler.Event_OFFERS: trackOffersReceived(state).HandleF(resourceOffers(state, fidStore)), - scheduler.Event_UPDATE: controller.AckStatusUpdates(state.cli).AndThen().HandleF(statusUpdate(state)), - scheduler.Event_SUBSCRIBED: eventrules.New( - logger, - controller.TrackSubscription(fidStore, viper.GetDuration("mesosFailoverTimeout")), - ), - scheduler.Event_MESSAGE: eventrules.HandleF(incomingMessageHandler(state, fidStore)), - }.Otherwise(logger.HandleEvent)) -} - - -// Channel the event type of the newly received event to an asynchronous dispatcher -// in runSchedulerController -func notifyStateMachine(state *internalState) events.HandlerFunc { - return func(ctx context.Context, e *scheduler.Event) error { - schedEventsCh <- e.GetType() - return nil - } -} - -// Update metrics when we receive an offer -func trackOffersReceived(state *internalState) eventrules.Rule { - return func(ctx context.Context, e *scheduler.Event, err error, chain eventrules.Chain) (context.Context, *scheduler.Event, error) { - if err == nil { - state.metricsAPI.offersReceived.Int(len(e.GetOffers().GetOffers())) - } - return chain(ctx, e, err) - } -} - -// Handle an incoming Event_FAILURE, which may be a failure in the executor or -// in the Mesos agent. -func failure(_ context.Context, e *scheduler.Event) error { - var ( - f = e.GetFailure() - eid, aid, stat = f.ExecutorID, f.AgentID, f.Status - ) - if eid != nil { - // executor failed.. - fields := logrus.Fields{ - "executor": eid.Value, - } - if aid != nil { - fields["agent"] = aid.Value - } - if stat != nil { - fields["error"] = strconv.Itoa(int(*stat)) - } - log.WithPrefix("scheduler").WithFields(fields).Error("executor failed") - } else if aid != nil { - // agent failed.. - log.WithPrefix("scheduler").WithField("agent", aid.Value).Error("agent failed") - } - return nil -} - -// Handler for Event_MESSAGE -func incomingMessageHandler(state *internalState, fidStore store.Singleton) events.HandlerFunc { - // instantiate map of MCtargets, command IDs and timeouts here - // what should happen - // SendCommand sends a command, pushes the targets list, command id and timeout (maybe - // through a channel) to a structure accessible here. - // then when we receive a response, if its id, target and timeout is satisfied by one and - // only one entry in the list, we signal back to commandqueue - // otherwise, we log and ignore. - return func(ctx context.Context, e *scheduler.Event) (err error) { - log.Debug("scheduler.incomingMessageHandler BEGIN") - defer log.Debug("scheduler.incomingMessageHandler END") - - mesosMessage := e.GetMessage() - if mesosMessage == nil { - err = errors.New("message handler got bad MESSAGE") - log.WithPrefix("scheduler"). - WithError(err). - Warning("message handler cannot continue") - return - } - agentId := mesosMessage.GetAgentID() - executorId := mesosMessage.GetExecutorID() - if len(agentId.GetValue()) == 0 || len(executorId.GetValue()) == 0 { - err = errors.New("message handler got MESSAGE with no valid sender") - log.WithPrefix("scheduler"). - WithFields(logrus.Fields{ - "agentId": agentId.GetValue(), - "executorId": executorId.GetValue(), - "error": err.Error(), - }). - Warning("message handler cannot continue") - return - } - - data := mesosMessage.GetData() - - var incomingType struct { - MessageType string `json:"_messageType"` - } - err = json.Unmarshal(data, &incomingType) - if err != nil { - return - } - - switch incomingType.MessageType { - case "DeviceEvent": - var incomingEvent struct { - Type pb.DeviceEventType `json:"type"` - Origin event.DeviceEventOrigin `json:"origin"` - } - err = json.Unmarshal(data, &incomingEvent) - if err != nil { - return - } - ev := event.NewDeviceEvent(incomingEvent.Origin, incomingEvent.Type) - if ev != nil { - handleDeviceEvent(state, ev) - } else { - log.WithFields(logrus.Fields{ - "type": incomingEvent.Type.String(), - "originTask": incomingEvent.Origin.TaskId.Value, - }). - Error("cannot handle incoming device event") - } - - case "MesosCommandResponse": - var incomingCommand struct { - CommandName string `json:"name"` - } - err = json.Unmarshal(data, &incomingCommand) - if err != nil { - return - } - - log.WithPrefix("scheduler").WithField("commandName", incomingCommand.CommandName).Debug("processing incoming MESSAGE") - switch incomingCommand.CommandName { - case "MesosCommand_Transition": - var res controlcommands.MesosCommandResponse_Transition - err = json.Unmarshal(data, &res) - if err != nil { - log.WithPrefix("scheduler").WithFields(logrus.Fields{ - "commandName": incomingCommand.CommandName, - "agentId": agentId.GetValue(), - "executorId": executorId.GetValue(), - "message": string(data[:]), - "error": err.Error(), - }). - Error("cannot unmarshal incoming MESSAGE") - return - } - sender := controlcommands.MesosCommandTarget{ - AgentId: agentId, - ExecutorId: executorId, - TaskId: mesos.TaskID{Value: res.TaskId}, - } - - go func() { - state.taskman.UpdateTaskState(res.TaskId, res.CurrentState) - state.servent.ProcessResponse(&res, sender) - }() - return - default: - return errors.New(fmt.Sprintf("unrecognized response for controlcommand %s", incomingCommand.CommandName)) - } - } - return - } -} - -func handleDeviceEvent(state *internalState, evt event.DeviceEvent) { - if evt == nil { - log.WithPrefix("scheduler").Error("cannot handle null DeviceEvent") - return - } - - switch evt.GetType() { - case pb.DeviceEventType_END_OF_DATA: - taskId := evt.GetOrigin().TaskId - t := state.taskman.GetTask(taskId.Value) - if t == nil { - log.WithPrefix("scheduler").Error("cannot find task for DeviceEvent") - return - } - env, err := state.environments.Environment(t.GetEnvironmentId().UUID()) - if err != nil { - log.WithPrefix("scheduler").WithError(err).Error("cannot find environment for DeviceEvent") - } - if env.CurrentState() == "RUNNING" { - err = env.TryTransition(environment.NewStopActivityTransition(state.taskman)) - if err != nil { - log.WithPrefix("scheduler").WithError(err).Error("cannot stop run after END_OF_DATA event") - } - } - } -} - -// Handler for Event_OFFERS -func resourceOffers(state *internalState, fidStore store.Singleton) events.HandlerFunc { - return func(ctx context.Context, e *scheduler.Event) error { - var ( - offers = e.GetOffers().GetOffers() - callOption = calls.RefuseSeconds(time.Second)//calls.RefuseSecondsWithJitter(state.random, state.config.maxRefuseSeconds) - tasksLaunchedThisCycle = 0 - offersDeclined = 0 - ) - - if viper.GetBool("veryVerbose") { - var( - prettyOffers []string - offerIds []string - ) - for i := range offers { - prettyOffer, _ := json.MarshalIndent(offers[i], "", "\t") - prettyOffers = append(prettyOffers, string(prettyOffer)) - offerIds = append(offerIds, offers[i].ID.Value) - } - log.WithPrefix("scheduler").WithFields(logrus.Fields{ - "offerIds": strings.Join(offerIds, ", "), - //"offers": strings.Join(prettyOffers, "\n"), - }).Debug("received offers") - } - - var descriptorsToDeploy task.Descriptors - select { - case descriptorsToDeploy = <- state.tasksToDeploy: - if viper.GetBool("veryVerbose") { - rolePaths := make([]string, len(descriptorsToDeploy)) - taskClasses := make([]string, len(descriptorsToDeploy)) - for i, d := range descriptorsToDeploy { - rolePaths[i] = d.TaskRole.GetPath() - taskClasses[i] = d.TaskClassName - } - log.WithPrefix("scheduler"). - WithFields(logrus.Fields{ - "roles": strings.Join(rolePaths, ", "), - "classes": strings.Join(taskClasses, ", "), - }). - Debug("received descriptors for tasks to deploy on this offers round") - } - default: - if viper.GetBool("veryVerbose") { - log.WithPrefix("scheduler").Debug("no roles need deployment") - } - } - - // by default we get ready to decline all offers - offerIDsToDecline := make(map[mesos.OfferID]struct{}, len(offers)) - for i := range offers { - offerIDsToDecline[offers[i].ID] = struct{}{} - } - - tasksDeployed := make(task.DeploymentMap) - - if len(descriptorsToDeploy) > 0 { - // 3 ways to make decisions - // * FLP1, FLP2, ... , EPN1, EPN2, ... o2-roles as mesos attributes of an agent - // * readout cards as resources - // * o2 machine types (FLP, EPN) as mesos-roles so that other frameworks never get - // offers for stuff that doesn't belong to them i.e. readout cards - - // Walk through the roles list and find out if the current []offers satisfies - // what we need. - - log.WithPrefix("scheduler").Debug("about to deploy workflow tasks") - - var err error - - // We make a map[Descriptor]constraint.Constraints and for each descriptor to deploy we - // fill it with the pre-computed total constraints for that Descriptor. - descriptorConstraints := state.taskman.BuildDescriptorConstraints(descriptorsToDeploy) - - // NOTE: 1 offer per host - for _, offer := range offers { - var ( - remainingResources = mesos.Resources(offer.Resources) - tasks = make([]mesos.TaskInfo, 0) - tasksDeployedForCurrentOffer = make(task.DeploymentMap) - targetExecutorId = mesos.ExecutorID{} - ) - - if len(offer.ExecutorIDs) == 0 { - targetExecutorId.Value = uuid.NewUUID().String() - } else { - targetExecutorId.Value = offer.ExecutorIDs[0].Value - } - - log.WithPrefix("scheduler"). - WithFields(logrus.Fields{ - "offerId": offer.ID.Value, - "resources": remainingResources.String(), - }).Debug("processing offer") - - remainingResourcesFlattened := resources.Flatten(remainingResources) - - // avoid the expense of computing these if we can... - if viper.GetBool("summaryMetrics") && viper.GetBool("mesosResourceTypeMetrics") { - for name, resType := range resources.TypesOf(remainingResourcesFlattened...) { - if resType == mesos.SCALAR { - sum, _ := name.Sum(remainingResourcesFlattened...) - state.metricsAPI.offeredResources(sum.GetScalar().GetValue(), name.String()) - } - } - } - - log.WithPrefix("scheduler").Debug("state lock") - state.Lock() - - // We iterate down over the descriptors, and we remove them as we match - FOR_DESCRIPTORS: - for i := len(descriptorsToDeploy)-1; i >= 0; i-- { - descriptor := descriptorsToDeploy[i] - log.WithPrefix("scheduler"). - WithField("taskClass", descriptor.TaskClassName). - Debug("processing descriptor") - offerAttributes := constraint.Attributes(offer.Attributes) - if !offerAttributes.Satisfy(descriptorConstraints[descriptor]) { - if viper.GetBool("veryVerbose") { - log.WithPrefix("scheduler"). - WithFields(logrus.Fields{ - "taskClass": descriptor.TaskClassName, - "constraints": descriptorConstraints[descriptor], - "offerId": offer.ID.Value, - "resources": remainingResources.String(), - "attributes": offerAttributes.String(), - }). - Warn("descriptor constraints not satisfied by offer attributes") - } - continue - } - log.WithPrefix("scheduler").Debug("offer attributes satisfy constraints") - - wants := state.taskman.GetWantsForDescriptor(descriptor) - if wants == nil { - log.WithPrefix("scheduler").WithField("class", descriptor.TaskClassName). - Warning("no resource demands for descriptor, invalid class perhaps?") - continue - } - if !task.Resources(remainingResources).Satisfy(wants) { - continue - } - - // Point of no return, we start subtracting resources - - bindMap := make(map[string]uint64) - for _, ch := range wants.BindPorts { - availPorts, ok := resources.Ports(remainingResources...) - if !ok { - continue FOR_DESCRIPTORS - } - // TODO: this can be optimized by excluding the base range outside the loop - availPorts = availPorts.Remove(mesos.Value_Range{Begin: 0, End: 8999}) - port := availPorts.Min() - builder := resources.Build(). - Name(resources.Name("ports")). - Ranges(resources.BuildRanges().Span(port, port).Ranges) - remainingResources.Subtract(builder.Resource) - bindMap[ch.Name] = port - } - - agentForCache := task.AgentCacheInfo{ - AgentId: offer.AgentID, - Attributes: offer.Attributes, - Hostname: offer.Hostname, - } - state.taskman.AgentCache.Update(agentForCache) //thread safe - - taskPtr := state.taskman.NewTaskForMesosOffer(&offer, descriptor, bindMap, targetExecutorId) - if taskPtr == nil { - log.WithPrefix("scheduler"). - WithField("offerId", offer.ID.Value). - Error("cannot get task for offer+descriptor, this should never happen") - log.Debug("state unlock") - continue - } - - // Do not decline this offer - _, contains := offerIDsToDecline[offer.ID] - if contains { - delete(offerIDsToDecline, offer.ID) - } - - // Define the O² process to run as a mesos.CommandInfo, which we'll then JSON-serialize - cmd := taskPtr.BuildTaskCommand() - - // Claim the control port - availPorts, ok := resources.Ports(remainingResources...) - if !ok { - continue FOR_DESCRIPTORS - } - // The control port range starts at 47101 - // FIXME: make the control ports cutoff configurable - availPorts = availPorts.Remove(mesos.Value_Range{Begin: 0, End: 29999}) - controlPort := availPorts.Min() - builder := resources.Build(). - Name(resources.Name("ports")). - Ranges(resources.BuildRanges().Span(controlPort, controlPort).Ranges) - remainingResources.Subtract(builder.Resource) - - // Append control port to arguments - // For the control port parameter and/or environment variable, see occ/OccGlobals.h - cmd.Arguments = append(cmd.Arguments, "--control-port", strconv.FormatUint(controlPort, 10)) - cmd.ControlPort = controlPort - cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%d", "OCC_CONTROL_PORT", controlPort)) - cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", "O2_ROLE", offer.Hostname)) - - runCommand := *cmd - - // Serialize the actual command to be passed to the executor - var jsonCommand []byte - jsonCommand, err = json.Marshal(&runCommand) - if err != nil { - log.WithPrefix("scheduler"). - WithFields(logrus.Fields{ - "error": err.Error(), - "value": *runCommand.Value, - "args": runCommand.Arguments, - "shell": *runCommand.Shell, - "json": jsonCommand, - }). - Error("cannot serialize mesos.CommandInfo for executor") - state.Unlock() - log.WithPrefix("scheduler").Debug("state unlock") - continue - } - - // Build resources request - resourcesRequest := make(mesos.Resources, 0) - resourcesRequest.Add1(resources.NewCPUs(wants.Cpu).Resource) - resourcesRequest.Add1(resources.NewMemory(wants.Memory).Resource) - portsBuilder := resources.BuildRanges() - for _, rng := range wants.StaticPorts { - portsBuilder = portsBuilder.Span(rng.Begin, rng.End) - } - for _, port := range bindMap { - portsBuilder = portsBuilder.Span(port, port) - } - portsBuilder = portsBuilder.Span(controlPort, controlPort) - - portRanges := portsBuilder.Ranges.Sort().Squash() - portsResources := resources.Build().Name(resources.Name("ports")).Ranges(portRanges) - resourcesRequest.Add1(portsResources.Resource) - - // Append executor resources to request - executorResources := mesos.Resources(state.executor.Resources) - log.WithPrefix("scheduler"). - WithField("taskResources", resourcesRequest). - WithField("executorResources", executorResources). - Debug("creating Mesos task") - resourcesRequest.Add(executorResources...) - - newTaskId := taskPtr.GetTaskId() - - executor := state.executor - executor.ExecutorID.Value = taskPtr.GetExecutorId() - - mesosTaskInfo := mesos.TaskInfo{ - Name: taskPtr.GetName(), - TaskID: mesos.TaskID{Value: newTaskId}, - AgentID: offer.AgentID, - Executor: executor, - Resources: resourcesRequest, - Data: jsonCommand, // this ends up in LAUNCH for the executor - } - - // We must run the executor with a special LD_LIBRARY_PATH because - // its InfoLogger binding is built with GCC-Toolchain - ldLibPath, ok := agentForCache.Attributes.Get("executor_env_LD_LIBRARY_PATH") - mesosTaskInfo.Executor.Command.Environment = &mesos.Environment{} - if ok { - mesosTaskInfo.Executor.Command.Environment.Variables = - append(mesosTaskInfo.Executor.Command.Environment.Variables, - mesos.Environment_Variable{ - Name: "LD_LIBRARY_PATH", - Value: proto.String(ldLibPath), - }) - } - - log.WithPrefix("scheduler"). - WithFields(logrus.Fields{ - "taskId": newTaskId, - "offerId": offer.ID.Value, - "executorId": state.executor.ExecutorID.Value, - "task": mesosTaskInfo, - }).Debug("launching task") - - tasks = append(tasks, mesosTaskInfo) - descriptorsToDeploy = append(descriptorsToDeploy[:i], descriptorsToDeploy[i+1:]...) - tasksDeployedForCurrentOffer[taskPtr] = descriptor - } - state.Unlock() - log.WithPrefix("scheduler").Debug("state unlock") - - // build ACCEPT call to launch all of the tasks we've assembled - accept := calls.Accept( - calls.OfferOperations{calls.OpLaunch(tasks...)}.WithOffers(offer.ID), - ).With(callOption) // handles refuseSeconds etc. - - // send ACCEPT call to mesos - err = calls.CallNoData(ctx, state.cli, accept) - if err != nil { - log.WithPrefix("scheduler").WithField("error", err.Error()). - Error("failed to launch tasks") - // FIXME: we probably need to react to a failed ACCEPT here - } else { - if n := len(tasks); n > 0 { - tasksLaunchedThisCycle += n - log.WithPrefix("scheduler").WithField("tasks", n). - Info("tasks launched") - for _, taskInfo := range tasks { - log.WithPrefix("scheduler"). - WithFields(logrus.Fields{ - "executorId": taskInfo.GetExecutor().ExecutorID.Value, - "executorName": taskInfo.GetExecutor().GetName(), - "agentId": taskInfo.GetAgentID().Value, - "taskId": taskInfo.GetTaskID().Value, - }). - Debug("launched") - } - - // update deployment map - for k, v := range tasksDeployedForCurrentOffer { - tasksDeployed[k] = v - } - } else { - offersDeclined++ - } - } - } // end for _, offerUsed := range offersUsed - } // end if len(descriptorsToDeploy) > 0 - - // build DECLINE call to reject offers we don't need any more - declineSlice := make([]mesos.OfferID, len(offerIDsToDecline)) - j := 0 - for k := range offerIDsToDecline { - declineSlice[j] = k - j++ - } - decline := calls.Decline(declineSlice...).With(callOption) - - if n := len(offerIDsToDecline); n > 0 { - err := calls.CallNoData(ctx, state.cli, decline) - if err != nil { - log.WithPrefix("scheduler").WithField("error", err.Error()). - Error("failed to decline tasks") - } else { - log.WithPrefix("scheduler").WithField("offers", n). - Debug("offers declined") - } - } else { - log.WithPrefix("scheduler").Info("no offers to decline") - } - - // Notify listeners... - select { - case state.resourceOffersDone <- tasksDeployed: - log.WithPrefix("scheduler"). - WithField("tasksDeployed", len(tasksDeployed)). - Debug("notified listeners on resourceOffers done") - default: - if viper.GetBool("veryVerbose") { - log.WithPrefix("scheduler"). - Debug("no listeners notified") - } - } - - // Update metrics... - state.metricsAPI.offersDeclined.Int(offersDeclined) - state.metricsAPI.tasksLaunched.Int(tasksLaunchedThisCycle) - if viper.GetBool("summaryMetrics") { - state.metricsAPI.launchesPerOfferCycle(float64(tasksLaunchedThisCycle)) - } - var msg string - if tasksLaunchedThisCycle == 0 { - msg = "offers cycle complete, no tasks launched" - } else { - msg = "offers cycle complete, tasks launched" - } - log.WithPrefix("scheduler").WithField("tasks", tasksLaunchedThisCycle).Debug(msg) - return nil - } -} - -// statusUpdate handles an incoming UPDATE event. -// This func runs after acknowledgement. -func statusUpdate(state *internalState) events.HandlerFunc { - return func(ctx context.Context, e *scheduler.Event) error { - s := e.GetUpdate().GetStatus() - if viper.GetBool("verbose") { - log.WithPrefix("scheduler").WithFields(logrus.Fields{ - "task": s.TaskID.Value, - "state": s.GetState().String(), - "message": s.GetMessage(), - }).Debug("task status update received") - } - - // What's the new task state? - switch st := s.GetState(); st { - - case mesos.TASK_FINISHED: - log.WithPrefix("scheduler").Debug("state lock") - state.Lock() - state.tasksFinished++ - state.metricsAPI.tasksFinished() - - // FIXME: this should not quit when all tasks are done, but rather do some transition - /* - if state.tasksFinished == state.totalTasks { - log.Println("Mission accomplished, all tasks completed. Terminating scheduler.") - state.shutdown() - } else { - tryReviveOffers(ctx, state) - }*/ - state.Unlock() - log.WithPrefix("scheduler").Debug("state unlock") - - case mesos.TASK_LOST, mesos.TASK_KILLED, mesos.TASK_FAILED, mesos.TASK_ERROR: - log.WithPrefix("scheduler"). - WithFields(logrus.Fields{ - "taskId": s.GetTaskID().Value, - "state": st.String(), - "reason": s.GetReason().String(), - "source": s.GetSource().String(), - "message": s.GetMessage(), - }). - Info("task inactive exception") - } - - // Enqueue task state update - go state.taskman.UpdateTaskStatus(&s) - - return nil - } -} - -// tryReviveOffers sends a REVIVE call to Mesos. With this we clear all filters we might previously -// have set through ACCEPT or DECLINE calls, in the hope that Mesos then sends us new resource offers. -// This should generally run when we have received a TASK_FINISHED for some tasks, and we have more -// tasks to run. -func tryReviveOffers(ctx context.Context, state *internalState) { - // limit the rate at which we request offer revival - select { - case <-state.reviveTokens: - // not done yet, revive offers! - doReviveOffers(ctx, state) - default: - // noop - } -} - -func doReviveOffers(ctx context.Context, state *internalState) { - err := calls.CallNoData(ctx, state.cli, calls.Revive()) - if err != nil { - log.WithPrefix("scheduler").WithField("error", err.Error()). - Error("failed to revive offers") - return - } - log.WithPrefix("scheduler").Debug("revive offers done") -} - -func KillTask(ctx context.Context, state *internalState, receiver controlcommands.MesosCommandTarget) (err error) { - killCall := calls.Kill(receiver.TaskId.GetValue(), receiver.AgentId.GetValue()) - - err = calls.CallNoData(ctx, state.cli, killCall) - return -} - -func SendCommand(ctx context.Context, state *internalState, command controlcommands.MesosCommand, receiver controlcommands.MesosCommandTarget) (err error) { - log.Debug("SendCommand BEGIN") - defer log.Debug("SendCommand END") - var bytes []byte - bytes, err = json.Marshal(command) - if err != nil { - return - } - - message := calls.Message(receiver.AgentId.Value, receiver.ExecutorId.Value, bytes) - - err = calls.CallNoData(ctx, state.cli, message) - - log.WithPrefix("scheduler"). - WithFields(logrus.Fields{ - "agentId": receiver.AgentId.Value, - "executorId": receiver.ExecutorId.Value, - "payload": string(bytes), - "error": func() string { if err == nil { return "nil" } else { return err.Error() } }(), - }). - Debug("outgoing MESSAGE call") - - return err -} - -// logAllEvents logs every observed event; this is somewhat expensive to do so it only happens if -// the config is verbose. -func logAllEvents() eventrules.Rule { - return func(ctx context.Context, e *scheduler.Event, err error, ch eventrules.Chain) (context.Context, *scheduler.Event, error) { - log.WithPrefix("scheduler").WithField("event", fmt.Sprintf("%+v", *e)). - Debug("incoming event") - return ch(ctx, e, err) - } -} - -// eventMetrics logs metrics for every processed API event -func eventMetrics(metricsAPI *metricsAPI, clock func() time.Time, timingMetrics bool) eventrules.Rule { - timed := metricsAPI.eventReceivedLatency - if !timingMetrics { - timed = nil - } - harness := xmetrics.NewHarness(metricsAPI.eventReceivedCount, metricsAPI.eventErrorCount, timed, clock) - return eventrules.Metrics(harness, nil) -} - -// callMetrics logs metrics for every outgoing Mesos call -func callMetrics(metricsAPI *metricsAPI, clock func() time.Time, timingMetrics bool) callrules.Rule { - timed := metricsAPI.callLatency - if !timingMetrics { - timed = nil - } - harness := xmetrics.NewHarness(metricsAPI.callCount, metricsAPI.callErrorCount, timed, clock) - return callrules.Metrics(harness, nil) -} - -// logCalls logs a specific message string when a particular call-type is observed -func logCalls(messages map[scheduler.Call_Type]string) callrules.Rule { - return func(ctx context.Context, c *scheduler.Call, r mesos.Response, err error, ch callrules.Chain) (context.Context, *scheduler.Call, mesos.Response, error) { - if message, ok := messages[c.GetType()]; ok { - log.WithPrefix("scheduler").Debug(message) - } - return ch(ctx, c, r, err) - } -} diff --git a/core/server.go b/core/server.go index bd984d8c3..b6e50b816 100644 --- a/core/server.go +++ b/core/server.go @@ -1,7 +1,7 @@ /* * === This file is part of ALICE O² === * - * Copyright 2018 CERN and copyright holders of ALICE O². + * Copyright 2018-2022 CERN and copyright holders of ALICE O². * Author: Teo Mrnjavac * * This program is free software: you can redistribute it and/or modify @@ -22,17 +22,37 @@ * Intergovernmental Organization or submit itself to any jurisdiction. */ -//go:generate protoc --gofast_out=plugins=grpc:. protos/o2control.proto +//go:generate protoc -I=./ -I=../common/ --go_out=. --go_opt=paths=source_relative --go-grpc_opt=paths=source_relative --go-grpc_out=require_unimplemented_servers=false:. protos/o2control.proto + package core import ( + "encoding/json" "fmt" - "github.com/spf13/viper" + "maps" "runtime" "sort" "strconv" "time" + "github.com/AliceO2Group/Control/common/event/topic" + "github.com/AliceO2Group/Control/common/logger/infologger" + evpb "github.com/AliceO2Group/Control/common/protos" + "github.com/AliceO2Group/Control/common/system" + "github.com/AliceO2Group/Control/common/utils" + "github.com/AliceO2Group/Control/common/utils/uid" + "github.com/AliceO2Group/Control/core/integration" + "github.com/AliceO2Group/Control/core/repos" + "github.com/AliceO2Group/Control/core/repos/varsource" + "github.com/AliceO2Group/Control/core/workflow" + "github.com/jinzhu/copier" + "github.com/spf13/viper" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/health" + "google.golang.org/grpc/health/grpc_health_v1" + "google.golang.org/grpc/reflection" + "google.golang.org/grpc/status" + "github.com/AliceO2Group/Control/common/product" "github.com/AliceO2Group/Control/core/task" "github.com/AliceO2Group/Control/core/task/channel" @@ -41,21 +61,17 @@ import ( "google.golang.org/grpc" "github.com/AliceO2Group/Control/core/environment" - "github.com/AliceO2Group/Control/core/protos" - "github.com/looplab/fsm" - "github.com/mesos/mesos-go/api/v1/lib/extras/store" - "github.com/pborman/uuid" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/reflection" - "google.golang.org/grpc/status" + pb "github.com/AliceO2Group/Control/core/protos" ) +const MAX_ERROR_LENGTH = 6000 // gRPC seems to impose this limit on the status message -func NewServer(state *internalState, fidStore store.Singleton) *grpc.Server { +func NewServer(state *globalState) *grpc.Server { s := grpc.NewServer() + grpc_health_v1.RegisterHealthServer(s, health.NewServer()) pb.RegisterControlServer(s, &RpcServer{ - state: state, - fidStore: fidStore, + state: state, + envStreams: newSafeStreamsMap(), }) // Register reflection service on gRPC server. reflection.Register(s) @@ -63,9 +79,27 @@ func NewServer(state *internalState, fidStore store.Singleton) *grpc.Server { } func (m *RpcServer) logMethod() { - if !viper.GetBool("verbose") { + //if !viper.GetBool("verbose") { + // return + //} + pc, _, _, ok := runtime.Caller(1) + if !ok { + return + } + fun := runtime.FuncForPC(pc) + if fun == nil { return } + log.WithPrefix("rpcserver"). + WithField("method", fun.Name()). + WithField("level", infologger.IL_Support). + Debug("handling RPC request") +} + +func (m *RpcServer) logMethodHandled() { + //if !viper.GetBool("verbose") { + // return + //} pc, _, _, ok := runtime.Caller(1) if !ok { return @@ -76,47 +110,99 @@ func (m *RpcServer) logMethod() { } log.WithPrefix("rpcserver"). WithField("method", fun.Name()). - Debug("handling RPC request") + WithField("level", infologger.IL_Support). + Debug("handling RPC request DONE") } // Implements interface pb.ControlServer type RpcServer struct { - state *internalState - fidStore store.Singleton + state *globalState + envStreams SafeStreamsMap } -func (*RpcServer) TrackStatus(*pb.StatusRequest, pb.Control_TrackStatusServer) error { - log.WithPrefix("rpcserver"). - WithField("method", "TrackStatus"). - Debug("implement me") +// We are setting up timestamp field of proto files in the moment of function return, +// NOT when object is created. This function is meant to be used with defer keyword +func setCurrentUnixMilli(valueToSet *int64) { + *valueToSet = currentUnixMilli() +} + +func currentUnixMilli() int64 { + return time.Now().UnixMilli() +} + +func (m *RpcServer) GetIntegratedServices(ctx context.Context, empty *pb.Empty) (*pb.ListIntegratedServicesReply, error) { + defer utils.TimeTrackFunction(time.Now(), log.WithPrefix("rpcserver")) + m.logMethod() + defer m.logMethodHandled() + + services := make(map[string]*pb.IntegratedServiceInfo) + + for pluginName := range integration.RegisteredPlugins() { + s := &pb.IntegratedServiceInfo{} + var plugin integration.Plugin + for _, p := range integration.PluginsInstance() { + if pluginName == p.GetName() { + plugin = p + break + } + } + + // If this plugin was loaded + if plugin != nil { + s = &pb.IntegratedServiceInfo{ + Name: plugin.GetPrettyName(), + Enabled: false, + Endpoint: plugin.GetEndpoint(), + ConnectionState: plugin.GetConnectionState(), + Data: plugin.GetData(nil), + } + enabledPlugins := viper.GetStringSlice("integrationPlugins") + if utils.StringSliceContains(enabledPlugins, pluginName) { + s.Enabled = true + } + } + services[pluginName] = s + } - return status.New(codes.Unimplemented, "not implemented").Err() + return &pb.ListIntegratedServicesReply{Services: services, Timestamp: currentUnixMilli()}, nil } func (m *RpcServer) GetFrameworkInfo(context.Context, *pb.GetFrameworkInfoRequest) (*pb.GetFrameworkInfoReply, error) { + defer utils.TimeTrackFunction(time.Now(), log.WithPrefix("rpcserver")) m.logMethod() - m.state.RLock() - defer m.state.RUnlock() + defer m.logMethodHandled() - maj, _ := strconv.ParseInt(product.VERSION_MAJOR, 10, 32) - min, _ := strconv.ParseInt(product.VERSION_MINOR, 10, 32) - pat, _ := strconv.ParseInt(product.VERSION_PATCH, 10, 32) + major, _ := strconv.ParseInt(product.VERSION_MAJOR, 10, 32) + minor, _ := strconv.ParseInt(product.VERSION_MINOR, 10, 32) + patch, _ := strconv.ParseInt(product.VERSION_PATCH, 10, 32) + + availableDetectors, activeDetectors, allDetectors, err := m.listDetectors() + if err != nil { + allDetectors = []string{"NIL"} + availableDetectors = []string{} + activeDetectors = []string{} + } r := &pb.GetFrameworkInfoReply{ - FrameworkId: store.GetIgnoreErrors(m.fidStore)(), - EnvironmentsCount: int32(len(m.state.environments.Ids())), - TasksCount: int32(m.state.taskman.TaskCount()), - State: m.state.sm.Current(), - HostsCount: int32(m.state.taskman.AgentCache.Count()), - InstanceName: viper.GetString("instanceName"), - Version: &pb.Version{ - Major: int32(maj), - Minor: int32(min), - Patch: int32(pat), - Build: product.BUILD, - VersionStr: product.VERSION, - ProductName: product.PRETTY_SHORTNAME, + FrameworkId: m.state.taskman.GetFrameworkID(), + EnvironmentsCount: int32(len(m.state.environments.Ids())), + TasksCount: int32(m.state.taskman.TaskCount()), + State: m.state.taskman.GetState(), + HostsCount: int32(m.state.taskman.AgentCache.Count()), + InstanceName: viper.GetString("instanceName"), + Version: &pb.Version{ + Major: int32(major), + Minor: int32(minor), + Patch: int32(patch), + Build: product.BUILD, + VersionStr: product.VERSION, + ProductName: product.PRETTY_SHORTNAME, }, + ConfigurationEndpoint: viper.GetString("configServiceUri"), + DetectorsInInstance: allDetectors, + ActiveDetectors: activeDetectors, + AvailableDetectors: availableDetectors, + Timestamp: currentUnixMilli(), } return r, nil } @@ -128,67 +214,94 @@ func (*RpcServer) Teardown(context.Context, *pb.TeardownRequest) (*pb.TeardownRe return nil, status.New(codes.Unimplemented, "not implemented").Err() } -type EnvironmentInfos []*pb.EnvironmentInfo -func (infos EnvironmentInfos) Len() int { - return len(infos) -} -func (infos EnvironmentInfos) Less(i, j int) bool { - iv := infos[i] - jv := infos[j] - if iv == nil { - return true - } - if jv == nil { - return false - } - iTime, err := time.Parse(time.RFC3339, iv.CreatedWhen) - if err != nil { - return true - } - jTime, err := time.Parse(time.RFC3339, jv.CreatedWhen) - if err != nil { - return false - } - if iTime.Unix() < jTime.Unix() { - return true - } else { - return false - } -} -func (infos EnvironmentInfos) Swap(i, j int) { - var temp *pb.EnvironmentInfo - temp = infos[i] - infos[i] = infos[j] - infos[j] = temp -} - -func (m *RpcServer) GetEnvironments(context.Context, *pb.GetEnvironmentsRequest) (*pb.GetEnvironmentsReply, error) { +func (m *RpcServer) GetEnvironments(cxt context.Context, request *pb.GetEnvironmentsRequest) (*pb.GetEnvironmentsReply, error) { + defer utils.TimeTrackFunction(time.Now(), log.WithPrefix("rpcserver")) m.logMethod() - m.state.RLock() - defer m.state.RUnlock() + defer m.logMethodHandled() r := &pb.GetEnvironmentsReply{ - FrameworkId: store.GetIgnoreErrors(m.fidStore)(), + FrameworkId: m.state.taskman.GetFrameworkID(), Environments: make(EnvironmentInfos, 0, 0), } + defer setCurrentUnixMilli(&r.Timestamp) + + // Get plugin-provided environment data for all envs + var integratedServicesEnvsData map[uid.ID]map[string]string + if request.GetShowDetailedIntegratedServices() { + integratedServicesEnvsData = integration.PluginsInstance().GetEnvironmentsData(m.state.environments.Ids()) + } else { + integratedServicesEnvsData = integration.PluginsInstance().GetEnvironmentsShortData(m.state.environments.Ids()) + } + + // Get all environments for _, id := range m.state.environments.Ids() { env, err := m.state.environments.Environment(id) if err != nil { log.WithPrefix("rpcserver"). WithField("error", err). - WithField("envId", id.String()). + WithField("partition", id.String()). Error("cannot get environment") continue } + if !request.ShowAll && !env.Public { + continue + } tasks := env.Workflow().GetTasks() + var defaults, vars, userVars map[string]string + defaults, vars, userVars, err = env.Workflow().ConsolidatedVarMaps() + if err != nil { + // take raw of copy, not raw of original because the actual varstack is mutex protected + defaults = env.GlobalDefaults.RawCopy() + vars = env.GlobalVars.RawCopy() + userVars = env.UserVars.RawCopy() + } + + isEnvData, ok := integratedServicesEnvsData[id] + if !ok || isEnvData == nil { + isEnvData = make(map[string]string) + } + e := &pb.EnvironmentInfo{ - Id: env.Id().String(), - CreatedWhen: env.CreatedWhen().Format(time.RFC3339), - State: env.CurrentState(), - Tasks: tasksToShortTaskInfos(tasks), - RootRole: env.Workflow().GetName(), - CurrentRunNumber: env.GetCurrentRunNumber(), + Id: env.Id().String(), + CreatedWhen: env.CreatedWhen().UnixMilli(), + State: env.CurrentState(), + RootRole: env.Workflow().GetName(), + Description: env.Description, + CurrentRunNumber: env.GetCurrentRunNumber(), + Defaults: defaults, + Vars: vars, + UserVars: userVars, + NumberOfFlps: int32(len(env.GetFLPs())), + NumberOfHosts: int32(len(env.GetAllHosts())), + NumberOfTasks: int32(len(tasks)), + IntegratedServicesData: isEnvData, + CurrentTransition: env.CurrentTransition(), + NumberOfActiveTasks: int32(len(tasks.Filtered(func(t *task.Task) bool { + parent := t.GetParentRole() + if parent == nil { + return false + } + if parentRole, ok := parent.(workflow.Role); ok { + return parentRole.GetStatus() == task.ACTIVE + } + return false + }))), + NumberOfInactiveTasks: int32(len(tasks.Filtered(func(t *task.Task) bool { + parent := t.GetParentRole() + if parent == nil { + return true // if task has no parent, we treat it as inactive + } + if parentRole, ok := parent.(workflow.Role); ok { + return parentRole.GetStatus() != task.ACTIVE + } + return false + }))), } + if request.GetShowTaskInfos() { + e.Tasks = tasksToShortTaskInfos(tasks, m.state.taskman) + } + + e.IncludedDetectors = env.GetActiveDetectors().StringList() r.Environments = append(r.Environments, e) } @@ -197,8 +310,88 @@ func (m *RpcServer) GetEnvironments(context.Context, *pb.GetEnvironmentsRequest) return r, nil } -func (m *RpcServer) NewEnvironment(cxt context.Context, request *pb.NewEnvironmentRequest) (*pb.NewEnvironmentReply, error) { +func (m *RpcServer) doNewEnvironmentAsync(cxt context.Context, userVars map[string]string, requestUser *evpb.User, workflowTemplate string, public bool, autoTransition bool, id uid.ID) { + var err error + + // we store the last known request user in the environment + lastRequestUserJ, _ := json.Marshal(requestUser) + userVars["last_request_user"] = string(lastRequestUserJ[:]) + id, err = m.state.environments.CreateEnvironment(workflowTemplate, userVars, public, id, autoTransition) + if err != nil { + the.EventWriterWithTopic(topic.Environment).WriteEvent(&evpb.Ev_EnvironmentEvent{ + EnvironmentId: id.String(), + State: "ERROR", + Error: err.Error(), + Message: "cannot create new environment", // GUI listens for this concrete string + LastRequestUser: requestUser, + WorkflowTemplateInfo: &evpb.WorkflowTemplateInfo{ + Public: public, + Path: workflowTemplate, + }, + }) + return + } + + newEnv, err := m.state.environments.Environment(id) + if err != nil { + the.EventWriterWithTopic(topic.Environment).WriteEvent(&evpb.Ev_EnvironmentEvent{ + EnvironmentId: id.String(), + State: "ERROR", + Error: err.Error(), + Message: "cannot get newly created environment", // GUI listens for this concrete string + LastRequestUser: requestUser, + WorkflowTemplateInfo: &evpb.WorkflowTemplateInfo{ + Public: public, + Path: workflowTemplate, + }, + }) + return + } + + the.EventWriterWithTopic(topic.Environment).WriteEvent(&evpb.Ev_EnvironmentEvent{ + EnvironmentId: id.String(), + State: newEnv.CurrentState(), + LastRequestUser: requestUser, + WorkflowTemplateInfo: newEnv.GetWorkflowInfo(), + Message: "created new environment", + }) + return +} + +func (m *RpcServer) NewEnvironmentAsync(cxt context.Context, request *pb.NewEnvironmentRequest) (reply *pb.NewEnvironmentReply, err error) { + defer utils.TimeTrackFunction(time.Now(), log.WithPrefix("rpcserver")) m.logMethod() + + // Create new Environment instance with some roles, we get back a UUID + id := uid.New() + + // We must make a copy of the userVars, otherwise there will be a race for access with gRPC + userVars := make(map[string]string) + if len(request.GetVars()) > 0 { + maps.Copy(userVars, request.GetVars()) + } + + ei := &pb.EnvironmentInfo{ + Id: id.String(), + State: "PENDING", + UserVars: request.GetVars(), + } + reply = &pb.NewEnvironmentReply{ + Environment: ei, + Public: request.GetPublic(), + } + defer setCurrentUnixMilli(&reply.Timestamp) + + go m.doNewEnvironmentAsync(cxt, userVars, request.GetRequestUser(), request.GetWorkflowTemplate(), request.GetPublic(), request.GetAutoTransition(), id) + + return +} + +func (m *RpcServer) NewEnvironment(cxt context.Context, request *pb.NewEnvironmentRequest) (reply *pb.NewEnvironmentReply, err error) { + defer utils.TimeTrackFunction(time.Now(), log.WithPrefix("rpcserver")) + m.logMethod() + defer m.logMethodHandled() + // NEW_ENVIRONMENT transition // The following should // 1) Create a new value of type Environment struct @@ -209,101 +402,275 @@ func (m *RpcServer) NewEnvironment(cxt context.Context, request *pb.NewEnvironme // make sure they are now successfully in CONFIGURED // 5) Report back here with the new environment id and error code, if needed. - if m.state.sm.Cannot("NEW_ENVIRONMENT") { - msg := fmt.Sprintf("NEW_ENVIRONMENT transition impossible, current state: %s", - m.state.sm.Current()) - return nil, status.Error(codes.Internal, msg) - } - err := m.state.sm.Event("NEW_ENVIRONMENT") //Async until Transition call - defer m.state.sm.Transition() - - if _, ok := err.(fsm.NoTransitionError); !ok && err != nil { - return nil, status.Newf(codes.Internal, "cannot create new environment: %s", err.Error()).Err() + // FIXME: figure out if the state.sm becomes a task.Manager sm, or no global sm at all + //if m.state.sm.Cannot("NEW_ENVIRONMENT") { + // msg := fmt.Sprintf("NEW_ENVIRONMENT transition impossible, current state: %s", + // m.state.sm.Current()) + // return nil, status.Error(codes.Internal, msg) + //} + //err := m.state.sm.Event("NEW_ENVIRONMENT") //Async until Transition call + //defer m.state.sm.Transition() + // + //if _, ok := err.(fsm.NoTransitionError); !ok && err != nil { + // return nil, status.Newf(codes.Internal, "cannot create new environment: %s", err.Error()).Err() + //} + + // TODO: is this reply necessary here? We are creating new one at the end of this function + reply = &pb.NewEnvironmentReply{Public: request.Public} + defer setCurrentUnixMilli(&reply.Timestamp) + + inputUserVars := request.GetVars() + if len(inputUserVars) == 0 { + inputUserVars = make(map[string]string) } + // we store the last known request user in the environment + lastRequestUserJ, _ := json.Marshal(request.RequestUser) + inputUserVars["last_request_user"] = string(lastRequestUserJ[:]) // Create new Environment instance with some roles, we get back a UUID - id, err := m.state.environments.CreateEnvironment(request.GetWorkflowTemplate()) + id := uid.New() + id, err = m.state.environments.CreateEnvironment(request.GetWorkflowTemplate(), inputUserVars, request.GetPublic(), id, request.GetAutoTransition()) if err != nil { - return nil, status.Newf(codes.Internal, "cannot create new environment: %s", err.Error()).Err() + st := status.Newf(codes.Internal, "cannot create new environment: %s", utils.TruncateString(err.Error(), MAX_ERROR_LENGTH)) + ei := &pb.EnvironmentInfo{ + Id: id.String(), + CreatedWhen: time.Now().UnixMilli(), + State: "ERROR", // not really, but close + NumberOfFlps: 0, + } + st, _ = st.WithDetails(ei) + err = st.Err() + + return } newEnv, err := m.state.environments.Environment(id) if err != nil { - return nil, status.Newf(codes.Internal, "cannot get newly created environment: %s", err.Error()).Err() + st := status.Newf(codes.Internal, "cannot get newly created environment: %s", utils.TruncateString(err.Error(), MAX_ERROR_LENGTH)) + ei := &pb.EnvironmentInfo{ + Id: id.String(), + CreatedWhen: time.Now().UnixMilli(), + State: "ERROR", // not really, but close + NumberOfFlps: 0, + } + st, _ = st.WithDetails(ei) + err = st.Err() + + return } tasks := newEnv.Workflow().GetTasks() - r := &pb.NewEnvironmentReply{ - Environment: &pb.EnvironmentInfo{ - Id: newEnv.Id().String(), - CreatedWhen: newEnv.CreatedWhen().Format(time.RFC3339), - State: newEnv.CurrentState(), - Tasks: tasksToShortTaskInfos(tasks), - RootRole: newEnv.Workflow().GetName(), - CurrentRunNumber: newEnv.GetCurrentRunNumber(), - }, + var defaults, vars, userVars map[string]string + defaults, vars, userVars, err = newEnv.Workflow().ConsolidatedVarMaps() + if err != nil { + defaults = newEnv.GlobalDefaults.RawCopy() + vars = newEnv.GlobalVars.RawCopy() + userVars = newEnv.UserVars.RawCopy() + err = nil } - return r, nil + integratedServicesEnvsData := integration.PluginsInstance().GetEnvironmentsData([]uid.ID{id}) + isEnvData, ok := integratedServicesEnvsData[id] + if !ok { + isEnvData = make(map[string]string) + } + + ei := &pb.EnvironmentInfo{ + Id: newEnv.Id().String(), + CreatedWhen: newEnv.CreatedWhen().UnixMilli(), + State: newEnv.CurrentState(), + Tasks: tasksToShortTaskInfos(tasks, m.state.taskman), + RootRole: newEnv.Workflow().GetName(), + Description: newEnv.Description, + CurrentRunNumber: newEnv.GetCurrentRunNumber(), + Defaults: defaults, + Vars: vars, + UserVars: userVars, + NumberOfFlps: int32(len(newEnv.GetFLPs())), + NumberOfHosts: int32(len(newEnv.GetAllHosts())), + NumberOfTasks: int32(len(tasks)), + IncludedDetectors: newEnv.GetActiveDetectors().StringList(), + IntegratedServicesData: isEnvData, + CurrentTransition: newEnv.CurrentTransition(), + NumberOfActiveTasks: int32(len(tasks.Filtered(func(t *task.Task) bool { + parent := t.GetParentRole() + if parent == nil { + return false + } + if parentRole, ok := parent.(workflow.Role); ok { + return parentRole.GetStatus() == task.ACTIVE + } + return false + }))), + NumberOfInactiveTasks: int32(len(tasks.Filtered(func(t *task.Task) bool { + parent := t.GetParentRole() + if parent == nil { + return true // if task has no parent, we treat it as inactive + } + if parentRole, ok := parent.(workflow.Role); ok { + return parentRole.GetStatus() != task.ACTIVE + } + return false + }))), + } + reply = &pb.NewEnvironmentReply{ + Environment: ei, + Public: newEnv.Public, + } + defer setCurrentUnixMilli(&reply.Timestamp) + return } -func (m *RpcServer) GetEnvironment(cxt context.Context, req *pb.GetEnvironmentRequest) (*pb.GetEnvironmentReply, error) { +func (m *RpcServer) GetEnvironment(cxt context.Context, req *pb.GetEnvironmentRequest) (reply *pb.GetEnvironmentReply, err error) { + defer utils.TimeTrackFunction(time.Now(), log.WithPrefix("rpcserver")) m.logMethod() - m.state.RLock() - defer m.state.RUnlock() + defer m.logMethodHandled() if req == nil || len(req.Id) == 0 { return nil, status.New(codes.InvalidArgument, "received nil request").Err() } - env, err := m.state.environments.Environment(uuid.Parse(req.Id)) + var envId uid.ID + envId, err = uid.FromString(req.Id) + if err != nil { + return nil, status.New(codes.InvalidArgument, "received bad environment id").Err() + } + + var env *environment.Environment + env, err = m.state.environments.Environment(envId) if err != nil { return nil, status.Newf(codes.NotFound, "environment not found: %s", err.Error()).Err() } tasks := env.Workflow().GetTasks() - r := &pb.GetEnvironmentReply{ + var defaults, vars, userVars map[string]string + defaults, vars, userVars, err = env.Workflow().ConsolidatedVarMaps() + if err != nil { + defaults = env.GlobalDefaults.RawCopy() + vars = env.GlobalVars.RawCopy() + userVars = env.UserVars.RawCopy() + } + + integratedServicesEnvsData := integration.PluginsInstance().GetEnvironmentsData([]uid.ID{envId}) + isEnvData, ok := integratedServicesEnvsData[envId] + if !ok { + isEnvData = make(map[string]string) + } + reply = &pb.GetEnvironmentReply{ Environment: &pb.EnvironmentInfo{ - Id: env.Id().String(), - CreatedWhen: env.CreatedWhen().Format(time.RFC3339), - State: env.CurrentState(), - Tasks: tasksToShortTaskInfos(tasks), - RootRole: env.Workflow().GetName(), - CurrentRunNumber: env.GetCurrentRunNumber(), + Id: env.Id().String(), + CreatedWhen: env.CreatedWhen().UnixMilli(), + State: env.CurrentState(), + Tasks: tasksToShortTaskInfos(tasks, m.state.taskman), + RootRole: env.Workflow().GetName(), + Description: env.Description, + CurrentRunNumber: env.GetCurrentRunNumber(), + Defaults: defaults, + Vars: vars, + UserVars: userVars, + NumberOfFlps: int32(len(env.GetFLPs())), + NumberOfHosts: int32(len(env.GetAllHosts())), + NumberOfTasks: int32(len(tasks)), + IntegratedServicesData: isEnvData, + CurrentTransition: env.CurrentTransition(), + NumberOfActiveTasks: int32(len(tasks.Filtered(func(t *task.Task) bool { + parent := t.GetParentRole() + if parent == nil { + return false + } + if parentRole, ok := parent.(workflow.Role); ok { + return parentRole.GetStatus() == task.ACTIVE + } + return false + }))), + NumberOfInactiveTasks: int32(len(tasks.Filtered(func(t *task.Task) bool { + parent := t.GetParentRole() + if parent == nil { + return true // if task has no parent, we treat it as inactive + } + if parentRole, ok := parent.(workflow.Role); ok { + return parentRole.GetStatus() != task.ACTIVE + } + return false + }))), }, - Workflow: workflowToRoleTree(env.Workflow()), + Public: env.Public, } - return r, nil + defer setCurrentUnixMilli(&reply.Timestamp) + if req.GetShowWorkflowTree() { + reply.Workflow = workflowToRoleTree(env.Workflow()) + } + reply.Environment.IncludedDetectors = env.GetActiveDetectors().StringList() + + jsonISData, err := json.Marshal(isEnvData) + log.WithPrefix("rpcserver"). + WithField("method", "GetEnvironment"). + WithField("level", infologger.IL_Support). + WithField("intServPayloadSize", len(jsonISData)). + Tracef("returning payload incl. integrated services data for %d services", len(isEnvData)) + return } func (m *RpcServer) ControlEnvironment(cxt context.Context, req *pb.ControlEnvironmentRequest) (*pb.ControlEnvironmentReply, error) { + defer utils.TimeTrackFunction(time.Now(), log.WithPrefix("rpcserver")) m.logMethod() - m.state.RLock() - defer m.state.RUnlock() + defer m.logMethodHandled() if req == nil || len(req.Id) == 0 { return nil, status.New(codes.InvalidArgument, "received nil request").Err() } - env, err := m.state.environments.Environment(uuid.Parse(req.Id)) + envId, err := uid.FromString(req.Id) + if err != nil { + return nil, status.New(codes.InvalidArgument, "received bad environment id").Err() + } + + env, err := m.state.environments.Environment(envId) if err != nil { return nil, status.Newf(codes.NotFound, "environment not found: %s", err.Error()).Err() } + env.SetLastRequestUser(req.RequestUser) + trans := environment.MakeTransition(m.state.taskman, req.Type) if trans == nil { return nil, status.Newf(codes.InvalidArgument, "cannot prepare invalid transition %s", req.GetType().String()).Err() } + sot := time.Now() err = env.TryTransition(trans) + eot := time.Now() + td := eot.Sub(sot) + + if err != nil { + log.WithField("partition", env.Id()). + WithField("level", infologger.IL_Ops). + WithError(err). + Errorf("transition '%s' failed, transitioning into ERROR.", req.GetType().String()) + the.EventWriterWithTopic(topic.Environment).WriteEvent( + environment.NewEnvGoErrorEvent(env, fmt.Sprintf("transition %s failed: %v", req.GetType().String(), err)), + ) + err = env.TryTransition(environment.NewGoErrorTransition(m.state.taskman)) + if err != nil { + log.WithField("partition", env.Id()).Warnf("could not complete requested GO_ERROR transition, forcing move to ERROR: %s", err.Error()) + env.Sm.SetState("ERROR") + } + } reply := &pb.ControlEnvironmentReply{ - Id: env.Id().String(), - State: env.CurrentState(), - CurrentRunNumber: env.GetCurrentRunNumber(), + Id: env.Id().String(), + State: env.CurrentState(), + CurrentRunNumber: env.GetCurrentRunNumber(), + StartOfTransition: sot.UnixMilli(), + EndOfTransition: eot.UnixMilli(), + TransitionDuration: td.Milliseconds(), + } + defer setCurrentUnixMilli(&reply.Timestamp) + if err != nil { + return reply, status.Newf(codes.Aborted, err.Error()).Err() } - return reply, err + return reply, nil } func (*RpcServer) ModifyEnvironment(context.Context, *pb.ModifyEnvironmentRequest) (*pb.ModifyEnvironmentReply, error) { @@ -313,22 +680,64 @@ func (*RpcServer) ModifyEnvironment(context.Context, *pb.ModifyEnvironmentReques return nil, status.New(codes.Unimplemented, "not implemented").Err() } -func (m *RpcServer) DestroyEnvironment(cxt context.Context, req *pb.DestroyEnvironmentRequest) (*pb.DestroyEnvironmentReply, error) { +func (m *RpcServer) DestroyEnvironment(cxt context.Context, req *pb.DestroyEnvironmentRequest) (reply *pb.DestroyEnvironmentReply, err error) { + defer utils.TimeTrackFunction(time.Now(), log.WithPrefix("rpcserver")) m.logMethod() - m.state.RLock() - defer m.state.RUnlock() + defer m.logMethodHandled() + + reply = nil + defer func() { + if reply != nil { + reply.Timestamp = currentUnixMilli() + } + }() if req == nil || len(req.Id) == 0 { - return nil, status.New(codes.InvalidArgument, "received nil request").Err() + err = status.New(codes.InvalidArgument, "received nil request").Err() + return } - env, err := m.state.environments.Environment(uuid.Parse(req.Id)) + envId, err := uid.FromString(req.Id) if err != nil { - return nil, status.Newf(codes.NotFound, "environment not found: %s", err.Error()).Err() + err = status.New(codes.InvalidArgument, "received bad environment id").Err() + return + } + + env, err := m.state.environments.Environment(envId) + if err != nil { + err = status.Newf(codes.NotFound, "environment not found: %s", err.Error()).Err() + return + } + + env.SetLastRequestUser(req.RequestUser) + + // if Force immediately disband the environment (unlocking all tasks) and run the cleanup. + if req.Force { + reply, err = m.doTeardownAndCleanup(env, req.Force, req.KeepTasks) + return + } + + if req.AllowInRunningState && env.CurrentState() == "RUNNING" { + err = env.TryTransition(environment.NewStopActivityTransition(m.state.taskman)) + if err != nil { + log.WithError(err). + WithField("partition", env.Id().String()). + Warn("could not perform STOP transition for environment teardown, going to ERROR, then forcing") + the.EventWriterWithTopic(topic.Environment).WriteEvent( + environment.NewEnvGoErrorEvent(env, "STOP_ACTIVITY during environment destruction failed"), + ) + err = env.TryTransition(environment.NewGoErrorTransition(m.state.taskman)) + if err != nil { + environment.HandleFailedGoError(err, env) + } + reply, err = m.doTeardownAndCleanup(env, true /*force*/, false /*keepTasks*/) + return + } } - statesForDestroy := [...]string{"CONFIGURED", "STANDBY"} canDestroy := false + statesForDestroy := []string{"CONFIGURED", "DEPLOYED", "STANDBY"} + for _, v := range statesForDestroy { if env.CurrentState() == v { canDestroy = true @@ -337,136 +746,230 @@ func (m *RpcServer) DestroyEnvironment(cxt context.Context, req *pb.DestroyEnvir } if !canDestroy { - return nil, status.Newf(codes.FailedPrecondition, "cannot destroy environment in state %s", env.CurrentState()).Err() + log.WithField("partition", env.Id().String()). + Warnf("cannot teardown environment in state %s, forcing", env.CurrentState()) + reply, err = m.doTeardownAndCleanup(env, true /*force*/, false /*keepTasks*/) + return } - // This might transition to STANDBY if needed, of do nothing if we're already there + // This might transition to STANDBY if needed, or do nothing if we're already there if env.CurrentState() == "CONFIGURED" { - err = env.TryTransition(environment.MakeTransition(m.state.taskman, pb.ControlEnvironmentRequest_RESET)) + err = env.TryTransition(environment.NewResetTransition(m.state.taskman)) if err != nil { - return &pb.DestroyEnvironmentReply{}, status.New(codes.Internal, err.Error()).Err() + log.WithError(err). + WithField("partition", env.Id().String()). + Warnf("cannot teardown environment in state %s, going to ERROR, then forcing", env.CurrentState()) + the.EventWriterWithTopic(topic.Environment).WriteEvent( + environment.NewEnvGoErrorEvent(env, "RESET during environment destruction failed"), + ) + err = env.TryTransition(environment.NewGoErrorTransition(m.state.taskman)) + if err != nil { + environment.HandleFailedGoError(err, env) + } + reply, err = m.doTeardownAndCleanup(env, true /*force*/, false /*keepTasks*/) + return } } - err = m.state.environments.TeardownEnvironment(env.Id()) + reply, err = m.doTeardownAndCleanup(env, req.Force, req.KeepTasks) + return +} + +func (m *RpcServer) doTeardownAndCleanup(env *environment.Environment, force bool, keepTasks bool) (*pb.DestroyEnvironmentReply, error) { + log.WithField("partition", env.Id().String()). + WithField("level", infologger.IL_Ops). + Info("DESTROY starting") + + err := m.state.environments.TeardownEnvironment(env.Id(), force) if err != nil { + if !force { + // if the teardown failed, but we haven't tried a Force teardown yet, we retry + // a second time but with force + return m.doTeardownAndCleanup(env, true, keepTasks) + } return &pb.DestroyEnvironmentReply{}, status.New(codes.Internal, err.Error()).Err() } - if req.KeepTasks { // Tasks should stay running, so we're done + if keepTasks { // Tasks should stay running, so we're done return &pb.DestroyEnvironmentReply{}, nil } + // cleanup tasks tasksForEnv := env.Workflow().GetTasks().GetTaskIds() killed, running, err := m.doCleanupTasks(tasksForEnv) ctr := &pb.CleanupTasksReply{KilledTasks: killed, RunningTasks: running} if err != nil { - log.WithError(err).Error("task cleanup error") + log.WithError(err). + WithField("partition", env.Id().String()). + Error("task cleanup error") return &pb.DestroyEnvironmentReply{CleanupTasksReply: ctr}, status.New(codes.Internal, err.Error()).Err() } - return &pb.DestroyEnvironmentReply{CleanupTasksReply: ctr}, nil } +func (m *RpcServer) GetActiveDetectors(_ context.Context, _ *pb.Empty) (*pb.GetActiveDetectorsReply, error) { + defer utils.TimeTrackFunction(time.Now(), log.WithPrefix("rpcserver")) + m.logMethod() + defer m.logMethodHandled() + + r := &pb.GetActiveDetectorsReply{ + Detectors: make([]string, 0), + } + defer setCurrentUnixMilli(&r.Timestamp) + + detIds := m.state.environments.GetActiveDetectors() + r.Detectors = detIds.StringList() + + sort.Strings(r.Detectors) + return r, nil +} + +// return string lists of available, active and all detectors +func (m *RpcServer) listDetectors() ([]string, []string, []string, error) { + cs := the.ConfSvc() + allDetectors, err := cs.ListDetectors(true) + if err != nil { + return nil, nil, nil, err + } + activeDetectorsMap := m.state.environments.GetActiveDetectors() + availableDetectors := make([]string, 0) + for _, det := range allDetectors { + detId, err := system.IDString(det) + if err != nil { + continue + } + if _, contains := activeDetectorsMap[detId]; !contains { + availableDetectors = append(availableDetectors, det) + } + } + + return availableDetectors, activeDetectorsMap.StringList(), allDetectors, nil +} + +func (m *RpcServer) GetAvailableDetectors(_ context.Context, _ *pb.Empty) (*pb.GetAvailableDetectorsReply, error) { + defer utils.TimeTrackFunction(time.Now(), log.WithPrefix("rpcserver")) + m.logMethod() + defer m.logMethodHandled() + + if availableDetectors, _, _, err := m.listDetectors(); err != nil { + return nil, err + } else { + r := &pb.GetAvailableDetectorsReply{ + Detectors: availableDetectors, + Timestamp: currentUnixMilli(), + } + return r, nil + } +} + func (m *RpcServer) GetTasks(context.Context, *pb.GetTasksRequest) (*pb.GetTasksReply, error) { + defer utils.TimeTrackFunction(time.Now(), log.WithPrefix("rpcserver")) m.logMethod() - m.state.RLock() - defer m.state.RUnlock() + defer m.logMethodHandled() tasks := m.state.taskman.GetTasks() r := &pb.GetTasksReply{ - Tasks: tasksToShortTaskInfos(tasks), + Tasks: tasksToShortTaskInfos(tasks, m.state.taskman), + Timestamp: currentUnixMilli(), } return r, nil } func (m *RpcServer) GetTask(cxt context.Context, req *pb.GetTaskRequest) (*pb.GetTaskReply, error) { + defer utils.TimeTrackFunction(time.Now(), log.WithPrefix("rpcserver")) m.logMethod() - m.state.RLock() - defer m.state.RUnlock() + defer m.logMethodHandled() - task := m.state.taskman.GetTask(req.TaskId) - if task == nil { - return &pb.GetTaskReply{}, status.New(codes.NotFound, "task not found").Err() + requestedTask := m.state.taskman.GetTask(req.TaskId) + if requestedTask == nil { + return &pb.GetTaskReply{Timestamp: currentUnixMilli()}, status.New(codes.NotFound, "task not found").Err() } - taskClass := task.GetTaskClass() - commandInfo := task.BuildTaskCommand() + taskClass := requestedTask.GetTaskClass() + commandInfo := requestedTask.GetTaskCommandInfo() var outbound []channel.Outbound + var inbound []channel.Inbound taskPath := "" // TODO: probably not the nicest way to do this... the outbound assignments should be cached // in the Task - if task.IsLocked() { + if requestedTask.IsLocked() { type parentRole interface { CollectOutboundChannels() []channel.Outbound GetPath() string + CollectInboundChannels() []channel.Inbound } - parent, ok := task.GetParentRole().(parentRole) + parent, ok := requestedTask.GetParentRole().(parentRole) if ok { - outbound = parent.CollectOutboundChannels() + outbound = channel.MergeOutbound(parent.CollectOutboundChannels(), taskClass.Connect) taskPath = parent.GetPath() + inbound = channel.MergeInbound(parent.CollectInboundChannels(), taskClass.Bind) } } + if inbound == nil { + inbound = make([]channel.Inbound, len(taskClass.Bind)) + copy(inbound, taskClass.Bind) + } rep := &pb.GetTaskReply{ Task: &pb.TaskInfo{ - ShortInfo: taskToShortTaskInfo(task), - ClassInfo: &pb.TaskClassInfo{ - Name: task.GetClassName(), - ControlMode: taskClass.Control.Mode.String(), - }, - InboundChannels: inboundChannelsToPbChannels(taskClass.Bind), + ShortInfo: taskToShortTaskInfo(requestedTask, m.state.taskman), + InboundChannels: inboundChannelsToPbChannels(inbound), OutboundChannels: outboundChannelsToPbChannels(outbound), - CommandInfo: commandInfoToPbCommandInfo(commandInfo), - TaskPath: taskPath, - EnvId: task.GetEnvironmentId().String(), + CommandInfo: commandInfoToPbCommandInfo(commandInfo), + TaskPath: taskPath, + EnvId: requestedTask.GetEnvironmentId().String(), + Properties: requestedTask.GetProperties(), }, + Timestamp: currentUnixMilli(), } return rep, nil } func (m *RpcServer) CleanupTasks(cxt context.Context, req *pb.CleanupTasksRequest) (*pb.CleanupTasksReply, error) { + defer utils.TimeTrackFunction(time.Now(), log.WithPrefix("rpcserver")) m.logMethod() - m.state.Lock() - defer m.state.Unlock() + defer m.logMethodHandled() + idsToKill := req.GetTaskIds() killed, running, err := m.doCleanupTasks(idsToKill) if err != nil { log.WithError(err).Error("task cleanup error") - return &pb.CleanupTasksReply{KilledTasks: killed, RunningTasks: running}, status.New(codes.Internal, err.Error()).Err() + return &pb.CleanupTasksReply{KilledTasks: killed, RunningTasks: running, Timestamp: currentUnixMilli()}, status.New(codes.Internal, err.Error()).Err() } - return &pb.CleanupTasksReply{KilledTasks: killed, RunningTasks: running}, nil + return &pb.CleanupTasksReply{KilledTasks: killed, RunningTasks: running, Timestamp: currentUnixMilli()}, nil } func (m *RpcServer) doCleanupTasks(taskIds []string) (killedTaskInfos []*pb.ShortTaskInfo, runningTaskInfos []*pb.ShortTaskInfo, err error) { - var( - killedTasks, runningTasks task.Tasks - ) + var killedTasks, runningTasks task.Tasks if len(taskIds) == 0 { // by default we try to kill all, best effort killedTasks, runningTasks, err = m.state.taskman.Cleanup() } else { killedTasks, runningTasks, err = m.state.taskman.KillTasks(taskIds) } - killedTaskInfos = tasksToShortTaskInfos(killedTasks) - runningTaskInfos = tasksToShortTaskInfos(runningTasks) + killedTaskInfos = tasksToShortTaskInfos(killedTasks, m.state.taskman) + runningTaskInfos = tasksToShortTaskInfos(runningTasks, m.state.taskman) return } - func (m *RpcServer) GetRoles(cxt context.Context, req *pb.GetRolesRequest) (*pb.GetRolesReply, error) { + defer utils.TimeTrackFunction(time.Now(), log.WithPrefix("rpcserver")) m.logMethod() - m.state.RLock() - defer m.state.RUnlock() + defer m.logMethodHandled() if req == nil || len(req.EnvId) == 0 { return nil, status.New(codes.InvalidArgument, "received nil request").Err() } - env, err := m.state.environments.Environment(uuid.Parse(req.EnvId)) + envId, err := uid.FromString(req.EnvId) + if err != nil { + return nil, status.New(codes.InvalidArgument, "received bad environment id").Err() + } + + env, err := m.state.environments.Environment(envId) if err != nil { return nil, status.Newf(codes.NotFound, "environment not found: %s", err.Error()).Err() } @@ -477,39 +980,86 @@ func (m *RpcServer) GetRoles(cxt context.Context, req *pb.GetRolesRequest) (*pb. for i, rr := range resultRoles { roleInfos[i] = workflowToRoleTree(rr) } - return &pb.GetRolesReply{Roles: roleInfos}, nil + return &pb.GetRolesReply{Roles: roleInfos, Timestamp: currentUnixMilli()}, nil } func (m *RpcServer) GetWorkflowTemplates(cxt context.Context, req *pb.GetWorkflowTemplatesRequest) (*pb.GetWorkflowTemplatesReply, error) { + defer utils.TimeTrackFunction(time.Now(), log.WithPrefix("rpcserver")) m.logMethod() - m.state.RLock() - defer m.state.RUnlock() + defer m.logMethodHandled() + + if req == nil { + return nil, status.New(codes.InvalidArgument, "received nil request").Err() + } - workflowMap, numWorkflows, err := the.RepoManager().GetWorkflowTemplates() + workflowMap, numWorkflows, err := the.RepoManager().GetWorkflowTemplates(req.GetRepoPattern(), req.GetRevisionPattern(), req.GetAllBranches(), req.GetAllTags(), req.GetAllWorkflows()) if err != nil { - return nil, status.New(codes.FailedPrecondition, "cannot query available workflows").Err() + return nil, status.New(codes.InvalidArgument, "cannot query available workflows for "+req.GetRepoPattern()+"@"+req.GetRevisionPattern()+": "+ + err.Error()).Err() } + // Behaviour: in AliECS, template resolution follows specific rules of variable priority + // Apricot defaults are the most defaulty, so in the context of VarSpec, which entails as a minimum + // a declaration of type default in the WFT, no Apricot default can ever override a VarSpec default, + // so we don't even need to access Apricot defaults at all. These are only used if a variable is + // read in the WFT but never declared. + // Therefore the only thing from Apricot that can affect the VarSpec is a vars entry, and due to + // priority rules, Apricot vars can only override WFT/TT defaults, because WFT/TT vars have higher + // priority than Apricot vars. This way we ensure that the GUI receives any default values as + // expected with the correct precedence order with respect to Apricot entries. + // Here we make sure that for each VarSpec entry sourced from the WFT: + // * if declared as default + // * if the same declaration exists as Apricot var -> override + // * else no change + // * else no change + vars := the.ConfSvc().GetVars() + workflowTemplateInfos := make([]*pb.WorkflowTemplateInfo, numWorkflows) i := 0 - for repo, templates := range workflowMap { - for _, template := range templates { - workflowTemplateInfos[i] = &pb.WorkflowTemplateInfo{Repo: repo, Template: template} - i++ + for repo, revisions := range workflowMap { + for revision, templates := range revisions { + for _, template := range templates { + // First we take care of overriding any WFT VarSpec defaults with Apricot vars + varSpecMap := make(repos.VarSpecMap) + if template.VarInfo != nil { + _ = copier.Copy(&varSpecMap, template.VarInfo) + } + + for k, v := range varSpecMap { + if v.Source == varsource.WorkflowDefaults { // if this varSpec was declared as a default + if apricotValue, exists := vars[k]; exists { // and a corresponding Apricot var exists + v.DefaultValue = apricotValue + varSpecMap[k] = v + } + } + } + + // Finally, we build the protobuf response + workflowTemplateInfos[i] = &pb.WorkflowTemplateInfo{ + Repo: string(repo), + Revision: string(revision), + Template: template.Name, + VarSpecMap: VarSpecMapToPbVarSpecMap(varSpecMap), + Description: template.Description, + } + i++ + } } } - return &pb.GetWorkflowTemplatesReply{WorkflowTemplates: workflowTemplateInfos}, nil + return &pb.GetWorkflowTemplatesReply{WorkflowTemplates: workflowTemplateInfos, Timestamp: currentUnixMilli()}, nil } func (m *RpcServer) ListRepos(cxt context.Context, req *pb.ListReposRequest) (*pb.ListReposReply, error) { + defer utils.TimeTrackFunction(time.Now(), log.WithPrefix("rpcserver")) m.logMethod() + defer m.logMethodHandled() if req == nil { return nil, status.New(codes.InvalidArgument, "received nil request").Err() } - repoList := the.RepoManager().GetRepos() + repoList := the.RepoManager().GetAllRepos() repoInfos := make([]*pb.RepoInfo, len(repoList)) // Ensure alphabetical order of repos in output @@ -517,42 +1067,69 @@ func (m *RpcServer) ListRepos(cxt context.Context, req *pb.ListReposRequest) (*p for i, repoName := range keys { repo := repoList[repoName] - repoInfos[i] = &pb.RepoInfo{Name: repoName, Default: repo.Default} + var revisions []string + if req.GetRevisions { + revisions = repo.GetRevisions() + } else { + revisions = nil + } + repoInfos[i] = &pb.RepoInfo{Name: repoName, Default: repo.IsDefault(), DefaultRevision: repo.GetDefaultRevision(), Revisions: revisions} } - return &pb.ListReposReply{Repos: repoInfos}, nil + return &pb.ListReposReply{ + Repos: repoInfos, + GlobalDefaultRevision: viper.GetString("globalDefaultRevision"), + Timestamp: currentUnixMilli(), + }, nil } func (m *RpcServer) AddRepo(cxt context.Context, req *pb.AddRepoRequest) (*pb.AddRepoReply, error) { + defer utils.TimeTrackFunction(time.Now(), log.WithPrefix("rpcserver")) m.logMethod() + defer m.logMethodHandled() if req == nil { return nil, status.New(codes.InvalidArgument, "received nil request").Err() } - err := the.RepoManager().AddRepo(req.Name) - if err == nil { //new Repo -> refresh - return &pb.AddRepoReply{ErrorString: "" }, nil + newDefaultRevision, isGlobalDefault, err := the.RepoManager().AddRepo(req.Name, req.DefaultRevision) + if err != nil { + return nil, err + } + + var info string + if newDefaultRevision == req.DefaultRevision { + info = "The default revision for this repository has been set to \"" + newDefaultRevision + "\"." + } else if isGlobalDefault { + info = "The default revision for this repository has been set to \"" + newDefaultRevision + "\" (global default value)." } else { - return &pb.AddRepoReply{ErrorString: err.Error() }, nil + info = "The default revision for this repository has been set to \"" + newDefaultRevision + "\" (fallback value)." } + return &pb.AddRepoReply{NewDefaultRevision: newDefaultRevision, Info: info, Timestamp: currentUnixMilli()}, nil } func (m *RpcServer) RemoveRepo(cxt context.Context, req *pb.RemoveRepoRequest) (*pb.RemoveRepoReply, error) { + defer utils.TimeTrackFunction(time.Now(), log.WithPrefix("rpcserver")) m.logMethod() + defer m.logMethodHandled() if req == nil { return nil, status.New(codes.InvalidArgument, "received nil request").Err() } - ok, newDefaultRepo := the.RepoManager().RemoveRepoByIndex(int(req.Index)) + newDefaultRepo, err := the.RepoManager().RemoveRepoByIndex(int(req.Index)) + if err != nil { + return nil, err + } - return &pb.RemoveRepoReply{Ok: ok, NewDefaultRepo: newDefaultRepo}, nil + return &pb.RemoveRepoReply{NewDefaultRepo: newDefaultRepo, Timestamp: currentUnixMilli()}, nil } -func (m *RpcServer) RefreshRepos(cxt context.Context, req *pb.RefreshReposRequest) (*pb.RefreshReposReply, error) { +func (m *RpcServer) RefreshRepos(cxt context.Context, req *pb.RefreshReposRequest) (*pb.Empty, error) { + defer utils.TimeTrackFunction(time.Now(), log.WithPrefix("rpcserver")) m.logMethod() + defer m.logMethodHandled() if req == nil { return nil, status.New(codes.InvalidArgument, "received nil request").Err() @@ -565,14 +1142,16 @@ func (m *RpcServer) RefreshRepos(cxt context.Context, req *pb.RefreshReposReques err = the.RepoManager().RefreshRepoByIndex(int(req.Index)) } if err != nil { - return &pb.RefreshReposReply{ErrorString: err.Error()}, nil + return nil, err } - return &pb.RefreshReposReply{ErrorString: ""}, nil + return &pb.Empty{}, nil } -func (m *RpcServer) SetDefaultRepo(cxt context.Context, req *pb.SetDefaultRepoRequest) (*pb.SetDefaultRepoReply, error) { +func (m *RpcServer) SetDefaultRepo(cxt context.Context, req *pb.SetDefaultRepoRequest) (*pb.Empty, error) { + defer utils.TimeTrackFunction(time.Now(), log.WithPrefix("rpcserver")) m.logMethod() + defer m.logMethodHandled() if req == nil { return nil, status.New(codes.InvalidArgument, "received nil request").Err() @@ -580,8 +1159,92 @@ func (m *RpcServer) SetDefaultRepo(cxt context.Context, req *pb.SetDefaultRepoRe err := the.RepoManager().UpdateDefaultRepoByIndex(int(req.Index)) if err != nil { - return &pb.SetDefaultRepoReply{ErrorString: err.Error()}, nil - } else { - return &pb.SetDefaultRepoReply{ErrorString: ""}, nil + return nil, err } -} \ No newline at end of file + + return &pb.Empty{}, nil +} + +func (m *RpcServer) SetGlobalDefaultRevision(cxt context.Context, req *pb.SetGlobalDefaultRevisionRequest) (*pb.Empty, error) { + defer utils.TimeTrackFunction(time.Now(), log.WithPrefix("rpcserver")) + m.logMethod() + defer m.logMethodHandled() + + if req == nil { + return nil, status.New(codes.InvalidArgument, "received nil request").Err() + } + + err := the.RepoManager().SetGlobalDefaultRevision(req.Revision) + if err != nil { + return nil, err + } + + return &pb.Empty{}, nil +} + +func (m *RpcServer) SetRepoDefaultRevision(cxt context.Context, req *pb.SetRepoDefaultRevisionRequest) (*pb.SetRepoDefaultRevisionReply, error) { + defer utils.TimeTrackFunction(time.Now(), log.WithPrefix("rpcserver")) + m.logMethod() + defer m.logMethodHandled() + + if req == nil { + return nil, status.New(codes.InvalidArgument, "received nil request").Err() + } + + info, err := the.RepoManager().UpdateDefaultRevisionByIndex(int(req.Index), req.Revision) + if err != nil { + return &pb.SetRepoDefaultRevisionReply{Info: info, Timestamp: currentUnixMilli()}, nil // Info is filled with available revisions + // err can't be set here, otherwise the response will be empty + } + + return &pb.SetRepoDefaultRevisionReply{Info: info, Timestamp: currentUnixMilli()}, nil // Info is empty +} + +func (m *RpcServer) Subscribe(req *pb.SubscribeRequest, srv pb.Control_SubscribeServer) error { + defer utils.TimeTrackFunction(time.Now(), log.WithPrefix("rpcserver")) + m.logMethod() + defer m.logMethodHandled() + + for { + ch, chOk := m.envStreams.GetChannel(req.GetId()) + if !chOk { + continue + } + select { + case event, ok := <-ch: + if !ok { + m.envStreams.delete(req.GetId()) + return nil + } + err := srv.Send(event) + if err != nil { + log.WithError(err). + WithField("subscribe", req.GetId()). + Error(err.Error()) + } + } + } +} + +func (m *RpcServer) NewAutoEnvironment(cxt context.Context, request *pb.NewAutoEnvironmentRequest) (*pb.NewAutoEnvironmentReply, error) { + defer utils.TimeTrackFunction(time.Now(), log.WithPrefix("rpcserver")) + m.logMethod() + defer m.logMethodHandled() + + ch := make(chan *pb.Event) + m.envStreams.add(request.GetId(), ch) + sub := environment.SubscribeToStream(ch) + id := uid.New() + + inputUserVars := request.GetVars() + if len(inputUserVars) == 0 { + inputUserVars = make(map[string]string) + } + // we store the last known request user in the environment + lastRequestUserJ, _ := json.Marshal(request.RequestUser) + inputUserVars["last_request_user"] = string(lastRequestUserJ[:]) + + go m.state.environments.CreateAutoEnvironment(request.GetWorkflowTemplate(), inputUserVars, id, sub) + r := &pb.NewAutoEnvironmentReply{Timestamp: currentUnixMilli()} + return r, nil +} diff --git a/core/servertypes.go b/core/servertypes.go new file mode 100644 index 000000000..675f93934 --- /dev/null +++ b/core/servertypes.go @@ -0,0 +1,56 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2018-2020 CERN and copyright holders of ALICE O². + * Author: Teo Mrnjavac + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package core + +import ( + pb "github.com/AliceO2Group/Control/core/protos" +) + +type EnvironmentInfos []*pb.EnvironmentInfo + +func (infos EnvironmentInfos) Len() int { + return len(infos) +} +func (infos EnvironmentInfos) Less(i, j int) bool { + iv := infos[i] + jv := infos[j] + if iv == nil { + return true + } + if jv == nil { + return false + } + if iv.CreatedWhen < jv.CreatedWhen { + return true + } else { + return false + } +} +func (infos EnvironmentInfos) Swap(i, j int) { + var temp *pb.EnvironmentInfo + temp = infos[i] + infos[i] = infos[j] + infos[j] = temp +} diff --git a/core/serverutil.go b/core/serverutil.go index 9cd9d65e5..3dbd081ed 100644 --- a/core/serverutil.go +++ b/core/serverutil.go @@ -25,24 +25,30 @@ package core import ( + "fmt" + + "github.com/AliceO2Group/Control/core/repos" + "github.com/AliceO2Group/Control/common" - "github.com/AliceO2Group/Control/core/protos" + pb "github.com/AliceO2Group/Control/core/protos" "github.com/AliceO2Group/Control/core/task/channel" "github.com/AliceO2Group/Control/core/task" "github.com/AliceO2Group/Control/core/workflow" ) +const MESOS_AGENT_PORT = 5051 + func commandInfoToPbCommandInfo(c *common.TaskCommandInfo) (pci *pb.CommandInfo) { if c == nil { return } pci = &pb.CommandInfo{ - Env: c.GetEnv(), - Shell: c.GetShell(), - Value: c.GetValue(), + Env: c.GetEnv(), + Shell: c.GetShell(), + Value: c.GetValue(), Arguments: c.GetArguments(), - User: c.GetUser(), + User: c.GetUser(), } return } @@ -79,46 +85,69 @@ func outboundChannelsToPbChannels(chs []channel.Outbound) (pchs []*pb.ChannelInf func outboundChannelToPbChannel(ch channel.Outbound) (pch *pb.ChannelInfo) { pch = &pb.ChannelInfo{ - Name: ch.Name, - Type: ch.Type.String(), + Name: ch.Name, + Type: ch.Type.String(), Target: ch.Target, } return } -func taskToShortTaskInfo(t *task.Task) (sti *pb.ShortTaskInfo) { +func taskToShortTaskInfo(t *task.Task, taskman *task.Manager) (sti *pb.ShortTaskInfo) { if t == nil { return } + + var ( + slaveHost = t.GetHostname() + slavePort = MESOS_AGENT_PORT + workDir = "/var/mesos" + slaveId = t.GetAgentId() + frameworkId = taskman.GetFrameworkID() + executorId = t.GetExecutorId() + containerId = "latest" + ) + sandboxStdoutUri := fmt.Sprintf("http://%s:%d/files/download?path=%s/slaves/%s/frameworks/%s/executors/%s/runs/%s/stdout", + slaveHost, + slavePort, + workDir, + slaveId, + frameworkId, + executorId, + containerId) + sti = &pb.ShortTaskInfo{ - Name: t.GetName(), - Locked: t.IsLocked(), - TaskId: t.GetTaskId(), - Status: "UNKNOWN", - State: "UNKNOWN", + Name: t.GetName(), + Locked: t.IsLocked(), + Claimable: t.IsClaimable(), + TaskId: t.GetTaskId(), + Status: "UNKNOWN", + State: "UNKNOWN", ClassName: t.GetClassName(), DeploymentInfo: &pb.TaskDeploymentInfo{ - Hostname: t.GetHostname(), - AgentId: t.GetAgentId(), - OfferId: t.GetOfferId(), + Hostname: t.GetHostname(), + AgentId: t.GetAgentId(), + OfferId: t.GetOfferId(), ExecutorId: t.GetExecutorId(), }, + Pid: t.GetTaskPID(), + SandboxStdout: sandboxStdoutUri, } parentRole, ok := t.GetParentRole().(workflow.Role) if ok && parentRole != nil { sti.Status = parentRole.GetStatus().String() sti.State = parentRole.GetState().String() + sti.Critical = parentRole.IsCritical() } return } -func tasksToShortTaskInfos(tasks []*task.Task) (stis []*pb.ShortTaskInfo) { +func tasksToShortTaskInfos(tasks []*task.Task, taskman *task.Manager) (stis []*pb.ShortTaskInfo) { if tasks == nil { return } stis = make([]*pb.ShortTaskInfo, len(tasks)) for i, t := range tasks { - shortTaskInfo := taskToShortTaskInfo(t) + shortTaskInfo := taskToShortTaskInfo(t, taskman) stis[i] = shortTaskInfo } return @@ -144,13 +173,67 @@ func workflowToRoleTree(root workflow.Role) (ri *pb.RoleInfo) { for i, cr := range childRoles { childRoleInfos[i] = workflowToRoleTree(cr) } + consolidatedVarStack, _ := root.ConsolidatedVarStack() ri = &pb.RoleInfo{ - Name: root.GetName(), - Status: root.GetStatus().String(), - State: root.GetState().String(), - FullPath: root.GetPath(), - TaskIds: tasksToTaskIds(root.GetTasks()), - Roles: childRoleInfos, + Name: root.GetName(), + Status: root.GetStatus().String(), + State: root.GetState().String(), + FullPath: root.GetPath(), + TaskIds: tasksToTaskIds(root.GetTasks()), + Roles: childRoleInfos, + Defaults: root.GetDefaults().RawCopy(), + Vars: root.GetVars().RawCopy(), + UserVars: root.GetUserVars().RawCopy(), + ConsolidatedStack: consolidatedVarStack, } return } + +func VarSpecMapToPbVarSpecMap(varSpecMap map[string]repos.VarSpec) map[string]*pb.VarSpecMessage { + ret := make(map[string]*pb.VarSpecMessage) + var vsm *pb.VarSpecMessage + for k, v := range varSpecMap { + vsm = &pb.VarSpecMessage{ + DefaultValue: v.DefaultValue, + Type: convertVarTypeStringToEnum(v.VarType), + Label: v.Label, + Description: v.Description, + Widget: convertWidgetStringToEnum(v.Widget), + Panel: v.Panel, + AllowedValues: v.AllowedValues, + Index: v.Index, + VisibleIf: v.VisibleIf, + EnabledIf: v.EnabledIf, + Rows: v.Rows, + } + ret[k] = vsm + } + return ret +} + +func convertWidgetStringToEnum(hint string) pb.VarSpecMessage_UiWidget { + switch hint { + case "slider": + return 1 + case "listBox": + return 2 + case "dropDownBox": + return 3 + case "comboBox": + return 4 + case "radioButtonBox": + return 5 + case "checkBox": + return 6 + default: + return 0 // "editBox + } +} + +func convertVarTypeStringToEnum(varType string) pb.VarSpecMessage_Type { + msgType, ok := pb.VarSpecMessage_Type_value[varType] + if !ok { + msgType = int32(pb.VarSpecMessage_string) + } + return pb.VarSpecMessage_Type(msgType) +} diff --git a/core/signals.go b/core/signals.go new file mode 100644 index 000000000..5a4f67c5c --- /dev/null +++ b/core/signals.go @@ -0,0 +1,114 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2020 CERN and copyright holders of ALICE O². + * Author: Miltiadis Alexis + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package core + +import ( + "fmt" + "os" + "os/signal" + "syscall" + "time" + + "github.com/AliceO2Group/Control/core/environment" +) + +func signals(state *globalState) { + + // Create channel to receive unix signals + signal_chan := make(chan os.Signal, 1) + + //Register channel to receive SIGINT and SIGTERM signals + signal.Notify(signal_chan, + syscall.SIGINT, + syscall.SIGTERM) + + // Goroutine executes a blocking receive for signals + go func() { + s := <-signal_chan + manageKillSignals(state) + + // Mesos calls are async.Sleep for 2s to mark tasks as completed. + time.Sleep(2 * time.Second) + switch s { + case syscall.SIGINT: + os.Exit(130) // 128+2 + case syscall.SIGTERM: + os.Exit(143) // 128+15 + } + }() +} + +func manageKillSignals(state *globalState) { + // Get all enviroment ids + uids := state.environments.Ids() + + for _, uid := range uids { + // Get enviroment + env, err := state.environments.Environment(uid) + if err != nil { + log.WithPrefix("termination").WithError(err).Error(fmt.Sprintf("cannot find enviroment %s", uid.String())) + } + // This might transition to CONFIGURED if needed, of do nothing if we're already there + if env.CurrentState() == "RUNNING" { + + tasks := env.Workflow().GetTasks() + // we mark this specific task as ok to STOP + for _, t := range tasks { + t.SetSafeToStop(true) + } + // but then we ask the env whether *all* of them are + if env.IsSafeToStop() { + err = env.TryTransition(environment.NewStopActivityTransition(state.taskman)) + if err != nil { + log.WithPrefix("termination").WithError(err).Error(fmt.Sprintf("cannot transition enviroment %s from RUNNING to CONFIGURED", uid.String())) + } + } + } + + // This might transition to STANDBY if needed, or do nothing if we're already there + if env.CurrentState() == "CONFIGURED" { + err = env.TryTransition(environment.NewResetTransition(state.taskman)) + if err != nil { + log.WithPrefix("termination").WithError(err).Error(fmt.Sprintf("cannot transition enviroment %s from CONFIGURED to STANDBY", uid.String())) + } + } + + // Teardown Enviroment + err = state.environments.TeardownEnvironment(uid, false) + if err != nil { + log.WithPrefix("termination").WithError(err).Error(fmt.Sprintf("cannot teardown enviroment %s", uid.String())) + } + } + + // Perform cleanup + _, _, err := state.taskman.Cleanup() + if err != nil { + log.WithPrefix("termination").WithError(err).Error("can't perform cleanup") + } + + // Request Mesos to kill all tasks, regardless enviroment status and if a task is locked or not. + tasks := state.taskman.GetTasks() + state.taskman.EmergencyKillTasks(tasks) +} diff --git a/core/state.go b/core/state.go deleted file mode 100644 index 3df33d950..000000000 --- a/core/state.go +++ /dev/null @@ -1,158 +0,0 @@ -/* - * === This file is part of ALICE O² === - * - * Copyright 2017-2018 CERN and copyright holders of ALICE O². - * Author: Teo Mrnjavac - * - * Portions from examples in : - * Copyright 2013-2015, Mesosphere, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - *ù - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * In applying this license CERN does not waive the privileges and - * immunities granted to it by virtue of its status as an - * Intergovernmental Organization or submit itself to any jurisdiction. - */ - -package core - -import ( - "context" - "encoding/json" - "math/rand" - "sync" - "time" - - "github.com/AliceO2Group/Control/core/controlcommands" - "github.com/AliceO2Group/Control/core/environment" - "github.com/AliceO2Group/Control/core/task" - "github.com/AliceO2Group/Control/core/the" - "github.com/looplab/fsm" - "github.com/mesos/mesos-go/api/v1/lib" - "github.com/mesos/mesos-go/api/v1/lib/backoff" - "github.com/mesos/mesos-go/api/v1/lib/scheduler/calls" - "github.com/spf13/viper" -) - -func newInternalState(shutdown func()) (*internalState, error) { - metricsAPI := initMetrics() - executorInfo, err := prepareExecutorInfo( - viper.GetString("executor"), - viper.GetString("mesosExecutorImage"), - buildWantsExecutorResources(viper.GetFloat64("executorCPU"), - viper.GetFloat64("executorMemory")), - viper.GetDuration("mesosJobRestartDelay"), - metricsAPI, - ) - if err != nil { - return nil, err - } - creds, err := loadCredentials(viper.GetString("mesosCredentials.username"), - viper.GetString("mesosCredentials.password")) - if err != nil { - return nil, err - } - - if viper.GetBool("veryVerbose") { - cfgDump, err := the.ConfSvc().GetROSource().GetRecursive("o2/control") - if err != nil { - log.WithError(err).Fatal("cannot retrieve configuration") - return nil, err - } - cfgBytes, err := json.MarshalIndent(cfgDump,"", "\t") - if err != nil { - log.WithError(err).Fatal("cannot marshal configuration dump") - return nil, err - } - log.WithField("data", string(cfgBytes)).Debug("configuration dump") - } - - resourceOffersDone := make(chan task.DeploymentMap) - tasksToDeploy := make(chan task.Descriptors) - reviveOffersTrg := make(chan struct{}) - - state := &internalState{ - reviveTokens: backoff.BurstNotifier( - viper.GetInt("mesosReviveBurst"), - viper.GetDuration("mesosReviveWait"), - viper.GetDuration("mesosReviveWait"), - nil), - resourceOffersDone: resourceOffersDone, - tasksToDeploy: tasksToDeploy, - reviveOffersTrg: reviveOffersTrg, - wantsTaskResources: mesos.Resources{}, - executor: executorInfo, - metricsAPI: metricsAPI, - cli: buildHTTPSched(creds), - random: rand.New(rand.NewSource(time.Now().Unix())), - shutdown: shutdown, - environments: nil, - } - - state.servent = controlcommands.NewServent( - func(command controlcommands.MesosCommand, receiver controlcommands.MesosCommandTarget) error { - return SendCommand(context.TODO(), state, command, receiver) - }, - ) - state.commandqueue = controlcommands.NewCommandQueue(state.servent) - - taskman := task.NewManager( - resourceOffersDone, - tasksToDeploy, - reviveOffersTrg, - state.commandqueue, - func(task *task.Task) error { - return KillTask(context.TODO(), state, task.GetMesosCommandTarget()) - }, - ) - state.taskman = taskman - state.environments = environment.NewEnvManager(state.taskman) - state.commandqueue.Start() // FIXME: there should be 1 cq per env - - return state, nil -} - -type internalState struct { - sync.RWMutex - - // needs locking: - wantsTaskResources mesos.Resources - tasksLaunched int - tasksFinished int - err error - - // not used in multiple goroutines: - executor *mesos.ExecutorInfo - reviveTokens <-chan struct{} - resourceOffersDone chan task.DeploymentMap - tasksToDeploy chan task.Descriptors - reviveOffersTrg chan struct{} - random *rand.Rand - - // shouldn't change at runtime, so thread safe: - role string - cli calls.Caller - shutdown func() - - // uses prometheus counters, so thread safe - metricsAPI *metricsAPI - - // uses locks, so thread safe - sm *fsm.FSM - environments *environment.Manager - taskman *task.Manager - commandqueue *controlcommands.CommandQueue - servent *controlcommands.Servent -} - diff --git a/core/streamutil.go b/core/streamutil.go new file mode 100644 index 000000000..02cff8c86 --- /dev/null +++ b/core/streamutil.go @@ -0,0 +1,66 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2020-2022 CERN and copyright holders of ALICE O². + * Author: Miltiadis Alexis + * Teo Mrnjavac + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package core + +import ( + "sync" + + pb "github.com/AliceO2Group/Control/core/protos" +) + +// SafeStreamsMap is a safe map where the key is usually a +// subscriptionID received from the grpc call and as a value +// a channel where get events from the environment +// and we stream them to the grpc client. +type SafeStreamsMap struct { + mu sync.RWMutex + streams map[string]chan *pb.Event +} + +func (s *SafeStreamsMap) add(id string, ch chan *pb.Event) { + s.mu.Lock() + s.streams[id] = ch + s.mu.Unlock() +} + +func (s *SafeStreamsMap) delete(id string) { + s.mu.Lock() + delete(s.streams, id) + s.mu.Unlock() +} + +func (s *SafeStreamsMap) GetChannel(id string) (ch chan *pb.Event, ok bool) { + s.mu.RLock() + defer s.mu.RUnlock() + ch, ok = s.streams[id] + return +} + +func newSafeStreamsMap() SafeStreamsMap { + return SafeStreamsMap{ + streams: make(map[string]chan *pb.Event), + } +} diff --git a/core/task/agentcache.go b/core/task/agentcache.go index a248c0db8..57af48b67 100644 --- a/core/task/agentcache.go +++ b/core/task/agentcache.go @@ -25,17 +25,17 @@ package task import ( + "github.com/AliceO2Group/Control/core/task/constraint" "github.com/mesos/mesos-go/api/v1/lib" "sync" - "github.com/AliceO2Group/Control/core/task/constraint" ) type AgentCache struct { - mu sync.RWMutex + mu sync.RWMutex store map[mesos.AgentID]AgentCacheInfo } -type AgentCacheInfo struct{ +type AgentCacheInfo struct { AgentId mesos.AgentID Attributes constraint.Attributes Hostname string @@ -75,5 +75,7 @@ func (ac *AgentCache) Count() (count int) { if ac == nil || ac.store == nil { return 0 } + ac.mu.RLock() + defer ac.mu.RUnlock() return len(ac.store) -} \ No newline at end of file +} diff --git a/core/task/channel/channel.go b/core/task/channel/channel.go index 9b43a3180..d79496f6b 100644 --- a/core/task/channel/channel.go +++ b/core/task/channel/channel.go @@ -22,29 +22,50 @@ * Intergovernmental Organization or submit itself to any jurisdiction. */ +// Package channel provides functionality for managing task communication channels, +// including inbound and outbound channel configuration and message routing. package channel import ( + "errors" "strconv" "strings" - "errors" + + "github.com/AliceO2Group/Control/common/logger" + "github.com/sirupsen/logrus" ) -type channel struct { - Name string `yaml:"name"` - Type ChannelType `yaml:"type"` - SndBufSize int `yaml:"sndBufSize"` - RcvBufSize int `yaml:"rcvBufSize"` - RateLogging int `yaml:"rateLogging"` +var log = logger.New(logrus.StandardLogger(), "channel") + +type Channel struct { + Name string `yaml:"name"` + Type ChannelType `yaml:"type"` + SndBufSize int `yaml:"sndBufSize"` + RcvBufSize int `yaml:"rcvBufSize"` + RateLogging string `yaml:"rateLogging"` //actually an int but we allow templating + Transport TransportType `yaml:"transport"` //default: default + Target string `yaml:"target"` //default: empty + // allowed values for `target` field: + // outbound channel (mandatory!): ->outbound.go + // tcp://host:port + // ipc://named-pipe-name + // path.to.role:channel_name + // global_channel_name + // inbound channel (optional!): ->inbound.go + // tcp://host:port + // ipc://named-pipe-name + // -> automatic port assignment (pre-v0.24 behaviour) } -func (c *channel) UnmarshalYAML(unmarshal func(interface{}) error) (err error) { +func (c *Channel) UnmarshalYAML(unmarshal func(interface{}) error) (err error) { type _channel struct { - Name string `yaml:"name"` - Type ChannelType `yaml:"type"` - SndBufSize string `yaml:"sndBufSize"` - RcvBufSize string `yaml:"rcvBufSize"` - RateLogging string `yaml:"rateLogging"` + Name string `yaml:"name"` + Type ChannelType `yaml:"type"` + SndBufSize string `yaml:"sndBufSize"` + RcvBufSize string `yaml:"rcvBufSize"` + RateLogging string `yaml:"rateLogging"` + Transport TransportType `yaml:"transport"` + Target string `yaml:"target"` } aux := _channel{} err = unmarshal(&aux) @@ -63,6 +84,9 @@ func (c *channel) UnmarshalYAML(unmarshal func(interface{}) error) (err error) { if aux.RateLogging == "" { aux.RateLogging = "0" } + if len(aux.Transport) == 0 { + aux.Transport = DEFAULT + } c.SndBufSize, err = strconv.Atoi(aux.SndBufSize) if err != nil { @@ -72,10 +96,9 @@ func (c *channel) UnmarshalYAML(unmarshal func(interface{}) error) (err error) { if err != nil { return } - c.RateLogging, err = strconv.Atoi(aux.RateLogging) - if err != nil { - return - } + c.RateLogging = aux.RateLogging + c.Transport = aux.Transport + c.Target = strings.TrimSpace(aux.Target) return } @@ -84,6 +107,7 @@ func (c *channel) UnmarshalYAML(unmarshal func(interface{}) error) (err error) { // push/pull/pub/sub/spub/xsub/pair/req/rep/dealer/router // Do we need to support them all? type ChannelType string + const ( PUSH = ChannelType("push") PULL = ChannelType("pull") diff --git a/core/task/channel/endpoint.go b/core/task/channel/endpoint.go new file mode 100644 index 000000000..70b258fc1 --- /dev/null +++ b/core/task/channel/endpoint.go @@ -0,0 +1,164 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2020 CERN and copyright holders of ALICE O². + * Author: Teo Mrnjavac + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package channel + +import ( + "fmt" + "reflect" + "strings" + + "github.com/rs/xid" +) + +const IPC_PATH_FORMAT = "@o2ipc-%s" + +type BindMap map[string]Endpoint + +type Endpoint interface { + GetAddressFormat() AddressFormat + GetAddress() string + GetTransport() TransportType + ToTargetEndpoint(taskHostname string) Endpoint + ToBoundEndpoint() Endpoint +} + +func EndpointEquals(e Endpoint, f Endpoint) bool { + if reflect.TypeOf(e) != reflect.TypeOf(f) { + return false + } + switch v := e.(type) { + case TcpEndpoint: + w, ok := f.(TcpEndpoint) + if !ok { + return false + } + return v == w + case IpcEndpoint: + w, ok := f.(IpcEndpoint) + if !ok { + return false + } + return v == w + } + return false +} + +func NewTcpEndpoint(host string, port uint64, transport TransportType) Endpoint { + return TcpEndpoint{ + Host: host, + Port: port, + Transport: transport, + } +} + +func NewBoundTcpEndpoint(port uint64, transport TransportType) Endpoint { + return TcpEndpoint{ + Host: "*", + Port: port, + Transport: transport, + } +} + +func NewIpcEndpoint(path string, transport TransportType) Endpoint { + return IpcEndpoint{ + Path: strings.TrimPrefix(path, "ipc://"), + Transport: transport, + } +} + +func NewBoundIpcEndpoint(transport TransportType) Endpoint { + return IpcEndpoint{ + Path: fmt.Sprintf(IPC_PATH_FORMAT, xid.New().String()), + Transport: transport, + } +} + +type TcpEndpoint struct { + Host string + Port uint64 + Transport TransportType +} + +func (TcpEndpoint) GetAddressFormat() AddressFormat { + return TCP +} + +func (t TcpEndpoint) GetAddress() string { + if len(t.Host) == 0 || t.Host == "*" { + return fmt.Sprintf("tcp://*:%d", t.Port) + } + return fmt.Sprintf("tcp://%s:%d", t.Host, t.Port) +} + +func (t TcpEndpoint) GetTransport() TransportType { + return t.Transport +} + +func (t TcpEndpoint) ToTargetEndpoint(taskHostname string) Endpoint { + return TcpEndpoint{ + Host: taskHostname, + Port: t.Port, + Transport: t.Transport, + } +} + +func (t TcpEndpoint) ToBoundEndpoint() Endpoint { + return TcpEndpoint{ + Host: "*", + Port: t.Port, + Transport: t.Transport, + } +} + +type IpcEndpoint struct { + Path string + Transport TransportType +} + +func (IpcEndpoint) GetAddressFormat() AddressFormat { + return IPC +} + +func (t IpcEndpoint) GetAddress() string { + return fmt.Sprintf("ipc://%s", t.Path) +} + +func (t IpcEndpoint) GetTransport() TransportType { + return t.Transport +} + +func (t IpcEndpoint) ToTargetEndpoint(_ string) Endpoint { + return IpcEndpoint{ + Path: t.Path, + Transport: t.Transport, + } +} + +func (t IpcEndpoint) ToBoundEndpoint() Endpoint { + return IpcEndpoint{ + Path: t.Path, + Transport: t.Transport, + } +} diff --git a/core/task/channel/inbound.go b/core/task/channel/inbound.go index 13188f7d6..964678289 100644 --- a/core/task/channel/inbound.go +++ b/core/task/channel/inbound.go @@ -29,11 +29,68 @@ import ( "strconv" "strings" + "dario.cat/mergo" "github.com/AliceO2Group/Control/core/controlcommands" ) type Inbound struct { - channel + Channel + Global string `yaml:"global"` + Addressing AddressFormat `yaml:"addressing"` //default: tcp + // Addressing is ignored if Target not empty, because it means the WFT/TT is + // setting a static TCP or IPC bind address. +} + +func (inbound *Inbound) UnmarshalYAML(unmarshal func(interface{}) error) (err error) { + aux := struct { + Global string `yaml:"global"` + Addressing AddressFormat `yaml:"addressing"` //default: tcp + }{} + err = unmarshal(&aux) + if err != nil { + return + } + + ch := Channel{} + err = unmarshal(&ch) + if err != nil { + return + } + if len(aux.Addressing) == 0 { + aux.Addressing = TCP + } + + inbound.Addressing = aux.Addressing + inbound.Channel = ch + inbound.Global = aux.Global + return +} + +func (inbound Inbound) MarshalYAML() (interface{}, error) { + // Flatten Inbound to have Addressing and Channel elements on the same "level" + type _inbound struct { + Name string `yaml:"name"` + Global string `yaml:"global"` + Type ChannelType `yaml:"type"` + SndBufSize int `yaml:"sndBufSize,omitempty"` + RcvBufSize int `yaml:"rcvBufSize,omitempty"` + RateLogging string `yaml:"rateLogging,omitempty"` + Transport TransportType `yaml:"transport"` + Addressing AddressFormat `yaml:"addressing"` + } + + auxInbound := _inbound{ + Name: inbound.Channel.Name, + Global: inbound.Global, + Type: inbound.Channel.Type, + SndBufSize: inbound.Channel.SndBufSize, + RcvBufSize: inbound.Channel.RcvBufSize, + RateLogging: inbound.RateLogging, + Transport: inbound.Channel.Transport, + Addressing: inbound.Addressing, + } + + return auxInbound, nil } /* @@ -48,13 +105,45 @@ chans.data1.0.sndKernelSize = 0 chans.data1.0.transport = default [provided] chans.data1.0.type = push [provided] chans.data1.numSockets = 1 - */ +*/ + +func (inbound *Inbound) ToFMQMap(bindMap BindMap) (pm controlcommands.PropertyMap, err error) { + if inbound == nil { + err = fmt.Errorf("inbound channel object is nil") + return nil, err + } -func (inbound *Inbound) ToFMQMap(port uint64) (pm controlcommands.PropertyMap) { - return inbound.buildFMQMap(fmt.Sprintf("tcp://*:%d", port)) + var address string + var transport TransportType + // If an explicit target was provided, we use it. + // NO MATCHING WILL HAPPEN! + if strings.HasPrefix(inbound.Target, "tcp://") || + strings.HasPrefix(inbound.Target, "ipc://") { + address = inbound.Target + transport = inbound.Transport + } else if len(inbound.Target) != 0 { + err = fmt.Errorf("inbound channel target %s provided but invalid", inbound.Target) + log.WithError(err). + Error("bad inbound channel specification") + return nil, err + } else { + // We ignore any Target spec and proceed with inbound-outbound matching with + // the provided localBindMap. + endpoint, ok := bindMap[inbound.Name] + if !ok { + err = fmt.Errorf("endpoint for key %s not present in bindMap", inbound.Name) + log.WithError(err). + Warn("cannot match endpoint for channel") + return nil, err + } + address = endpoint.ToBoundEndpoint().GetAddress() + transport = endpoint.GetTransport() + } + + return inbound.buildFMQMap(address, transport), nil } -func (inbound *Inbound) buildFMQMap(address string) (pm controlcommands.PropertyMap) { +func (inbound *Inbound) buildFMQMap(address string, transport TransportType) (pm controlcommands.PropertyMap) { pm = make(controlcommands.PropertyMap) const chans = "chans" chName := inbound.Name @@ -63,19 +152,41 @@ func (inbound *Inbound) buildFMQMap(address string) (pm controlcommands.Property prefix := strings.Join([]string{chans, chName, "0"}, ".") chanProps := controlcommands.PropertyMap{ - "address": address, - "method": "bind", - "rateLogging": strconv.Itoa(inbound.RateLogging), - "rcvBufSize": strconv.Itoa(inbound.RcvBufSize), + "address": address, + "method": "bind", + "autoBind": "0", + "rateLogging": inbound.RateLogging, + "rcvBufSize": strconv.Itoa(inbound.RcvBufSize), "rcvKernelSize": "0", //NOTE: hardcoded - "sndBufSize": strconv.Itoa(inbound.SndBufSize), + "sndBufSize": strconv.Itoa(inbound.SndBufSize), "sndKernelSize": "0", //NOTE: hardcoded - "transport": "default", //NOTE: hardcoded - "type": inbound.Type.String(), + "transport": transport.String(), + "type": inbound.Type.String(), } for k, v := range chanProps { - pm[prefix + "." + k] = v + pm[prefix+"."+k] = v } return -} \ No newline at end of file +} + +func MergeInbound(hp, lp []Inbound) (channels []Inbound) { + channels = make([]Inbound, len(hp)) + copy(channels, hp) + + for _, v := range lp { + updated := false + for _, pCh := range channels { + if v.Name == pCh.Name { + _ = mergo.Merge(&pCh, v) + updated = true + break + } + } + if !updated { + channels = append(channels, v) + } + } + + return +} diff --git a/core/task/channel/outbound.go b/core/task/channel/outbound.go index 88a5426f9..040ca95df 100644 --- a/core/task/channel/outbound.go +++ b/core/task/channel/outbound.go @@ -26,44 +26,54 @@ package channel import ( "fmt" + "github.com/AliceO2Group/Control/common/logger/infologger" "strconv" "strings" + "dario.cat/mergo" "github.com/AliceO2Group/Control/core/controlcommands" + "github.com/sirupsen/logrus" ) -type Endpoint struct { - Host string - Port uint64 -} - -type BindMap map[string]Endpoint - type Outbound struct { - channel - Target string `json:"target" yaml:"target"` + Channel } func (outbound *Outbound) UnmarshalYAML(unmarshal func(interface{}) error) (err error) { - target := struct { - Target string `json:"target" yaml:"target"` - }{} - err = unmarshal(&target) - if err != nil { - return - } - - ch := channel{} + ch := Channel{} err = unmarshal(&ch) if err != nil { return } - outbound.Target = target.Target - outbound.channel = ch + outbound.Channel = ch return } +func (outbound Outbound) MarshalYAML() (interface{}, error) { + // Flatten Outbound to have Target and Channel elements on the same "level" + type _outbound struct { + Name string `yaml:"name"` + Type ChannelType `yaml:"type"` + SndBufSize int `yaml:"sndBufSize,omitempty"` + RcvBufSize int `yaml:"rcvBufSize,omitempty"` + RateLogging string `yaml:"rateLogging,omitempty"` + Transport TransportType `yaml:"transport"` + Target string `yaml:"target"` + } + + auxOutbound := _outbound{ + Name: outbound.Channel.Name, + Type: outbound.Channel.Type, + SndBufSize: outbound.Channel.SndBufSize, + RcvBufSize: outbound.Channel.RcvBufSize, + RateLogging: outbound.Channel.RateLogging, + Transport: outbound.Channel.Transport, + Target: outbound.Channel.Target, + } + + return auxOutbound, nil +} /* FairMQ outbound channel property map example: @@ -79,37 +89,47 @@ chans.data1.0.type = pull chans.data1.numSockets = 1 */ -func (outbound *Outbound) ToFMQMap(bindMap BindMap) (pm controlcommands.PropertyMap) { +func (outbound *Outbound) ToFMQMap(bindMap BindMap) (pm controlcommands.PropertyMap, err error) { if outbound == nil { + err = fmt.Errorf("outbound channel object is nil") return } var address string + var transport TransportType // If an explicit target was provided, we use it if strings.HasPrefix(outbound.Target, "tcp://") || strings.HasPrefix(outbound.Target, "ipc://") { address = outbound.Target + transport = outbound.Transport } else { + matched := false + // we don't need class.Bind data for this one, only task.bindPorts after resolving paths! for chPath, endpoint := range bindMap { + // FIXME: implement more sophisticated channel matching here if outbound.Target == chPath { - // We have a match, so we generate a resolved target address and break - address = fmt.Sprintf("tcp://%s:%d", endpoint.Host, endpoint.Port) + address = endpoint.GetAddress() + transport = endpoint.GetTransport() + matched = true break } } + if !matched { + err = fmt.Errorf("could not match target for outbound channel %s", outbound.Target) + } } if len(address) == 0 { return } - return outbound.buildFMQMap(address) + return outbound.buildFMQMap(address, transport), err } -func (outbound *Outbound) buildFMQMap(address string) (pm controlcommands.PropertyMap) { +func (outbound *Outbound) buildFMQMap(address string, transport TransportType) (pm controlcommands.PropertyMap) { pm = make(controlcommands.PropertyMap) const chans = "chans" chName := outbound.Name @@ -118,19 +138,54 @@ func (outbound *Outbound) buildFMQMap(address string) (pm controlcommands.Proper prefix := strings.Join([]string{chans, chName, "0"}, ".") chanProps := controlcommands.PropertyMap{ - "address": address, - "method": "connect", - "rateLogging": strconv.Itoa(outbound.RateLogging), - "rcvBufSize": strconv.Itoa(outbound.RcvBufSize), + "address": address, + "method": "connect", + "rateLogging": outbound.RateLogging, + "rcvBufSize": strconv.Itoa(outbound.RcvBufSize), "rcvKernelSize": "0", //NOTE: hardcoded - "sndBufSize": strconv.Itoa(outbound.SndBufSize), + "sndBufSize": strconv.Itoa(outbound.SndBufSize), "sndKernelSize": "0", //NOTE: hardcoded - "transport": "default", //NOTE: hardcoded - "type": outbound.Type.String(), + "transport": transport.String(), + "type": outbound.Type.String(), + } + + if (transport != outbound.Transport) && + (outbound.Transport != DEFAULT) { + log.WithFields(logrus.Fields{ + "address": address, + "oubound": outbound.Name, + "actualInboundTransport": transport, + "outboundTransport": outbound.Transport, + }). + Warn("channel transport mismatch, fix workflow template") } for k, v := range chanProps { - pm[prefix + "." + k] = v + pm[prefix+"."+k] = v + } + return +} + +func MergeOutbound(hp, lp []Outbound) (channels []Outbound) { + channels = make([]Outbound, len(hp)) + copy(channels, hp) + + for _, v := range lp { + updated := false + for _, pCh := range channels { + if v.Name == pCh.Name { + err := mergo.Merge(&pCh, v) + if err != nil { + log.WithField(infologger.Level, infologger.IL_Devel).Errorf("error merging outbound channel '%s': %v", v.Name, err) + } + updated = true + break + } + } + if !updated { + channels = append(channels, v) + } } + return -} \ No newline at end of file +} diff --git a/core/task/channel/transport.go b/core/task/channel/transport.go new file mode 100644 index 000000000..0ba83db22 --- /dev/null +++ b/core/task/channel/transport.go @@ -0,0 +1,84 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2020 CERN and copyright holders of ALICE O². + * Author: Teo Mrnjavac + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package channel + +import ( + "errors" + "strings" +) + +type TransportType string + +const ( + DEFAULT = TransportType("default") + ZEROMQ = TransportType("zeromq") + NANOMSG = TransportType("nanomsg") + SHMEM = TransportType("shmem") +) + +func (tr TransportType) String() string { + return string(tr) +} + +func (tr *TransportType) UnmarshalText(b []byte) error { + str := strings.ToLower(strings.Trim(string(b), `"`)) + + switch str { + case "": + *tr = DEFAULT + case DEFAULT.String(), ZEROMQ.String(), NANOMSG.String(), SHMEM.String(): + *tr = TransportType(str) + default: + return errors.New("invalid transport type: " + str) + } + + return nil +} + +type AddressFormat string + +const ( + TCP = AddressFormat("tcp") + IPC = AddressFormat("ipc") +) + +func (af AddressFormat) String() string { + return string(af) +} + +func (af *AddressFormat) UnmarshalText(b []byte) error { + str := strings.ToLower(strings.Trim(string(b), `"`)) + + switch str { + case "": + *af = TCP + case TCP.String(), IPC.String(): + *af = AddressFormat(str) + default: + return errors.New("invalid address format: " + str) + } + + return nil +} diff --git a/core/task/class.go b/core/task/class.go deleted file mode 100644 index 71fdf480c..000000000 --- a/core/task/class.go +++ /dev/null @@ -1,127 +0,0 @@ -/* - * === This file is part of ALICE O² === - * - * Copyright 2018 CERN and copyright holders of ALICE O². - * Author: Teo Mrnjavac - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * In applying this license CERN does not waive the privileges and - * immunities granted to it by virtue of its status as an - * Intergovernmental Organization or submit itself to any jurisdiction. - */ - -package task - -import ( - "fmt" - "github.com/AliceO2Group/Control/common" - "github.com/AliceO2Group/Control/common/controlmode" - "github.com/AliceO2Group/Control/core/controlcommands" - "github.com/AliceO2Group/Control/core/repos" - "github.com/AliceO2Group/Control/core/task/channel" - "github.com/AliceO2Group/Control/core/task/constraint" - "strconv" -) - -type TaskClass info - -// ↓ We need the roles tree to know *where* to run it and how to *configure* it, but -// the following information is enough to run the task even with no environment or -// role info. -type info struct { - Identifier taskClassIdentifier `yaml:"name"` - Control struct { - Mode controlmode.ControlMode `yaml:"mode"` - } `yaml:"control"` - Command *common.CommandInfo `yaml:"command"` - Wants ResourceWants `yaml:"wants"` - Bind []channel.Inbound `yaml:"bind"` - Properties controlcommands.PropertyMap `yaml:"properties"` - Constraints []constraint.Constraint `yaml:"constraints"` -} - -type taskClassIdentifier struct { - repo repos.Repo - Name string -} - -func (tcID taskClassIdentifier) String() string { - if tcID.repo.Revision != "" { - return fmt.Sprintf("%stasks/%s@%s", tcID.repo.GetIdentifier(), tcID.Name, tcID.repo.Revision) - } else { - return fmt.Sprintf("%stasks/%s@master", tcID.repo.GetIdentifier(), tcID.Name) - } -} - -func (tcID *taskClassIdentifier) UnmarshalYAML(unmarshal func(interface{}) error) (err error) { - err = unmarshal(&tcID.Name) - return -} - -type ResourceWants struct { - Cpu *float64 `yaml:"cpu"` - Memory *float64 `yaml:"memory"` - Ports Ranges `yaml:"ports"` -} - -func (rw *ResourceWants) UnmarshalYAML(unmarshal func(interface{}) error) (err error) { - type _resourceWants struct { - Cpu *string `yaml:"cpu"` - Memory *string `yaml:"memory"` - Ports *string `yaml:"ports"` - } - aux := _resourceWants{} - err = unmarshal(&aux) - if err != nil { - return - } - - if aux.Cpu != nil { - var cpuCount float64 - cpuCount, err = strconv.ParseFloat(*aux.Cpu, 64) - if err != nil { - return - } - rw.Cpu = &cpuCount - } - if aux.Memory != nil { - var memCount float64 - memCount, err = strconv.ParseFloat(*aux.Memory, 64) - if err != nil { - return - } - rw.Memory = &memCount - } - if aux.Ports != nil { - var ranges Ranges - ranges, err = parsePortRanges(*aux.Ports) - if err != nil { - return - } - rw.Ports = ranges - } - return -} - -func (this *info) Equals(other *info) (response bool) { - if this == nil || other == nil { - return false - } - response = this.Command.Equals(other.Command) && - *this.Wants.Cpu == *other.Wants.Cpu && - *this.Wants.Memory == *other.Wants.Memory && - this.Wants.Ports.Equals(other.Wants.Ports) - return -} diff --git a/core/task/classToTask.go b/core/task/classToTask.go new file mode 100644 index 000000000..40b41250f --- /dev/null +++ b/core/task/classToTask.go @@ -0,0 +1,39 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2020 CERN and copyright holders of ALICE O². + * Author: Ayaan Zaidi + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package task + +import "github.com/AliceO2Group/Control/core/task/taskclass" + +func ClassToTask(input *taskclass.Class, parent parentRole) *Task { + output := Task{ + parent: parent, + GetTaskClass: func() *taskclass.Class { + return input + }, + commandInfo: nil, + } + + return &output +} diff --git a/core/task/constraint/attributes.go b/core/task/constraint/attributes.go index 267d49351..d824bbefa 100644 --- a/core/task/constraint/attributes.go +++ b/core/task/constraint/attributes.go @@ -28,7 +28,8 @@ import ( "fmt" "strings" - "github.com/mesos/mesos-go/api/v1/lib" + "github.com/AliceO2Group/Control/common/utils" + mesos "github.com/mesos/mesos-go/api/v1/lib" ) type Attributes []mesos.Attribute @@ -67,11 +68,17 @@ func (attrs Attributes) Satisfy(cts Constraints) (ok bool) { } for _, constraint := range cts { - log.WithField("constraint", constraint.String()).Debug("processing constraint") switch constraint.Operator { case Equals: var value string if value, ok = attrs.Get(constraint.Attribute); ok { + if strings.Contains(value, ",") { + values := strings.Split(value, ",") + if utils.StringSliceContains(values, constraint.Value) { + ok = true + continue + } + } if value == constraint.Value { ok = true continue diff --git a/core/task/constraint/constraints.go b/core/task/constraint/constraints.go index 5b0cbc683..3cfac6ca6 100644 --- a/core/task/constraint/constraints.go +++ b/core/task/constraint/constraints.go @@ -36,22 +36,26 @@ import ( "github.com/sirupsen/logrus" ) -/* TODO: +/* + TODO: + type Operator func(attribute string, value string) bool func Equals(attribute string, value string) bool { -}*/ -var log = logger.New(logrus.StandardLogger(),"constraints") +} +*/ +var log = logger.New(logrus.StandardLogger(), "constraints") type Constraint struct { - Attribute string `yaml:"attribute"` - Value string `yaml:"value"` + Attribute string `yaml:"attribute"` + Value string `yaml:"value"` // TODO: unmarshal this ↓ - Operator Operator + Operator Operator } type Operator int8 + const ( Equals Operator = 0 ) diff --git a/core/task/coreevent.go b/core/task/coreevent.go new file mode 100644 index 000000000..6eb2efc61 --- /dev/null +++ b/core/task/coreevent.go @@ -0,0 +1,179 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2020 CERN and copyright holders of ALICE O². + * Author: Miltiadis Alexis + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package task + +import ( + "github.com/AliceO2Group/Control/common/utils/uid" + "github.com/AliceO2Group/Control/core/controlcommands" + "github.com/AliceO2Group/Control/core/task/taskop" + "github.com/mesos/mesos-go/api/v1/lib" +) + +type TaskmanMessage struct { + MessageType taskop.MessageType `json:"_messageType"` + + environmentMessage + transitionTasksMessage + updateTaskMessage + // killTasksMessage +} + +func newTaskmanMessage(mt taskop.MessageType) (t *TaskmanMessage) { + t = &TaskmanMessage{ + MessageType: mt, + } + return t +} + +func (tm *TaskmanMessage) GetMessageType() taskop.MessageType { + return tm.MessageType +} + +type environmentMessage struct { + envId uid.ID + tasks Tasks + descriptors Descriptors + runNumber string + errSt string +} + +func (em *environmentMessage) GetEnvironmentId() (envid uid.ID) { + if em == nil { + return + } + return em.envId +} + +func (em *environmentMessage) GetTasks() Tasks { + if em == nil { + return nil + } + return em.tasks +} + +func (em *environmentMessage) GetDescriptors() Descriptors { + if em == nil { + return nil + } + return em.descriptors +} + +func (em *environmentMessage) GetRunNumber() string { + if em == nil { + return "" + } + return em.runNumber +} + +func (em *environmentMessage) GetError() string { + if em == nil { + return "" + } + return em.errSt +} + +func NewEnvironmentMessage(mt taskop.MessageType, envId uid.ID, tasks Tasks, desc Descriptors) (t *TaskmanMessage) { + t = newTaskmanMessage(mt) + t.environmentMessage = environmentMessage{ + envId: envId, + tasks: tasks, + descriptors: desc, + } + return t +} + +type transitionTasksMessage struct { + src string + event string + dest string + commonArgs controlcommands.PropertyMap +} + +func (trm *transitionTasksMessage) GetSource() string { + if trm == nil { + return "" + } + return trm.src +} + +func (trm *transitionTasksMessage) GetEvent() string { + if trm == nil { + return "" + } + return trm.event +} + +func (trm *transitionTasksMessage) GetDestination() string { + if trm == nil { + return "" + } + return trm.dest +} + +func (trm *transitionTasksMessage) GetArguments() controlcommands.PropertyMap { + if trm == nil { + return nil + } + return trm.commonArgs +} + +func NewTransitionTaskMessage(tasks Tasks, src, transitionEvent, dest string, cargs controlcommands.PropertyMap, envID uid.ID) (t *TaskmanMessage) { + t = newTaskmanMessage(taskop.TransitionTasks) + t.transitionTasksMessage = transitionTasksMessage{ + src: src, + event: transitionEvent, + dest: dest, + commonArgs: cargs, + } + t.environmentMessage = environmentMessage{ + tasks: tasks, + envId: envID, + runNumber: cargs["runNumber"], + } + return t +} + +type updateTaskMessage struct { + taskId string + state string + status mesos.TaskStatus +} + +func NewTaskStatusMessage(mesosStatus mesos.TaskStatus) (t *TaskmanMessage) { + t = newTaskmanMessage(taskop.TaskStatusMessage) + t.updateTaskMessage = updateTaskMessage{ + status: mesosStatus, + } + return t +} + +func NewTaskStateMessage(taskid, state string) (t *TaskmanMessage) { + t = newTaskmanMessage(taskop.TaskStateMessage) + t.updateTaskMessage = updateTaskMessage{ + taskId: taskid, + state: state, + } + return t +} diff --git a/core/task/descriptor.go b/core/task/descriptor.go index b8384c84f..c59a031b6 100644 --- a/core/task/descriptor.go +++ b/core/task/descriptor.go @@ -24,33 +24,60 @@ package task -import "github.com/AliceO2Group/Control/core/task/constraint" +import ( + "fmt" + "strings" + + "github.com/AliceO2Group/Control/core/task/channel" + "github.com/AliceO2Group/Control/core/task/constraint" +) /* -1) Role has a method GenerateTaskConfiguration []Descriptor. +1) Role has a method GenerateTaskDescriptors []Descriptor. For most roles, this function simply consolidates the output of itself for children. - For TaskRoles, this function generates a single Descriptor from its own TaskClass + For TaskRoles, this function generates a single Descriptor from its own Class and parent role vars. Descriptor must also have a Ptr or path to the TaskRole that should become the new Task's parent. So in ConfigureTransition.do, we first get the []Descriptor with - wf.GenerateTaskConfiguration(). -2) Now that we have a list of config items for taks to run, we can port + wf.GenerateTaskDescriptors(). +2) Now that we have a list of config items for tasks to run, we can port task.Manager.AcquireRoles. This one internally uses match.go facilities such as matchRoles, which must return a *Task. This Task must then be added to the roster and a pointer to it set on the relevant TaskRole. - */ +*/ type Descriptor struct { - TaskRole parentRole - TaskClassName string - RoleConstraints constraint.Constraints + TaskRole parentRole + TaskClassName string + RoleConstraints constraint.Constraints + RoleConnect []channel.Outbound + RoleBind []channel.Inbound //CmdExtraEnv []string //CmdExtraArguments []string } type Descriptors []*Descriptor +func (ds Descriptors) String() string { + return strings.Join(ds.StringSlice(), ", ") +} + +func (ds Descriptors) StringSlice() []string { + if len(ds) == 0 { + return []string{} + } + lines := make([]string, len(ds)) + for i, desc := range ds { + if desc == nil || desc.TaskRole == nil { + lines[i] = "unknown" + continue + } + lines[i] = fmt.Sprintf("%s->%s", desc.TaskRole.GetPath(), desc.TaskClassName) + } + return lines +} + /* func (this Descriptor) Equals(other *Descriptor) (response bool) { return this.EqualsPtr(other) @@ -77,7 +104,7 @@ func (this *Descriptor) EqualsPtr(other *Descriptor) (response bool) { } } - response = this.info.Equals(&other.info) && + response = this.Class.Equals(&other.Class) && this.TaskClassName == other.TaskClassName return } @@ -171,7 +198,7 @@ func roleCfgFromConfiguration(name string, cfgMap configuration.Map) (roleCfg *D } roleCfg = &Descriptor{ - info: *ri, + Class: *ri, TaskClassName: roleClassS, CmdExtraEnv: cmdExtraEnvSlice, CmdExtraArguments: cmdExtraArgumentsSlice, @@ -180,7 +207,7 @@ func roleCfgFromConfiguration(name string, cfgMap configuration.Map) (roleCfg *D } -func roleInfoFromConfiguration(name string, cfgMap configuration.Map, mandatoryFields bool) (ri *info, err error) { +func roleInfoFromConfiguration(name string, cfgMap configuration.Map, mandatoryFields bool) (ri *Class, err error) { cfgErr := errors.New(fmt.Sprintf("bad configuration for roleClass %s", name)) // Get WantsCPU @@ -253,7 +280,7 @@ func roleInfoFromConfiguration(name string, cfgMap configuration.Map, mandatoryF Debug(cfgErr.Error()) } } else { - wantsPortsR, err = parsePortRanges(string(wantsPorts.Value())) + wantsPortsR, err = class.RangesFromExpression(string(wantsPorts.Value())) if err != nil { err = errors.New(fmt.Sprintf("%s: %s", cfgErr.Error(), err.Error())) @@ -293,7 +320,7 @@ func roleInfoFromConfiguration(name string, cfgMap configuration.Map, mandatoryF } } - ri = &info{ + ri = &Class{ Name: name, Command: cmdInfo, WantsCPU: wantsCPUF, @@ -323,8 +350,6 @@ func roleInfoFromConfiguration(name string, cfgMap configuration.Map, mandatoryF // ↑ this guy will initially only have 2 states: running and not running, or somesuch // but he doesn't belong here because all this should be immutable - - /*func NewO2Process() *ProcessDescriptor { return &ProcessDescriptor{ Fsm: fsm.NewFSM( @@ -337,4 +362,4 @@ func roleInfoFromConfiguration(name string, cfgMap configuration.Map, mandatoryF ), Deployed: false, } -}*/ \ No newline at end of file +}*/ diff --git a/core/task/errors.go b/core/task/errors.go index fb6b53880..c813b889c 100644 --- a/core/task/errors.go +++ b/core/task/errors.go @@ -27,7 +27,8 @@ package task import ( "fmt" "strings" - "github.com/pborman/uuid" + + "github.com/AliceO2Group/Control/common/utils/uid" ) type TaskError interface { @@ -43,6 +44,7 @@ type TasksError interface { type taskErrorBase struct { taskId string } + func (r taskErrorBase) GetTaskId() string { return r.taskId } @@ -50,6 +52,7 @@ func (r taskErrorBase) GetTaskId() string { type tasksErrorBase struct { taskIds []string } + func (r tasksErrorBase) GetTaskIds() []string { return r.taskIds } @@ -58,6 +61,7 @@ type GenericTaskError struct { taskErrorBase message string } + func (r GenericTaskError) Error() string { return fmt.Sprintf("task %s error: %s", r.taskId, r.message) } @@ -66,32 +70,83 @@ type GenericTasksError struct { tasksErrorBase message string } + func (r GenericTasksError) Error() string { return fmt.Sprintf("tasks [%s] error: %s", strings.Join(r.taskIds, ", "), r.message) } -type TasksDeploymentError tasksErrorBase +type TasksDeploymentError struct { + tasksErrorBase + failedNonCriticalDescriptors Descriptors + failedCriticalDescriptors Descriptors + undeployableNonCriticalDescriptors Descriptors + undeployableCriticalDescriptors Descriptors +} + func (r TasksDeploymentError) Error() string { - return fmt.Sprintf("deployment failed for tasks [%s]", strings.Join(r.taskIds, ", ")) + failedCritCount := len(r.failedCriticalDescriptors) + failedNonCritCount := len(r.failedNonCriticalDescriptors) + undepCritCount := len(r.undeployableCriticalDescriptors) + undepNonCritCount := len(r.undeployableNonCriticalDescriptors) + + failedCritStr := fmt.Sprintf("%d failed [%s]", failedCritCount, r.failedCriticalDescriptors.String()) + undepCritStr := fmt.Sprintf("%d undeployable [%s]", undepCritCount, r.undeployableCriticalDescriptors.String()) + + failedNonCritStr := fmt.Sprintf("%d failed [%s]", failedNonCritCount, r.failedNonCriticalDescriptors.String()) + undepNonCritStr := fmt.Sprintf("%d undeployable [%s]", undepNonCritCount, r.undeployableNonCriticalDescriptors.String()) + + if failedCritCount == 0 { + failedCritStr = "0 failed" + } + if undepCritCount == 0 { + undepCritStr = "0 undeployable" + } + + if failedNonCritCount == 0 { + failedNonCritStr = "0 failed" + } + if undepNonCritCount == 0 { + undepNonCritStr = "0 undeployable" + } + + critStr := fmt.Sprintf("%s, %s", failedCritStr, undepCritStr) + nonCritStr := fmt.Sprintf("%s, %s", failedNonCritStr, undepNonCritStr) + + totalCritCount := failedCritCount + undepCritCount + totalNonCritCount := failedNonCritCount + undepNonCritCount + + total := totalCritCount + totalNonCritCount + + return fmt.Sprintf( + "deployment failed for %d tasks: %d critical (%s), %d non-critical (%s)", + total, + totalCritCount, + critStr, + totalNonCritCount, + nonCritStr, + ) } type TaskAlreadyReleasedError taskErrorBase + func (r TaskAlreadyReleasedError) Error() string { return fmt.Sprintf("task %s already released", r.taskId) } type TaskNotFoundError taskErrorBase + func (r TaskNotFoundError) Error() string { return fmt.Sprintf("task %s not found", r.taskId) } type TaskLockedError struct { taskErrorBase - envId uuid.Array + envId uid.ID } + func (r TaskLockedError) Error() string { return fmt.Sprintf("task %s is locked by environment %s", r.taskId, r.envId) } -func (r TaskLockedError) EnvironmentId() uuid.Array { +func (r TaskLockedError) EnvironmentId() uid.ID { return r.envId -} \ No newline at end of file +} diff --git a/core/task/manager.go b/core/task/manager.go index df1d354c9..3a4e340e9 100644 --- a/core/task/manager.go +++ b/core/task/manager.go @@ -25,57 +25,144 @@ package task import ( + "context" "errors" "fmt" + "os" + "strings" + "sync" + "time" + + "github.com/AliceO2Group/Control/common/controlmode" + "github.com/AliceO2Group/Control/common/utils/safeacks" + + "github.com/AliceO2Group/Control/apricot" + "github.com/AliceO2Group/Control/common/event" + "github.com/AliceO2Group/Control/common/gera" + "github.com/AliceO2Group/Control/common/logger/infologger" "github.com/AliceO2Group/Control/common/utils" + "github.com/AliceO2Group/Control/common/utils/uid" "github.com/AliceO2Group/Control/core/repos" + "github.com/AliceO2Group/Control/core/task/sm" + "github.com/AliceO2Group/Control/core/task/taskclass" + "github.com/AliceO2Group/Control/core/task/taskop" "github.com/AliceO2Group/Control/core/the" + "github.com/AliceO2Group/Control/executor/executorutil" + "github.com/mesos/mesos-go/api/v1/lib/extras/store" "github.com/spf13/viper" - "gopkg.in/yaml.v2" - "io/ioutil" - - "strings" - "sync" + "gopkg.in/yaml.v3" + k8sclient "github.com/AliceO2Group/Control/control-operator/pkg/client" "github.com/AliceO2Group/Control/core/controlcommands" "github.com/AliceO2Group/Control/core/task/channel" "github.com/k0kubun/pp" - "github.com/mesos/mesos-go/api/v1/lib" - "github.com/pborman/uuid" + mesos "github.com/mesos/mesos-go/api/v1/lib" + "github.com/mesos/mesos-go/api/v1/lib/scheduler/calls" "github.com/sirupsen/logrus" ) type KillTaskFunc func(*Task) error +const ( + TARGET_SEPARATOR_RUNE = ':' + TARGET_SEPARATOR = ":" +) + +const TaskMan_QUEUE = 32768 + +type ResourceOffersOutcome struct { + deployed DeploymentMap + undeployed Descriptors + undeployable Descriptors +} + +type ResourceOffersDeploymentRequest struct { + tasksToDeploy Descriptors + envId uid.ID + outcomeCh chan ResourceOffersOutcome +} + type Manager struct { - AgentCache AgentCache + deployMu sync.Mutex + + ctx context.Context + fidStore store.Singleton + AgentCache AgentCache + MessageChannel chan *TaskmanMessage - mu sync.RWMutex - classes map[string]*TaskClass - roster Tasks + classes *taskclass.Classes + roster *roster - resourceOffersDone <-chan DeploymentMap - tasksToDeploy chan<- Descriptors - reviveOffersTrg chan struct{} - cq *controlcommands.CommandQueue + tasksToDeploy chan<- *ResourceOffersDeploymentRequest - doKillTask KillTaskFunc + reviveOffersTrg chan struct{} + cq *controlcommands.CommandQueue + + tasksLaunched int + tasksFinished int + + schedulerState *schedulerState + internalEventCh chan<- event.Event + ackKilledTasks *safeacks.SafeAcks + killTasksMu sync.Mutex // to avoid races when attempting to kill the same tasks in different goroutines + + k8sClient *k8sclient.Client + k8sEnvs *k8sEnvRegistry } -func NewManager(resourceOffersDone <-chan DeploymentMap, - tasksToDeploy chan<- Descriptors, - reviveOffersTrg chan struct{}, - cq *controlcommands.CommandQueue, - killTaskFunc KillTaskFunc) (taskman *Manager) { +func NewManager(shutdown func(), internalEventCh chan<- event.Event) (taskman *Manager, err error) { + // TODO(jdef) how to track/handle timeout errors that occur for SUBSCRIBE calls? we should + // probably tolerate X number of subsequent subscribe failures before bailing. we'll need + // to track the lastCallAttempted along with subsequentSubscribeTimeouts. + + // store.Singleton is a thread-safe abstraction to load and store and string, + // provided by mesos-go. + // We also make sure that a log message is printed with the FrameworkID. + fidStore := store.DecorateSingleton( + store.NewInMemorySingleton(), + store.DoSet().AndThen(func(_ store.Setter, v string, _ error) error { + // Store Mesos Framework ID to configuration. + err = the.ConfSvc().SetRuntimeEntry("aliecs", "mesos_fid", v) + if err != nil { + log.WithField("error", err).Error("cannot write to configuration") + } + log.WithField("frameworkId", v).Debug("frameworkId") + return nil + })) + + // Set Framework ID from the configuration + if fidValue, err := the.ConfSvc().GetRuntimeEntry("aliecs", "mesos_fid"); err == nil { + store.SetOrPanic(fidStore)(fidValue) + } + taskman = &Manager{ - classes: make(map[string]*TaskClass), - roster: make(Tasks, 0), - resourceOffersDone: resourceOffersDone, - tasksToDeploy: tasksToDeploy, - reviveOffersTrg: reviveOffersTrg, - cq: cq, - doKillTask: killTaskFunc, + classes: taskclass.NewClasses(), + roster: newRoster(), + internalEventCh: internalEventCh, + } + schedState, err := NewScheduler(taskman, fidStore, shutdown) + if err != nil { + return nil, err + } + taskman.schedulerState = schedState + taskman.cq = taskman.schedulerState.commandqueue + taskman.tasksToDeploy = taskman.schedulerState.tasksToDeploy + taskman.reviveOffersTrg = taskman.schedulerState.reviveOffersTrg + taskman.ackKilledTasks = safeacks.NewAcks() + taskman.k8sEnvs = newK8sEnvRegistry() + if viper.GetBool("enableKubernetes") { + k8sClient, k8sErr := newK8sClientFromViper() + if k8sErr != nil { + return nil, fmt.Errorf("failed to initialize K8s client: %w", k8sErr) + } + log.Info("K8s client initiated") + taskman.k8sClient = k8sClient + } else { + log.Info("enableKubernetes not set, K8s tasks disabled") } + + schedState.setupCli() + return } @@ -83,10 +170,13 @@ func NewManager(resourceOffersDone <-chan DeploymentMap, // constructed Task. // This function should only be called by the Mesos scheduler controller when // matching role requests with offers (matchRoles). -// The new role is not assigned to an environment and comes without a roleClass -// function, as those two are filled out later on by Manager.AcquireTasks. -func (m*Manager) NewTaskForMesosOffer(offer *mesos.Offer, descriptor *Descriptor, bindPorts map[string]uint64, executorId mesos.ExecutorID) (t *Task) { - newId := uuid.NewUUID().String() +func (m *Manager) newTaskForMesosOffer( + offer *mesos.Offer, + descriptor *Descriptor, + localBindMap channel.BindMap, + executorId mesos.ExecutorID, +) (t *Task) { + newId := uid.New().String() t = &Task{ name: fmt.Sprintf("%s#%s", descriptor.TaskClassName, newId), parent: descriptor.TaskRole, @@ -95,91 +185,159 @@ func (m*Manager) NewTaskForMesosOffer(offer *mesos.Offer, descriptor *Descriptor agentId: offer.AgentID.Value, offerId: offer.ID.Value, taskId: newId, + properties: gera.MakeMap[string, string]().Wrap(m.GetTaskClass(descriptor.TaskClassName).Properties), executorId: executorId.Value, GetTaskClass: nil, - bindPorts: nil, - state: STANDBY, + localBindMap: nil, + state: sm.STANDBY, status: INACTIVE, } - t.GetTaskClass = func() *TaskClass { + t.GetTaskClass = func() *taskclass.Class { return m.GetTaskClass(t.className) } - t.bindPorts = make(map[string]uint64) - for k, v := range bindPorts { - t.bindPorts[k] = v + t.localBindMap = make(channel.BindMap) + for k, v := range localBindMap { + t.localBindMap[k] = v } return } -func getTaskClassList(taskClassesRequired []string) (taskClassList []*TaskClass, err error) { +func getTaskClassList(taskClassesRequired []string) (taskClassList []*taskclass.Class, err error) { repoManager := the.RepoManager() var yamlData []byte - taskClassList = make([]*TaskClass, 0) + taskClassList = make([]*taskclass.Class, 0) for _, taskClass := range taskClassesRequired { taskClassString := strings.Split(taskClass, "@") taskClassFile := taskClassString[0] + ".yaml" - var repo *repos.Repo - repo, err = repos.NewRepo(strings.Split(taskClassFile, "tasks/")[0]) + var tempRepo repos.Repo + _, tempRepo, err = repos.NewRepo(strings.Split(taskClassFile, "tasks")[0], repoManager.GetDefaultRevision(), repoManager.GetReposPath()) if err != nil { return } - repo = repoManager.GetRepos()[repo.GetIdentifier()] //get repo pointer from repomanager - if repo == nil { //should never end up here + repo := repoManager.GetAllRepos()[tempRepo.GetIdentifier()] // get IRepo pointer from RepoManager + if repo == nil { // should never end up here return nil, errors.New("getTaskClassList: repo not found for " + taskClass) } - yamlData, err = ioutil.ReadFile(viper.GetString("repositoriesPath") + taskClassFile) + taskTemplatePath := repo.GetTaskTemplatePath(taskClassFile) + yamlData, err = os.ReadFile(taskTemplatePath) if err != nil { return nil, err } - taskClassStruct := TaskClass{} + taskClassStruct := taskclass.Class{} err = yaml.Unmarshal(yamlData, &taskClassStruct) if err != nil { - return nil, err + return nil, fmt.Errorf("task template load error (template=%s): %w", taskTemplatePath, err) + } + + var taskFilename, taskPath string + taskRev := strings.Split(taskClass, "@") + if len(taskRev) == 2 { + taskPath = taskRev[0] + } else { + taskPath = taskClass + } + taskInfo := strings.Split(taskPath, "/tasks/") + if len(taskInfo) == 1 { + taskFilename = taskInfo[0] + } else { + taskFilename = taskInfo[1] + } + + if taskClassStruct.Identifier.Name != taskFilename { + err = fmt.Errorf("the name of the task template file (%s) and the name of the task (%s) don't match", taskFilename, taskClassStruct.Identifier.Name) + return + } + + // DPL generates JIT tasks with control mode fairmq, those generated to run on Kubernetes + // are told apart by their name and switched to kubernetes_fairmq. + if strings.HasPrefix(taskClassStruct.Identifier.Name, utils.JitK8sPrefix) && + taskClassStruct.Control.Mode == controlmode.FAIRMQ { + taskClassStruct.Control.Mode = controlmode.KUBERNETES_FAIRMQ } - taskClassStruct.Identifier.repo = *repo + taskClassStruct.Identifier.RepoIdentifier = repo.GetIdentifier() + taskClassStruct.Identifier.Hash = repo.GetHash() taskClassList = append(taskClassList, &taskClassStruct) } return taskClassList, nil } +func (m *Manager) GetState() string { + return m.schedulerState.sm.Current() +} + +func (m *Manager) GetFrameworkID() string { + return m.schedulerState.GetFrameworkID() +} + func (m *Manager) removeInactiveClasses() { + _ = m.classes.Do(func(classMap *map[string]*taskclass.Class) error { + keys := make([]string, 0) + + taskClassCacheTTL := viper.GetDuration("taskClassCacheTTL") + + // push keys of classes that don't appear in roster any more into a slice + for taskClassIdentifier, class := range *classMap { + if class == nil { + // don't really know what to do with a valid TCI but nil class + continue + } + if time.Since(class.UpdatedTimestamp) < taskClassCacheTTL { + // class is still fresh, skip + continue + } + if len(m.roster.filteredForClass(taskClassIdentifier)) == 0 { + keys = append(keys, taskClassIdentifier) + } + } - for taskClassIdentifier := range m.classes{ - if len(m.roster.FilteredForClass(taskClassIdentifier)) == 0 { - delete(m.classes, taskClassIdentifier) + // and delete them + for _, k := range keys { + delete(*classMap, k) } - } + + return nil + }) return } -func (m *Manager) RemoveReposClasses(repoPath string) { //Currently unused - m.mu.Lock() - defer m.mu.Unlock() - +func (m *Manager) RemoveReposClasses(repoPath string) { // Currently unused utils.EnsureTrailingSlash(&repoPath) - for taskClassIdentifier := range m.classes{ - if strings.HasPrefix(taskClassIdentifier, repoPath) && - len(m.roster.FilteredForClass(taskClassIdentifier)) == 0 { - delete(m.classes, taskClassIdentifier) + _ = m.classes.Do(func(classMap *map[string]*taskclass.Class) error { + for taskClassIdentifier := range *classMap { + if strings.HasPrefix(taskClassIdentifier, repoPath) && + len(m.roster.filteredForClass(taskClassIdentifier)) == 0 { + delete(*classMap, taskClassIdentifier) + } } - } + return nil + }) return } func (m *Manager) RefreshClasses(taskClassesRequired []string) (err error) { - m.mu.Lock() - defer m.mu.Unlock() + log.WithField("taskClassesRequired", len(taskClassesRequired)). + Debug("waiting to refresh task classes") + defer utils.TimeTrackFunction(time.Now(), log.WithField("taskClassesRequired", len(taskClassesRequired))) + + m.deployMu.Lock() + defer m.deployMu.Unlock() + + log.WithField("taskClassesRequired", len(taskClassesRequired)). + Debug("cleaning up inactive task classes") m.removeInactiveClasses() - var taskClassList []*TaskClass + log.WithField("taskClassesRequired", len(taskClassesRequired)). + Debug("loading required task classes") + + var taskClassList []*taskclass.Class taskClassList, err = getTaskClassList(taskClassesRequired) if err != nil { return err @@ -188,88 +346,183 @@ func (m *Manager) RefreshClasses(taskClassesRequired []string) (err error) { for _, class := range taskClassList { taskClassIdentifier := class.Identifier.String() // If it already exists we update, otherwise we add the new class - if _, ok := m.classes[taskClassIdentifier]; ok { - *m.classes[taskClassIdentifier] = *class - } else { - m.classes[taskClassIdentifier] = class - } + m.classes.UpdateClass(taskClassIdentifier, class) } return } -func (m *Manager) AcquireTasks(envId uuid.Array, taskDescriptors Descriptors) (err error) { - m.mu.Lock() - defer m.mu.Unlock() +// prefix will be prepended before name of descriptor when printing name of each descriptor +func logDescriptors(prefix string, logFunc func(format string, args ...interface{}), descriptors Descriptors) { + for _, desc := range descriptors { + printname := fmt.Sprintf("%s->%s", desc.TaskRole.GetPath(), desc.TaskClassName) + logFunc("%s%s", prefix, printname) + } +} +func (m *Manager) acquireTasks(envId uid.ID, taskDescriptors Descriptors) (err error) { /* - Here's what's gonna happen: - 1) check if any tasks are already in Roster, whether they are already locked - in an environment, and whether their host has attributes that satisfy the - constraints - 1a) TODO: for each of them in Roster with matching attributes but wrong class, - mark for teardown and mesos-deployment - 1b) for each of them in Roster with matching attributes and class, mark for - takeover and reconfiguration - 3) TODO: teardown the tasks in tasksToTeardown - 4) start the tasks in tasksToRun - 5) ensure that all of them reach a CONFIGURED state + Here's what's gonna happen: + 1) check if any tasks are already in Roster, whether they are already locked + in an environment, and whether their host has attributes that satisfy the + constraints + 1a) TODO: for each of them in Roster with matching attributes but wrong class, + mark for teardown and mesos-deployment + 1b) for each of them in Roster with matching attributes and class, mark for + takeover and reconfiguration + 3) TODO: teardown the tasks in tasksToTeardown + 4) start the tasks in tasksToRun + 5) ensure that all of them reach a CONFIGURED state */ + // TODO: switch to this logger with envId everywhere + logWithId := log.WithField("partition", envId) + + claimableTasks := m.roster.filtered(func(task *Task) bool { + return task.IsClaimable() + }) + orphanTasks := m.roster.filtered(func(task *Task) bool { + return !task.IsLocked() && !task.IsClaimable() + }) + claimableTasksByHostname := claimableTasks.Grouped(func(task *Task) string { + return task.hostname + }) + orphanTasksByHostname := orphanTasks.Grouped(func(task *Task) string { + return task.hostname + }) + log.WithField("partition", envId.String()). + Info("beginning of task acquisition and deployment for environment") + + for hostname, tasks := range claimableTasksByHostname { + shortClassNames := make([]string, len(tasks)) + for i, t := range tasks { + if tc := t.GetTaskClass(); tc != nil { + shortClassNames[i] = tc.Identifier.Name + } else { + shortClassNames[i] = "unknown" + } + } + log.WithField("partition", envId.String()). + Infof("host %s has %d claimable tasks [%s]", hostname, len(tasks), strings.Join(shortClassNames, ", ")) + } + + for hostname, tasks := range orphanTasksByHostname { + shortClassNames := make([]string, len(tasks)) + for i, t := range tasks { + if tc := t.GetTaskClass(); tc != nil { + shortClassNames[i] = tc.Identifier.Name + } else { + shortClassNames[i] = "unknown" + } + } + log.WithField("partition", envId.String()). + Warnf("host %s has %d orphan tasks (cleanup needed) [%s]", hostname, len(tasks), strings.Join(shortClassNames, ", ")) + } + tasksToRun := make(Descriptors, 0) // TODO: also filter for tasks that satisfy attributes but are of wrong class, // to be torn down and mesosed. // tasksToTeardown := make([]TaskPtr, 0) tasksAlreadyRunning := make(DeploymentMap) - for _, descriptor := range taskDescriptors { - /* - For each descriptor we check m.AgentCache for agent attributes: - this allows us for each idle task in roster, get agentid and plug it in cache to get the - attributes for that host, and then we know whether - 1) a running task of the same class on that agent would qualify - 2) TODO: given enough resources obtained by freeing tasks, that agent would qualify - */ - - // Filter function that accepts a Task if - // a) it's !Locked - // b) has className matching Descriptor - // c) its Agent's Attributes satisfy the Descriptor's Constraints - taskMatches := func(taskPtr *Task) (ok bool) { - if taskPtr != nil { - if !taskPtr.IsLocked() && taskPtr.className == descriptor.TaskClassName { - agentInfo := m.AgentCache.Get(mesos.AgentID{Value: taskPtr.agentId}) - taskClass, classFound := m.classes[descriptor.TaskClassName] - if classFound && taskClass != nil && agentInfo != nil { - targetConstraints := descriptor. - RoleConstraints.MergeParent(taskClass.Constraints) - if agentInfo.Attributes.Satisfy(targetConstraints) { - ok = true - return + + if viper.GetBool("reuseUnlockedTasks") { + for _, descriptor := range taskDescriptors { + /* + For each descriptor we check m.AgentCache for agent attributes: + this allows us for each idle task in roster, get agentid and plug it in cache to get the + attributes for that host, and then we know whether + 1) a running task of the same class on that agent would qualify + 2) TODO: given enough resources obtained by freeing tasks, that agent would qualify + */ + + // Filter function that accepts a Task if + // a) it's !Locked + // b) has className matching Descriptor + // c) its Agent's Attributes satisfy the Descriptor's Constraints + taskMatches := func(taskPtr *Task) (ok bool) { + if taskPtr != nil { + if taskPtr.IsClaimable() && taskPtr.className == descriptor.TaskClassName { + agentInfo := m.AgentCache.Get(mesos.AgentID{Value: taskPtr.agentId}) + taskClass, classFound := m.classes.GetClass(descriptor.TaskClassName) + if classFound && taskClass != nil && agentInfo != nil { + targetConstraints := descriptor. + RoleConstraints.MergeParent(taskClass.Constraints) + if agentInfo.Attributes.Satisfy(targetConstraints) { + ok = true + return + } } } } + return } - return - } - runningTasksForThisDescriptor := m.roster.Filtered(taskMatches) - claimed := false - if len(runningTasksForThisDescriptor) > 0 { - // We have received a list of running, unlocked candidates to take over - // the current Descriptor. - // We now go through them, and if one of them is already claimed in - // tasksAlreadyRunning, we go to the next one until we exhaust our options. - for _, taskPtr := range runningTasksForThisDescriptor { - if _, ok := tasksAlreadyRunning[taskPtr]; ok { - continue - } else { // task not claimed yet, we do so now - tasksAlreadyRunning[taskPtr] = descriptor - claimed = true - break + runningTasksForThisDescriptor := m.roster.filtered(taskMatches) + claimed := false + if len(runningTasksForThisDescriptor) > 0 { + // We have received a list of running, unlocked candidates to take over + // the current Descriptor. + // We now go through them, and if one of them is already claimed in + // tasksAlreadyRunning, we go to the next one until we exhaust our options. + for _, taskPtr := range runningTasksForThisDescriptor { + if _, ok := tasksAlreadyRunning[taskPtr]; ok { + continue + } else { // task not claimed yet, we do so now + tasksAlreadyRunning[taskPtr] = descriptor + claimed = true + detector := "" + parent := taskPtr.GetParent() + if parent != nil { + detector, ok = parent.GetUserVars().Get("detector") + if !ok { + detector = "" + } + } + + log.WithField("partition", envId.String()). + WithField("detector", detector). + WithField("class", descriptor.TaskClassName). + WithField("task", taskPtr.className). + WithField("taskHost", taskPtr.hostname). + Warn("claiming existing unlocked task for incoming descriptor") + break + } } } + + if !claimed { + // no task can be claimed for the current descriptor, we add it to the list of + // new tasks to run + tasksToRun = append(tasksToRun, descriptor) + } } + } else { + // no task reuse at all, just run tasks for all descriptors + tasksToRun = append(tasksToRun, taskDescriptors...) + } + + // Split K8s descriptors out before the Mesos retry loop. + var k8sDescriptors Descriptors + { + mesosDescriptors := make(Descriptors, 0, len(tasksToRun)) + for _, desc := range tasksToRun { + class, ok := m.classes.GetClass(desc.TaskClassName) + if ok && class != nil && (class.Control.Mode == controlmode.KUBERNETES_DIRECT || + class.Control.Mode == controlmode.KUBERNETES_FAIRMQ) { + k8sDescriptors = append(k8sDescriptors, desc) + } else { + mesosDescriptors = append(mesosDescriptors, desc) + } + } + tasksToRun = mesosDescriptors + } - if !claimed { - tasksToRun = append(tasksToRun, descriptor) + var k8sDeployed DeploymentMap + var k8sDeployErr error + if len(k8sDescriptors) > 0 { + if m.k8sClient == nil { + k8sDeployErr = fmt.Errorf("K8s client not configured") + } else { + // TODO: this is synchronous call it would be worth it to make it async, so mesos loop doesn't have to wait for k8s + k8sDeployed, k8sDeployErr = m.deployKubernetesTasks(context.Background(), envId, k8sDescriptors) } } @@ -285,95 +538,238 @@ func (m *Manager) AcquireTasks(envId uuid.Array, taskDescriptors Descriptors) (e // - matched to a TaskPtr in tasksAlreadyRunning // - awaiting Task deployment in tasksToRun deploymentSuccess := true // hopefully - - deployedTasks := make(DeploymentMap) + undeployedDescriptors := make(Descriptors, 0) + undeployableDescriptors := make(Descriptors, 0) + undeployedNonCriticalDescriptors := make(Descriptors, 0) + undeployedCriticalDescriptors := make(Descriptors, 0) + undeployableNonCriticalDescriptors := make(Descriptors, 0) + undeployableCriticalDescriptors := make(Descriptors, 0) + + // we are retrying deployment multiple times in case of failure and we don't want + // to rerun tasks already running + tasksToRunThisAttempt := make(Descriptors, len(tasksToRun)) + copy(tasksToRunThisAttempt, tasksToRun) + + allDeployedTasks := make(DeploymentMap) + m.deployMu.Lock() if len(tasksToRun) > 0 { // Alright, so we have some descriptors whose requirements should be met with // new Tasks we're about to deploy here. // First we ask Mesos to revive offers and block until done, then upon receiving // the offers, we ask Mesos to run the required roles - if any. - m.reviveOffersTrg <- struct{}{} // signal scheduler to revive offers - <- m.reviveOffersTrg // we only continue when it's done - - m.tasksToDeploy <- tasksToRun // blocks until received - log.WithField("environmentId", envId). - Debug("scheduler should have received request to deploy") + DEPLOYMENT_ATTEMPTS_LOOP: + for attemptCount := 0; attemptCount < MAX_ATTEMPTS_PER_DEPLOY_REQUEST; attemptCount++ { + // We loop through the deployment attempts until we either succeed or + // reach the maximum number of attempts. In the happy case, we should only + // need to try once. A retry should only be necessary if the Mesos master + // has not been able to provide the resources we need in the offers round + // immediately after reviving. + // We also keep track of the number of attempts made in the request object + // so that the scheduler can decide whether to retry or not. + // The request object is used to pass the tasks to deploy and the outcome + // channel to the deployment routine. + + // reset all variable before try + deploymentSuccess = true + undeployedDescriptors = make(Descriptors, 0) + undeployableDescriptors = make(Descriptors, 0) + undeployedNonCriticalDescriptors = make(Descriptors, 0) + undeployedCriticalDescriptors = make(Descriptors, 0) + undeployableNonCriticalDescriptors = make(Descriptors, 0) + undeployableCriticalDescriptors = make(Descriptors, 0) + + outcomeCh := make(chan ResourceOffersOutcome) + m.tasksToDeploy <- &ResourceOffersDeploymentRequest{ + tasksToDeploy: tasksToRunThisAttempt, + envId: envId, + outcomeCh: outcomeCh, + } // buffered channel, does not block + + log.WithField("partition", envId). + Debugf("scheduler has been sent request to deploy %d tasks", len(tasksToRunThisAttempt)) + + timeReviveOffers := time.Now() + timeDeployMu := time.Now() + m.reviveOffersTrg <- struct{}{} // signal scheduler to revive offers + <-m.reviveOffersTrg // we only continue when it's done + utils.TimeTrack(timeReviveOffers, "acquireTasks: revive offers", + log.WithField("tasksToRunThisAttempt", len(tasksToRunThisAttempt)). + WithField("partition", envId)) + + roOutcome := <-outcomeCh // blocks until a verdict from resourceOffers comes in + + utils.TimeTrack(timeDeployMu, "acquireTasks: deployment critical section", + log.WithField("tasksToRunThisAttempt", len(tasksToRunThisAttempt)). + WithField("partition", envId)) + + deployedThisAttempt := roOutcome.deployed + undeployedDescriptors = roOutcome.undeployed + undeployableDescriptors = roOutcome.undeployable + + logWithId.WithField("tasks", deployedThisAttempt). + Debugf("resourceOffers is done, %d new tasks running", len(deployedThisAttempt)) + + for deployedTask, deployedDescriptor := range deployedThisAttempt { + allDeployedTasks[deployedTask] = deployedDescriptor + // add deployed tasks to roster, so updates can be distributed properly + m.roster.append(deployedTask) + } - // IDEA: a flps mesos-role assigned to all mesos agents on flp hosts, and then a static - // reservation for that mesos-role on behalf of our scheduler + if len(deployedThisAttempt) != len(tasksToRunThisAttempt) { + // ↑ Not all roles could be deployed. If some were critical, + // we cannot proceed with running this environment. Either way, + // we keep the roles running since they might be useful in the future. + logWithId.WithField("level", infologger.IL_Devel). + Errorf("environment deployment failure: %d tasks requested for deployment, but %d deployed", len(tasksToRunThisAttempt), len(deployedThisAttempt)) + + for _, desc := range undeployedDescriptors { + if desc.TaskRole.GetTaskTraits().Critical == true { + deploymentSuccess = false + undeployedCriticalDescriptors = append(undeployedCriticalDescriptors, desc) + } else { + undeployedNonCriticalDescriptors = append(undeployedNonCriticalDescriptors, desc) + } + } - deployedTasks = <- m.resourceOffersDone - log.WithField("tasks", deployedTasks). - Debug("resourceOffers is done, new tasks running") + for _, desc := range undeployableDescriptors { + if desc.TaskRole.GetTaskTraits().Critical == true { + deploymentSuccess = false + undeployableCriticalDescriptors = append(undeployableCriticalDescriptors, desc) + } else { + undeployableNonCriticalDescriptors = append(undeployableNonCriticalDescriptors, desc) + } + } + } - if len(deployedTasks) != len(tasksToRun) { - // ↑ Not all roles could be deployed. We cannot proceed - // with running this environment, but we keep the roles - // running since they might be useful in the future. - deploymentSuccess = false + if deploymentSuccess { + // ↑ means all the required critical processes are now running, + // and we are ready to update the envId + for taskPtr, descriptor := range deployedThisAttempt { + taskPtr.SetParent(descriptor.TaskRole) + // Ensure everything is filled out properly + if !taskPtr.IsLocked() { + log.WithField("task", taskPtr.taskId).Warning("cannot lock newly deployed task") + deploymentSuccess = false + } + } + break DEPLOYMENT_ATTEMPTS_LOOP + } + tasksToRunThisAttempt = make(Descriptors, 0, len(undeployableDescriptors)+len(undeployedDescriptors)) + tasksToRunThisAttempt = append(tasksToRunThisAttempt, undeployedDescriptors...) + tasksToRunThisAttempt = append(tasksToRunThisAttempt, undeployableDescriptors...) + + log.WithField("partition", envId). + WithField("level", infologger.IL_Devel). + Errorf("Deployment failed %d/%d attempts. Check messages in IL to figure out why. Retrying...", attemptCount+1, MAX_ATTEMPTS_PER_DEPLOY_REQUEST) + time.Sleep(time.Second * SLEEP_LENGTH_BETWEEN_PER_DEPLOY_REQUESTS) } } - if deploymentSuccess { - // ↑ means all the required processes are now running, and we - // are ready to update the envId - for taskPtr, descriptor := range deployedTasks { - taskPtr.parent = descriptor.TaskRole - // Ensure everything is filled out properly + // Merge K8s deployment results into unified tracking. + if k8sDeployErr != nil { + logWithId.WithError(k8sDeployErr).Error("K8s task deployment failed") + for _, desc := range k8sDescriptors { + if desc.TaskRole.GetTaskTraits().Critical { + deploymentSuccess = false + undeployableCriticalDescriptors = append(undeployableCriticalDescriptors, desc) + undeployableDescriptors = append(undeployableDescriptors, desc) + } else { + undeployableNonCriticalDescriptors = append(undeployableNonCriticalDescriptors, desc) + undeployableDescriptors = append(undeployableDescriptors, desc) + } + } + } else { + logWithId.Infof("succeeded to deploy %d kubernetes tasks", len(k8sDeployed)) + for taskPtr, desc := range k8sDeployed { + taskPtr.SetParent(desc.TaskRole) if !taskPtr.IsLocked() { - log.WithField("task", taskPtr.taskId).Warning("cannot lock newly deployed task") + log.WithField("task", taskPtr.taskId).Warning("K8s task not locked after deployment") deploymentSuccess = false } + allDeployedTasks[taskPtr] = desc + desc.TaskRole.UpdateStatus(ACTIVE) } } + + log.Infof("Succeeded to deploy %d/%d tasks", len(allDeployedTasks), len(tasksToRun)) + + { + logWithIdDev := logWithId.WithField("level", infologger.IL_Devel) + logDescriptors("critical task deployment impossible: ", logWithIdDev.Errorf, undeployableCriticalDescriptors) + logDescriptors("critical task deployment failure: ", logWithIdDev.Errorf, undeployedCriticalDescriptors) + logDescriptors("non-critical task deployment failure: ", logWithIdDev.Warningf, undeployedNonCriticalDescriptors) + + logDescriptors("non-critical task deployment impossible: ", logWithIdDev.Warningf, undeployableNonCriticalDescriptors) + } + + // After retries notify environment about failed critical tasks + for _, desc := range undeployableDescriptors { + if desc.TaskRole.GetTaskTraits().Critical == true { + desc.TaskRole.UpdateStatus(UNDEPLOYABLE) + } + } + + m.deployMu.Unlock() + if !deploymentSuccess { - // While all the required roles are running, for some reason we - // can't lock some of them, so we must roll back and keep them - // unlocked in the roster. var deployedTaskIds []string - for taskPtr, _ := range deployedTasks { - taskPtr.parent = nil + for taskPtr := range allDeployedTasks { + taskPtr.SetParent(nil) deployedTaskIds = append(deployedTaskIds, taskPtr.taskId) } - err = TasksDeploymentError{taskIds: deployedTaskIds} - } - // Finally, we write to the roster. Point of no return! - for taskPtr, _ := range deployedTasks { - m.roster = append(m.roster, taskPtr) - taskPtr.parent.SetTask(taskPtr) + err = TasksDeploymentError{ + tasksErrorBase: tasksErrorBase{taskIds: deployedTaskIds}, + failedNonCriticalDescriptors: undeployedNonCriticalDescriptors, + failedCriticalDescriptors: undeployedCriticalDescriptors, + undeployableNonCriticalDescriptors: undeployableNonCriticalDescriptors, + undeployableCriticalDescriptors: undeployableCriticalDescriptors, + } } + if deploymentSuccess { + for taskPtr := range allDeployedTasks { + taskPtr.GetParent().SetTask(taskPtr) + } for taskPtr, descriptor := range tasksAlreadyRunning { - taskPtr.parent = descriptor.TaskRole - taskPtr.parent.SetTask(taskPtr) + taskPtr.SetParent(descriptor.TaskRole) + taskPtr.GetParent().SetTask(taskPtr) } } return } -func (m *Manager) ReleaseTasks(envId uuid.Array, tasks Tasks) error { - m.mu.Lock() - defer m.mu.Unlock() +func (m *Manager) releaseTasks(envId uid.ID, tasks Tasks) { + if m.k8sClient != nil { + if err := m.killK8sEnvironment(context.Background(), envId); err != nil { + log.WithError(err).WithField("partition", envId.String()). + Warn("failed to delete K8s custom Environment object during release") + } + } + + taskReleaseErrors := make(map[string]error) + taskIdsReleased := make([]string, 0) for _, task := range tasks { err := m.releaseTask(envId, task) - if err != nil { + if err == nil { + taskIdsReleased = append(taskIdsReleased, task.GetTaskId()) + } else { switch err.(type) { case TaskAlreadyReleasedError: continue default: - return err + taskReleaseErrors[task.GetTaskId()] = err } } } - return nil + m.internalEventCh <- event.NewTasksReleasedEvent(envId, taskIdsReleased, taskReleaseErrors) } -func (m *Manager) releaseTask(envId uuid.Array, task *Task) error { +func (m *Manager) releaseTask(envId uid.ID, task *Task) error { if task == nil { return TaskNotFoundError{} } @@ -381,66 +777,167 @@ func (m *Manager) releaseTask(envId uuid.Array, task *Task) error { return TaskLockedError{taskErrorBase: taskErrorBase{taskId: task.name}, envId: envId} } - task.parent = nil + task.SetParent(nil) return nil } -func (m *Manager) ConfigureTasks(envId uuid.Array, tasks Tasks) error { - notify := make(chan controlcommands.MesosCommandResponse) - receivers, err := tasks.GetMesosCommandTargets() - if err != nil { - return err +func (m *Manager) configureTasks(envId uid.ID, tasks Tasks) error { + var k8sTasks Tasks + var mesosTasks Tasks + for _, t := range tasks { + if t.GetControlMode() == controlmode.KUBERNETES_DIRECT || + t.GetControlMode() == controlmode.KUBERNETES_FAIRMQ { + k8sTasks = append(k8sTasks, t) + } else { + mesosTasks = append(mesosTasks, t) + } } - m.mu.RLock() - // We generate a "bindMap" i.e. a map of the paths of registered inbound channels and their ports + // We fetch each task's local bindMap to generate a global bindMap for the whole Tasks slice, + // i.e. a map of the paths of registered inbound channels and their ports. + // We use the full task list (including K8s tasks) so that Mesos tasks can resolve + // connect targets that point at K8s task channels. bindMap := make(channel.BindMap) for _, task := range tasks { - taskPath := task.parent.GetPath() - for inbChName, port := range task.GetBindPorts() { - bindMap[taskPath + ":" + inbChName] = channel.Endpoint{Host: task.GetHostname(), Port: port} + if task.GetParent() == nil { // Crash reported here by Roberto 6/2022 + return fmt.Errorf("task %s on %s has nil parent, this should never happen", task.GetClassName(), task.GetHostname()) + } + taskPath := task.GetParentRolePath() + for inbChName, endpoint := range task.GetLocalBindMap() { + var bindMapKey string + if strings.HasPrefix(inbChName, "::") { // global channel alias + bindMapKey = inbChName + + // deduplication + if existingEndpoint, existsAlready := bindMap[bindMapKey]; existsAlready { + if channel.EndpointEquals(existingEndpoint, endpoint) { + // means somewhere something redefines the global channel alias, + // but the endpoint is the same so there's no problem + continue + } else { + return fmt.Errorf("workflow template contains illegal redefinition of global channel alias %s", bindMapKey) + } + } + } else { + bindMapKey = taskPath + TARGET_SEPARATOR + inbChName + } + bindMap[bindMapKey] = endpoint.ToTargetEndpoint(task.GetHostname()) } } log.WithFields(logrus.Fields{"bindMap": pp.Sprint(bindMap), "envId": envId.String()}). Debug("generated inbound bindMap for environment configuration") - src := STANDBY.String() - event := "CONFIGURE" - dest := CONFIGURED.String() + if len(k8sTasks) > 0 && m.k8sClient != nil { + if err := m.configureK8sTasks(context.Background(), envId, k8sTasks, bindMap); err != nil { + return err + } + } + + if len(mesosTasks) == 0 { + return nil + } + + notify := make(chan controlcommands.MesosCommandResponse) + receivers, err := mesosTasks.GetMesosCommandTargets() + if err != nil { + return err + } + + src := sm.STANDBY.String() + evt := "CONFIGURE" + dest := sm.CONFIGURED.String() args := make(controlcommands.PropertyMapsMap) - args, err = tasks.BuildPropertyMaps(bindMap) + args, err = mesosTasks.BuildPropertyMaps(bindMap) if err != nil { - m.mu.RUnlock() return err } - log.WithField("map", pp.Sprint(args)).Debug("pushing configuration to tasks") - m.mu.RUnlock() + log.WithField("map", pp.Sprint(args)). + WithField("partition", envId.String()). + Debug("pushing configuration to tasks") - cmd := controlcommands.NewMesosCommand_Transition(receivers, src, event, dest, args) - m.cq.Enqueue(cmd, notify) + cmd := controlcommands.NewMesosCommand_Transition(envId, receivers, src, evt, dest, args) + cmd.ResponseTimeout = 120 * time.Second // The default timeout is 90 seconds, but we need more time for the tasks to configure + _ = m.cq.Enqueue(cmd, notify) - response := <- notify + response := <-notify close(notify) if response == nil { - return errors.New("nil response") + return fmt.Errorf("no response from Mesos to CONFIGURE transition request within %ds timeout", int(cmd.ResponseTimeout.Seconds())) } - errText := response.Err().Error() - if len(strings.TrimSpace(errText)) != 0 { - return errors.New(response.Err().Error()) - } + if response.IsMultiResponse() { + taskCriticalErrors := make([]string, 0) + taskNonCriticalErrors := make([]string, 0) + i := 0 + for k, v := range response.Errors() { + task := m.GetTask(k.TaskId.Value) + var taskDescription string + if task != nil { + taskDescription = fmt.Sprintf("task '%s' on %s (id %s) failed with error: %s", task.GetParent().GetName(), task.GetHostname(), task.GetTaskId(), v.Error()) + } else { + taskDescription = fmt.Sprintf("unknown task (id %s) failed with error: %s", k.TaskId.Value, v.Error()) + } + if task != nil && task.GetTraits().Critical { + taskCriticalErrors = append(taskCriticalErrors, taskDescription) + } else if task != nil && task.parent != nil && task.parent.GetTaskTraits().Critical { + taskCriticalErrors = append(taskCriticalErrors, taskDescription) + } else { + taskNonCriticalErrors = append(taskNonCriticalErrors, taskDescription) + } + i++ + } - // FIXME: improve error handling ↑ + if len(taskNonCriticalErrors) > 0 { + log.WithField("partition", envId). + Warnf("CONFIGURE could not complete for non-critical tasks, errors: %s", strings.Join(taskNonCriticalErrors, "; ")) + } + if len(taskCriticalErrors) > 0 { + return fmt.Errorf("CONFIGURE could not complete for critical tasks, errors: %s", strings.Join(taskCriticalErrors, "; ")) + } + return nil + } else { + respError := response.Err() + if respError != nil { + errText := respError.Error() + if len(strings.TrimSpace(errText)) != 0 { + return errors.New(response.Err().Error()) + } + // FIXME: improve error handling ↑ + } + } return nil } -func (m *Manager) TransitionTasks(tasks Tasks, src string, event string, dest string, commonArgs controlcommands.PropertyMap) error { +func (m *Manager) transitionTasks(envId uid.ID, tasks Tasks, src string, event string, dest string, commonArgs controlcommands.PropertyMap) error { + var mesosTasks, k8sTasks Tasks + + for _, t := range tasks { + if t.GetControlMode() == controlmode.KUBERNETES_DIRECT || + t.GetControlMode() == controlmode.KUBERNETES_FAIRMQ { + k8sTasks = append(k8sTasks, t) + } else { + mesosTasks = append(mesosTasks, t) + } + } + + if len(tasks) != len(mesosTasks) { + log.WithField("partition", envId).Infof("Transitioning k8s environment from %s, to %s, event: %s", src, dest, event) + if err := m.pushArgsAndTransitionK8sEnvState(context.Background(), envId, k8sTasks, strings.ToLower(dest), commonArgs); err != nil { + return err + } + } + + if len(mesosTasks) == 0 { + return nil + } + + tasks = mesosTasks + notify := make(chan controlcommands.MesosCommandResponse) receivers, err := tasks.GetMesosCommandTargets() - if err != nil { return err } @@ -457,27 +954,108 @@ func (m *Manager) TransitionTasks(tasks Tasks, src string, event string, dest st } } - cmd := controlcommands.NewMesosCommand_Transition(receivers, src, event, dest, args) - m.cq.Enqueue(cmd, notify) + cmd := controlcommands.NewMesosCommand_Transition(envId, receivers, src, event, dest, args) + _ = m.cq.Enqueue(cmd, notify) + + response := <-notify + close(notify) + + if response == nil { + return errors.New("unknown MesosCommand error: nil response received") + } + + if response.IsMultiResponse() { + taskCriticalErrors := make([]string, 0) + taskNonCriticalErrors := make([]string, 0) + i := 0 + for k, v := range response.Errors() { + task := m.GetTask(k.TaskId.Value) + var taskDescription string + if task != nil { + tci := task.GetTaskCommandInfo() + tciValue := "unknown command" + if tci.Value != nil { + tciValue = *tci.Value + } + + taskDescription = fmt.Sprintf("task '%s' on %s (id %s) failed with error: %s", tciValue, task.GetHostname(), task.GetTaskId(), v.Error()) + + } else { + taskDescription = fmt.Sprintf("unknown task (id %s) failed with error: %s", k.TaskId.Value, v.Error()) + } + if task != nil && task.GetTraits().Critical { + taskCriticalErrors = append(taskCriticalErrors, taskDescription) + } else if task != nil && task.parent != nil && task.parent.GetTaskTraits().Critical { + taskCriticalErrors = append(taskCriticalErrors, taskDescription) + } else { + taskNonCriticalErrors = append(taskNonCriticalErrors, taskDescription) + } + i++ + } + + if len(taskNonCriticalErrors) > 0 { + log.WithField("partition", envId). + Warnf("%s could not complete for non-critical tasks, errors: %s", event, strings.Join(taskNonCriticalErrors, "; ")) + } + if len(taskCriticalErrors) > 0 { + return fmt.Errorf("%s could not complete for critical tasks, errors: %s", event, strings.Join(taskCriticalErrors, "; ")) + } + return nil + } else { + respError := response.Err() + if respError != nil { + errText := respError.Error() + if len(strings.TrimSpace(errText)) != 0 { + return errors.New(response.Err().Error()) + } + // FIXME: improve error handling ↑ + } + } + + return nil +} + +func (m *Manager) TriggerHooks(envId uid.ID, tasks Tasks) error { + if len(tasks) == 0 { + return nil + } + + notify := make(chan controlcommands.MesosCommandResponse) + receivers, err := tasks.GetMesosCommandTargets() + if err != nil { + return err + } + + cmd := controlcommands.NewMesosCommand_TriggerHook(envId, receivers) + err = m.cq.Enqueue(cmd, notify) + if err != nil { + return err + } - response := <- notify + response := <-notify close(notify) - errText := response.Err().Error() - if len(strings.TrimSpace(errText)) != 0 { - return errors.New(response.Err().Error()) + if response == nil { + return errors.New("unknown MesosCommand error: nil response received") } - // FIXME: improve error handling ↑ + respError := response.Err() + if respError != nil { + errText := respError.Error() + if len(strings.TrimSpace(errText)) != 0 { + return errors.New(response.Err().Error()) + } + // FIXME: improve error handling ↑ + } return nil } -func (m *Manager) GetTaskClass(name string) (b *TaskClass) { +func (m *Manager) GetTaskClass(name string) (b *taskclass.Class) { if m == nil { return } - b = m.classes[name] + b, _ = m.classes.GetClass(name) return } @@ -485,21 +1063,21 @@ func (m *Manager) TaskCount() int { if m == nil { return -1 } - return len(m.roster) + return len(m.roster.getTasks()) } func (m *Manager) GetTasks() Tasks { if m == nil { return nil } - return m.roster + return m.roster.getTasks() } func (m *Manager) GetTask(id string) *Task { if m == nil { return nil } - for _, t := range m.roster { + for _, t := range m.roster.getTasks() { if t.taskId == id { return t } @@ -507,59 +1085,118 @@ func (m *Manager) GetTask(id string) *Task { return nil } -func (m *Manager) UpdateTaskState(taskId string, state string) { - m.mu.RLock() - defer m.mu.RUnlock() - - taskPtr := m.roster.GetByTaskId(taskId) +func (m *Manager) updateTaskState(taskId string, state string) { + taskPtr := m.roster.getByTaskId(taskId) if taskPtr == nil { - log.WithField("taskId", taskId). - Warn("attempted state update of task not in roster") + if state != "DONE" { + log.WithField("taskId", taskId). + WithField("state", state). + WithField(infologger.Level, infologger.IL_Support). + Warn("attempted state update of task not in roster") + } return } - st := StateFromString(state) + st := sm.StateFromString(state) taskPtr.state = st - if taskPtr.parent != nil { - taskPtr.parent.UpdateState(st) + taskPtr.safeToStop = false + taskPtr.SendEvent(&event.TaskEvent{Name: taskPtr.GetName(), TaskID: taskId, State: state, Hostname: taskPtr.hostname, ClassName: taskPtr.GetClassName()}) + if parent := taskPtr.GetParent(); parent != nil { + parent.UpdateState(st) } } -func (m *Manager) UpdateTaskStatus(status *mesos.TaskStatus) { - m.mu.RLock() - defer m.mu.RUnlock() +func (m *Manager) updateTaskStatus(status *mesos.TaskStatus) { + aid := status.GetAgentID() + host := "" + detector := "" + var err error + + if aid != nil { + aci := m.AgentCache.Get(*aid) + if aci != nil { + host = aci.Hostname + detector, err = apricot.Instance().GetDetectorForHost(host) + if err != nil { + detector = "" + } + } + } + + envId := executorutil.GetEnvironmentIdFromLabelerType(status) taskId := status.GetTaskID().Value - taskPtr := m.roster.GetByTaskId(taskId) + taskPtr := m.roster.getByTaskId(taskId) if taskPtr == nil { - log.WithField("taskId", taskId). - Warn("attempted status update of task not in roster") + if status != nil && + status.GetState() != mesos.TASK_FINISHED && + status.GetState() != mesos.TASK_FAILED { + log.WithField("taskId", taskId). + WithField("mesosStatus", status.GetState().String()). + WithField("level", infologger.IL_Devel). + WithField("status", status.GetState().String()). + WithField("reason", status.GetReason().String()). + WithField("detector", detector). + WithField("partition", envId.String()). + Warn("attempted status update of task not in roster") + } + err := m.ackKilledTasks.TrySendAck(taskId) + if err != nil { + log.WithField("taskId", taskId). + WithField("mesosStatus", status.GetState().String()). + WithField("level", infologger.IL_Devel). + WithField("status", status.GetState().String()). + WithField("reason", status.GetReason().String()). + WithField("detector", detector). + WithField("partition", envId.String()). + Warnf("%s", err) + } return } switch st := status.GetState(); st { case mesos.TASK_RUNNING: + cmdInfo := taskPtr.GetTaskCommandInfo() + var ( + cmdStr string + controlMode string + ) + if cmdInfo != nil { + cmdStr = cmdInfo.GetValue() + controlMode = cmdInfo.ControlMode.String() + } log.WithField("taskId", taskId). WithField("name", taskPtr.GetName()). - Debug("task running") + WithField("level", infologger.IL_Devel). + WithField("cmd", cmdStr). + WithField("controlmode", controlMode). + WithField("detector", detector). + WithField("partition", envId.String()). + Debug("task active (received TASK_RUNNING event from executor)") taskPtr.status = ACTIVE - if taskPtr.parent != nil { - taskPtr.parent.UpdateStatus(ACTIVE) + if parent := taskPtr.GetParent(); parent != nil { + parent.UpdateStatus(ACTIVE) + } + if status.GetAgentID() != nil { + taskPtr.agentId = status.GetAgentID().GetValue() } - case mesos.TASK_DROPPED, mesos.TASK_LOST, mesos.TASK_KILLED, mesos.TASK_FAILED, mesos.TASK_ERROR: + if status.GetExecutorID() != nil { + taskPtr.executorId = status.GetExecutorID().GetValue() + } + + case mesos.TASK_DROPPED, mesos.TASK_LOST, mesos.TASK_KILLED, mesos.TASK_FAILED, mesos.TASK_ERROR, mesos.TASK_FINISHED: + taskPtr.status = INACTIVE - if taskPtr.parent != nil { - taskPtr.parent.UpdateStatus(INACTIVE) + if parent := taskPtr.GetParent(); parent != nil { + parent.UpdateStatus(INACTIVE) } } + taskPtr.SendEvent(&event.TaskEvent{Name: taskPtr.GetName(), TaskID: taskId, Status: taskPtr.status.String(), Hostname: taskPtr.hostname, ClassName: taskPtr.GetClassName()}) } // Kill all tasks outside an environment (all unlocked tasks) func (m *Manager) Cleanup() (killed Tasks, running Tasks, err error) { - m.mu.Lock() - defer m.mu.Unlock() - - toKill := m.roster.Filtered(func(t *Task) bool { + toKill := m.roster.filtered(func(t *Task) bool { return !t.IsLocked() }) @@ -570,11 +1207,8 @@ func (m *Manager) Cleanup() (killed Tasks, running Tasks, err error) { // Kill a specific list of tasks. // If the task list includes locked tasks, TaskNotFoundError is returned. func (m *Manager) KillTasks(taskIds []string) (killed Tasks, running Tasks, err error) { - m.mu.Lock() - defer m.mu.Unlock() - - toKill := m.roster.Filtered(func(t *Task) bool { - if t.IsLocked() { + taskCanBeKilledFilter := func(t *Task) bool { + if t.IsLocked() || m.ackKilledTasks.ExpectsAck(t.taskId) { return false } for _, id := range taskIds { @@ -583,17 +1217,56 @@ func (m *Manager) KillTasks(taskIds []string) (killed Tasks, running Tasks, err } } return false - }) + } + + if !m.killTasksMu.TryLock() { + log.WithField("level", infologger.IL_Support).Warnf("Scheduling killing tasks was delayed until another goroutine finishes doing so") + m.killTasksMu.Lock() + log.WithField("level", infologger.IL_Support).Infof("Scheduling killing tasks is resumed") + } + // TODO: use grouping instead of 2 passes of filtering for performance + toKill := m.roster.filtered(taskCanBeKilledFilter) if len(toKill) < len(taskIds) { - err = TaskNotFoundError{} - return + unkillable := m.roster.filtered(func(t *Task) bool { return !taskCanBeKilledFilter(t) }) + log.WithField("taskIds", strings.Join(unkillable.GetTaskIds(), ", ")). + Debugf("some tasks cannot be physically killed (already dead or being killed in another goroutine?), will instead only be removed from roster") + } + + for _, id := range toKill.GetTaskIds() { + err := m.ackKilledTasks.RegisterAck(id) + if err != nil { + log.WithField("level", infologger.IL_Devel).Warnf("failed to register ack for task '%s': %s", id, err) + } } killed, running, err = m.doKillTasks(toKill) + m.killTasksMu.Unlock() + + for _, id := range killed.GetTaskIds() { + ok := m.ackKilledTasks.TryReceiveAck(id) + if !ok { + log.WithField("level", infologger.IL_Devel).Warnf("ack for task '%s' was never registered or was already received", id) + } + } return } +func (m *Manager) doKillTask(task *Task) error { + if task.GetControlMode() == controlmode.KUBERNETES_DIRECT || + task.GetControlMode() == controlmode.KUBERNETES_FAIRMQ { + if m.k8sClient != nil { + if err := m.killK8sTask(context.Background(), task); err != nil { + return err + } + } + // K8s delete is synchronous — send the ack immediately so KillTasks doesn't block. + _ = m.ackKilledTasks.TrySendAck(task.taskId) + return nil + } + return m.schedulerState.killTask(context.TODO(), task.GetMesosCommandTarget()) +} + func (m *Manager) doKillTasks(tasks Tasks) (killed Tasks, running Tasks, err error) { // We assume all tasks are unlocked @@ -601,30 +1274,266 @@ func (m *Manager) doKillTasks(tasks Tasks) (killed Tasks, running Tasks, err err inactiveTasks := tasks.Filtered(func(task *Task) bool { return task.status != ACTIVE }) + // Remove from the roster the tasks which are also in the inactiveTasks list to delete - m.roster = m.roster.Filtered(func(task *Task) bool { + m.roster.updateTasks(m.roster.filtered(func(task *Task) bool { return !inactiveTasks.Contains(func(t *Task) bool { return t.taskId == task.taskId }) - }) + })) + + // Remove from the roster the tasks we are going to kill + // if we couldn't kill a task add it back to the roster + m.roster.updateTasks(m.roster.filtered(func(task *Task) bool { + return !tasks.Contains(func(t *Task) bool { + return t.taskId == task.taskId + }) + })) for _, task := range tasks.Filtered(func(task *Task) bool { return task.status == ACTIVE }) { e := m.doKillTask(task) if e != nil { - log.WithError(e).WithField("taskId", task.taskId).Error("could not kill task") + log.WithError(e). + WithField("taskId", task.taskId). + Error("could not kill task") err = errors.New("could not kill some tasks") + // task should be added back to the roster + m.roster.append(task) } else { killed = append(killed, task) } } - // Remove from the roster the tasks we've just killed - m.roster = m.roster.Filtered(func(task *Task) bool { - return !killed.Contains(func(t *Task) bool { - return t.taskId == task.taskId - }) - }) - running = m.roster + running = m.roster.getTasks() return } + +func (m *Manager) Start(ctx context.Context) { + m.MessageChannel = make(chan *TaskmanMessage, TaskMan_QUEUE) + + m.schedulerState.Start(ctx) + + if m.k8sClient != nil { + go m.watchK8sTasks(ctx) + } + + go func() { + for { + select { + case taskmanMessage, ok := <-m.MessageChannel: + if !ok { // if the channel is closed, we bail + return + } + err := m.handleMessage(taskmanMessage) + if err != nil { + log.Debug(err) + } + } + } + }() +} + +func (m *Manager) stop() { + close(m.MessageChannel) +} + +func (m *Manager) handleMessage(tm *TaskmanMessage) error { + if m == nil { + return errors.New("manager is nil") + } + + messageType := tm.GetMessageType() + + switch messageType { + case taskop.AcquireTasks: + go func() { + err := m.acquireTasks(tm.GetEnvironmentId(), tm.GetDescriptors()) + if err != nil { + log.WithError(err). + WithField("partition", tm.GetEnvironmentId().String()). + WithField("level", infologger.IL_Support). + Errorf("failed task creation and Mesos resources allocation during the deployment of the environment. More details in Devel logs.") + } + }() + case taskop.ConfigureTasks: + go func() { + err := m.configureTasks(tm.GetEnvironmentId(), tm.GetTasks()) + m.internalEventCh <- event.NewTasksStateChangedEvent(tm.GetEnvironmentId(), tm.GetTasks().GetTaskIds(), err) + }() + case taskop.TransitionTasks: + go func() { + err := m.transitionTasks(tm.GetEnvironmentId(), tm.GetTasks(), tm.GetSource(), tm.GetEvent(), tm.GetDestination(), tm.GetArguments()) + m.internalEventCh <- event.NewTasksStateChangedEvent(tm.GetEnvironmentId(), tm.GetTasks().GetTaskIds(), err) + }() + case taskop.TaskStatusMessage: + mesosStatus := tm.status + mesosState := mesosStatus.GetState() + switch mesosState { + case mesos.TASK_FINISHED: + log.WithPrefix("taskman"). + WithFields(logrus.Fields{ + "taskId": mesosStatus.GetTaskID().Value, + "state": mesosState.String(), + "reason": mesosStatus.GetReason().String(), + "source": mesosStatus.GetSource().String(), + "message": mesosStatus.GetMessage(), + "level": infologger.IL_Devel, + }). + WithField("partition", tm.GetEnvironmentId().String()). // fixme: this is empty! + Info("task finished") + taskIDValue := mesosStatus.GetTaskID().Value + m.updateTaskState(taskIDValue, "DONE") + m.tasksFinished++ + case mesos.TASK_LOST, mesos.TASK_KILLED, mesos.TASK_FAILED, mesos.TASK_ERROR: + log.WithPrefix("taskman"). + WithFields(logrus.Fields{ + "taskId": mesosStatus.GetTaskID().Value, + "state": mesosState.String(), + "reason": mesosStatus.GetReason().String(), + "source": mesosStatus.GetSource().String(), + "message": mesosStatus.GetMessage(), + "level": infologger.IL_Devel, + }). + WithField("partition", tm.GetEnvironmentId().String()). // fixme: this is empty! + Error("task inactive exception") + taskIDValue := mesosStatus.GetTaskID().Value + t := m.GetTask(taskIDValue) + if t != nil && t.IsLocked() { + go m.updateTaskState(taskIDValue, "ERROR") + } + } + + // This will check if the task update is from a reconciliation, as well as whether the task + // is in a state in which a mesos Kill call is possible. + // Reconcilation tasks are not part of the taskman.roster + if mesosStatus.GetReason().String() == "REASON_RECONCILIATION" && + (mesosState == mesos.TASK_STAGING || + mesosState == mesos.TASK_STARTING || + mesosState == mesos.TASK_RUNNING || + mesosState == mesos.TASK_KILLING || + mesosState == mesos.TASK_UNKNOWN) { + killCall := calls.Kill(mesosStatus.TaskID.GetValue(), mesosStatus.AgentID.GetValue()) + err := calls.CallNoData(context.TODO(), m.schedulerState.cli, killCall) + if err != nil { + log.WithPrefix("taskman"). + WithField("taskId", mesosStatus.GetTaskID().Value). + WithField("state", mesosState.String()). + WithField("source", mesosStatus.GetSource().String()). + WithField("message", mesosStatus.GetMessage()). + WithField(infologger.Level, infologger.IL_Devel). + WithError(err). + Errorf("could not kill task '%s' after reconciliation", mesosStatus.GetTaskID().Value) + } + } else { + // Enqueue task state update + go m.updateTaskStatus(&mesosStatus) + } + case taskop.TaskStateMessage: + go m.updateTaskState(tm.taskId, tm.state) + case taskop.ReleaseTasks: + go m.releaseTasks(tm.GetEnvironmentId(), tm.GetTasks()) + case taskop.KillTasks: + log.WithPrefix("taskman"). + WithField("partition", tm.GetEnvironmentId().String()). + WithField("level", infologger.IL_Devel). + WithField("status", tm.status.String()). + WithField("source", tm.status.GetSource().String()). + WithField("message", tm.status.GetMessage()). + Warn("unexpected KillTasks message received") + case taskop.Error: + log.WithPrefix("taskman"). + WithField("partition", tm.GetEnvironmentId().String()). + WithField("level", infologger.IL_Devel). + WithField("status", tm.status.String()). + WithField("source", tm.status.GetSource().String()). + Warn("taskman received error: %s", tm.GetError()) + } + + return nil +} + +func (m *Manager) HandleExecutorFailed(e *event.ExecutorFailedEvent) map[uid.ID]struct{} { + // returns the set of environment ids affected by the failed executor + if len(e.ExecutorId.Value) == 0 { + return nil + } + + tasksForFailedExecutor := m.roster.filtered(func(t *Task) bool { + return t.executorId == e.ExecutorId.Value + }) + + envIdsForExecutor := make(map[uid.ID]struct{}) + + for _, t := range tasksForFailedExecutor { + envIdsForExecutor[t.GetEnvironmentId()] = struct{}{} + t.executorId = "" // causes IsLocked() to become false for sure + thisTask := t + go func() { + m.updateTaskState(thisTask.taskId, "ERROR") + thisTask.status = INACTIVE + taskParent := thisTask.GetParent() + if taskParent != nil { + thisTask.GetParent().UpdateStatus(INACTIVE) + } + }() + } + return envIdsForExecutor +} + +func (m *Manager) HandleAgentFailed(e *event.AgentFailedEvent) map[uid.ID]struct{} { + // returns the set of environment ids affected by the failed executor + if len(e.AgentId.Value) == 0 { + return nil + } + + tasksForFailedExecutor := m.roster.filtered(func(t *Task) bool { + return t.agentId == e.AgentId.Value + }) + + envIdsForExecutor := make(map[uid.ID]struct{}) + + for _, t := range tasksForFailedExecutor { + envIdsForExecutor[t.GetEnvironmentId()] = struct{}{} + t.agentId = "" // causes IsLocked() to become false for sure + thisTask := t + go func() { + m.updateTaskState(thisTask.taskId, "ERROR") + thisTask.status = INACTIVE + if taskParent := thisTask.GetParent(); taskParent != nil { + taskParent.UpdateStatus(INACTIVE) + } + }() + } + return envIdsForExecutor +} + +// This function should only be called from the SIGINT/SIGTERM handler +func (m *Manager) EmergencyKillTasks(tasks Tasks) { + for _, t := range tasks { + aidStr := t.GetAgentId() + killCall := calls.Kill(t.GetTaskId(), aidStr) + detector := "" + var err error + + if len(aidStr) > 0 { + aid := &mesos.AgentID{Value: aidStr} + aci := m.AgentCache.Get(*aid) + if aci != nil { + host := aci.Hostname + detector, err = apricot.Instance().GetDetectorForHost(host) + if err != nil { + detector = "" + } + } + } + + err = calls.CallNoData(context.TODO(), m.schedulerState.cli, killCall) + if err != nil { + log.WithPrefix("termination"). + WithField("detector", detector). + WithError(err). + Error(fmt.Sprintf("Mesos couldn't kill task %s", t.GetTaskId())) + } + } +} diff --git a/core/task/managerk8s.go b/core/task/managerk8s.go new file mode 100644 index 000000000..7d730842e --- /dev/null +++ b/core/task/managerk8s.go @@ -0,0 +1,558 @@ +/* + * === This file is part of ALICE O² === + * + * Copyright 2026 CERN and copyright holders of ALICE O². + * Author: Michal Tichak + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In applying this license CERN does not waive the privileges and + * immunities granted to it by virtue of its status as an + * Intergovernmental Organization or submit itself to any jurisdiction. + */ + +package task + +import ( + "context" + "fmt" + "maps" + "regexp" + "strconv" + "strings" + "sync" + "time" + + corev1 "k8s.io/api/core/v1" + k8sapierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/watch" + + "github.com/AliceO2Group/Control/common/gera" + "github.com/AliceO2Group/Control/common/utils/uid" + "github.com/AliceO2Group/Control/control-operator/api/v1alpha1" + k8sclient "github.com/AliceO2Group/Control/control-operator/pkg/client" + "github.com/AliceO2Group/Control/core/controlcommands" + "github.com/AliceO2Group/Control/core/task/channel" + "github.com/AliceO2Group/Control/core/task/sm" + "github.com/AliceO2Group/Control/core/task/taskclass" + "github.com/k0kubun/pp" + "github.com/spf13/viper" +) + +// TODO: revisit all the timeouts in ECS. they shouldn't be hardcoded. +const ( + k8sDeployTimeout = 80 * time.Second + k8sTransitionTimeout = 80 * time.Second + k8sWatchRetryDelay = 5 * time.Second + + // k8sJitTaskTemplateName is the shared TaskTemplate used to run JIT/DPL + // pipeline tasks, since their task class name is unique per generated + // workflow and can't be pre-registered as its own TaskTemplate. + k8sJitTaskTemplateName = "dpl" +) + +// jitClassNameRe matches JIT-generated task class identifiers of the form +// "jit-<40-hex-char-sha1>-" or "jit-k8s-<40-hex-char-sha1>-" +// (see configuration/template/dplutil.go) and captures the devicename, e.g. "readout-proxy" or "Dispatcher". +var jitClassNameRe = regexp.MustCompile(`^jit-(?:k8s-)?[0-9a-f]{40}-(.+)$`) + +// k8sEnvRegistry maps ECS environment IDs to K8s custom Environment names. +type k8sEnvRegistry struct { + mu sync.RWMutex + envs map[uid.ID]string +} + +func newK8sEnvRegistry() *k8sEnvRegistry { + return &k8sEnvRegistry{envs: make(map[uid.ID]string)} +} + +func (r *k8sEnvRegistry) set(envId uid.ID, customObjectName string) { + r.mu.Lock() + r.envs[envId] = customObjectName + r.mu.Unlock() +} + +func (r *k8sEnvRegistry) get(envId uid.ID) (string, bool) { + r.mu.RLock() + defer r.mu.RUnlock() + envK8sName, ok := r.envs[envId] + return envK8sName, ok +} + +func (r *k8sEnvRegistry) delete(envId uid.ID) { + r.mu.Lock() + delete(r.envs, envId) + r.mu.Unlock() +} + +// k8sPortAllocator hands out control ports for JIT tasks, one monotonically +// increasing counter per node (hostNetwork is used, so ports must not collide +// between JIT tasks scheduled to the same node) +type k8sPortAllocator struct { + portBase uint16 + next map[string]uint16 +} + +func newK8sPortAllocator(port uint16) *k8sPortAllocator { + return &k8sPortAllocator{portBase: port, next: make(map[string]uint16)} +} + +func (allocator *k8sPortAllocator) allocate(hostname string) uint16 { + port, ok := allocator.next[hostname] + if !ok { + port = allocator.portBase + } + allocator.next[hostname] = port + 1 + return port +} + +// newK8sClientFromViper creates a K8s client from viper config. +func newK8sClientFromViper() (*k8sclient.Client, error) { + namespace := viper.GetString("kubeNamespace") + if namespace == "" { + return nil, fmt.Errorf("kubeNamespace is empty") + } + kubeconfig := viper.GetString("kubeConfig") + return k8sclient.New(kubeconfig, namespace) +} + +type k8sTaskEntry struct { + task *Task + desc *Descriptor +} + +// deployKubernetesTasks deploys all KUBERNETES-mode descriptors by creating one custom Environment object that +// references the pre-deployed custom TaskTemplate object. It blocks until the operator reports status.state == "standby". +func (m *Manager) deployKubernetesTasks(ctx context.Context, envId uid.ID, descriptors Descriptors) (DeploymentMap, error) { + log.WithField("partition", envId). + WithField("count", len(descriptors)). + Info("deploying K8s tasks") + + entries, err := m.createK8sTaskEntries(envId, descriptors) + if err != nil { + log.WithField("partition", envId).WithError(err).Error("failed to create K8s task entries") + return nil, err + } + + nodeToRefs, err := m.buildK8sNodeTaskRefs(envId, entries) + if err != nil { + log.WithField("partition", envId).WithError(err).Error("failed to build K8s node task refs") + return nil, err + } + + for _, e := range entries { + m.roster.append(e.task) + } + + envK8sName, err := m.createK8sCustomEnvironmentObject(ctx, envId, nodeToRefs) + if err != nil { + log.WithField("partition", envId).WithError(err).Error("failed to create K8s custom Environment object") + return nil, err + } + + log.WithField("partition", envId). + WithField("customEnvObj", envK8sName). + Infof("K8s custom Environment object created, waiting up to %s for standby", k8sDeployTimeout) + + if err := m.waitForK8sEnvState(ctx, envK8sName, "standby", k8sDeployTimeout); err != nil { + log.WithField("partition", envId). + WithField("customEnvName", envK8sName). + WithError(err).Error("K8s Environment did not reach standby") + return nil, fmt.Errorf("waiting for Environment %s standby: %w", envK8sName, err) + } + + log.WithField("partition", envId). + WithField("customEnvObj", envK8sName). + Infof("K8s Environment reached standby, %d tasks deployed", len(entries)) + + deployed := make(DeploymentMap, len(entries)) + for _, e := range entries { + deployed[e.task] = e.desc + } + return deployed, nil +} + +func (m *Manager) createK8sTaskEntries(envId uid.ID, descriptors Descriptors) ([]k8sTaskEntry, error) { + entries := make([]k8sTaskEntry, 0, len(descriptors)) + for _, desc := range descriptors { + taskClass, ok := m.classes.GetClass(desc.TaskClassName) + if !ok || taskClass == nil { + return nil, fmt.Errorf("task class not found: %s", desc.TaskClassName) + } + + nodeName, err := nodeNameFromDescriptor(desc, taskClass) + if err != nil { + return nil, err + } + t := m.newTaskForKubernetes(desc, nodeName) + + wants, err := m.GetWantsForDescriptor(desc, envId) + if err != nil { + return nil, fmt.Errorf("getting wants for %s: %w", desc.TaskClassName, err) + } + + for _, ch := range wants.InboundChannels { + if ch.Addressing == channel.IPC { + t.localBindMap[ch.Name] = channel.NewBoundIpcEndpoint(ch.Transport) + } else { + log.WithField("task", desc.TaskClassName). + WithField("channel", ch.Name). + Warn("TCP inbound channel in K8s task — skipping port allocation") + } + if len(ch.Global) != 0 { + t.localBindMap["::"+ch.Global] = t.localBindMap[ch.Name] + } + } + + entries = append(entries, k8sTaskEntry{task: t, desc: desc}) + } + return entries, nil +} + +const jitK8sBasePortStr = "jitK8sBasePort" + +func (m *Manager) buildK8sNodeTaskRefs(envId uid.ID, entries []k8sTaskEntry) (map[string][]v1alpha1.TaskReference, error) { + nodeToRefs := make(map[string][]v1alpha1.TaskReference) + + portAllocator := newK8sPortAllocator(viper.GetUint16(jitK8sBasePortStr)) + for _, e := range entries { + t := e.task + desc := e.desc + + taskClass, _ := m.classes.GetClass(desc.TaskClassName) + + if err := t.BuildTaskCommand(desc.TaskRole); err != nil { + return nil, fmt.Errorf("building task command for %s: %w", desc.TaskClassName, err) + } + cmd := t.GetTaskCommandInfo() + + for varName, defaultValue := range map[string]string{ + "O2_ROLE": t.hostname, + "O2_SYSTEM": "FLP", + } { + varIsSet := false + for _, e := range cmd.Env { + key, _, _ := strings.Cut(e, "=") + if key == varName { + varIsSet = true + break + } + } + if !varIsSet { + cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", varName, defaultValue)) + } + } + + refName := taskClass.Identifier.Name + nameSuffix := "" + isJit := false + if match := jitClassNameRe.FindStringSubmatch(refName); match != nil { + isJit = true + refName = k8sJitTaskTemplateName + nameSuffix = match[1] + } + + var argsCLI []string + if isJit { + // Right now ports are allocated only for jitted tasks. This way is used + // because ports are being assigned by Mesos. Right now we assume that readout + // and dd (stfbuilder/sender) have ports assigned in the TaskTemplate and + // thus they don't need portAllocator. This might change. + controlPort := portAllocator.allocate(t.hostname) + cmd.Env = append(cmd.Env, fmt.Sprintf("OCC_CONTROL_PORT=%d", controlPort)) + cmd.Arguments = append(cmd.Arguments, "--control-port", strconv.FormatUint(uint64(controlPort), 10)) + + // The "dpl" TaskTemplate runs `bash -c