Compare commits

...

579 Commits

Author SHA1 Message Date
James Read
0283b51eca feature: Login/Logout links (#443)
* feature: Login/Logout links

* cicd: codestyle
2024-10-19 07:49:41 +00:00
jamesread
1af2e92132 feature: Local user login! 2024-10-18 01:20:04 +01:00
James Read
71ad5d2e3a feature: local auth! (#441)
* feature: local auth!

* cicd: contentLogin -> content-login

* bugfix: CSS codestyle
2024-10-17 13:23:01 +00:00
James Read
32b5fee108 bugfix: Fixed crash when requesting execution status that could not be found (NPE) (#440) 2024-10-15 10:48:55 +00:00
James Read
de81ec00fd feature: mix of bits (#439)
* feature: mix of bits

* bugfix: codestyle
2024-10-15 07:47:51 +00:00
Simon Dahlbacka
7cc07158ab bugfix: Turns out this should now be 403 instead (#438) 2024-10-14 13:53:23 +00:00
jamesread
3b8976fd51 feature: Login screen 2024-10-14 09:26:33 +01:00
jamesread
fb7e650267 feature: OAuth2 Redirect URL 2024-10-14 09:25:21 +01:00
jamesread
6a7187fb5b feature: OAuth2! :-D 2024-10-14 09:25:21 +01:00
James Read
b31cdf15a2 feature: Email argument type (#433)
* feature: Email argument type

* bugfix: Fix error if additional links is null
2024-10-13 18:40:16 +00:00
jamesread
6e0e0e8133 cicd: cleanup console log 2024-10-13 01:10:47 +01:00
jamesread
706441799f feature: Custom navigation links in the nav (sidebar/topbar) 2024-10-13 00:35:47 +01:00
jamesread
0b66bc7bbd cicd: Labels on issue templates [skip ci] 2024-10-13 00:35:28 +01:00
jamesread
86e876a9c9 Merge branch 'main' of ssh://github.com/OliveTin/OliveTin 2024-10-12 23:18:29 +01:00
jamesread
8b33061bbb cicd: Remove unused Jenkinsfile 2024-10-12 23:15:06 +01:00
James Read
411f1bff1c feature: Style the currently selected section in nav, and few minor style improvements (#429)
* feature: Style the currently selected section in nav, and few minor style improvements

* bugfix: No unit on 0 length
2024-10-12 21:45:26 +00:00
James Read
eee4c4e404 bugfix: Auth in the wrong place (#430) 2024-10-12 21:14:09 +00:00
James Read
a187c8c8a0 feature: Slightly nicer looking logs and diagnostics pages (#427) 2024-10-11 17:40:40 +00:00
Simon Dahlbacka
35dca50863 Make it possible to redirect to a login url unless authenticated (#347)
* feature: Try to navigate to UrlOnUnauthenticated if set and not authenticated

* refactor: use better naming

* feature: default to allow guest
2024-10-11 17:36:31 +00:00
Simon Dahlbacka
00856f15a7 feature: pass ot_ args to shellAfterCompleted (#420)
* feature: pass ot_ args to shellAfterCompleted

* refactor: Implement this the right way

---------

Co-authored-by: James Read <contact@jread.com>
2024-10-11 14:59:02 +00:00
James Read
d1c67a9dd8 depbump: js micromatch 4.0.8 (#426) 2024-10-10 23:38:35 +00:00
James Read
6c289506c2 bugfix: Fixed unusual issues that slipped through with shellAfterCompleted (#425)
* bugfix: Fixed unusual issues that slipped through with shellAfterCompleted #419 #420

* cicd: Fix broken style lint
2024-10-10 23:15:03 +00:00
James Read
5d82ae7680 feature: Log username with each request #422 (#424) 2024-10-10 20:50:47 +00:00
James Read
9737886839 feature: Better websocket error message (#417) 2024-10-02 00:19:49 +00:00
James Read
1d9502d800 Feature better 404s (#416)
* feature: CSS on actions

* feature: Less crashes when things are not found
2024-10-01 23:21:37 +00:00
James Read
c0a18f82a5 feature: Link to log outputs (#415) 2024-10-01 23:17:09 +00:00
jamesread
9723a38ca1 bugfix: Crash on trying to start a non-existant action from the API 2024-10-01 21:19:17 +01:00
James Read
8258a758d2 bugfix: Dashboards with multiple spaces in the name could not be navigated properly (#408) 2024-09-16 22:36:40 +00:00
James Read
044613ae9a bugfix: Null action map, causing crash on startup actions (#407) 2024-09-11 16:19:52 +00:00
James Read
76c9171356 feature: Add OT_ vars to each execution (#406) 2024-09-10 22:52:46 +00:00
James Read
f8c330aae3 feature: Better feedback on shellAfterCommand, and mark OliveTin specific output with "OliveTin::" in logs (#403) 2024-09-02 22:59:19 +00:00
jamesread
d4bd7dd586 doc: shellAfterCompleted should use output, not stdout [skip ci] 2024-09-02 21:14:34 +01:00
Mitch Brown
fa20f6a63a Fixed typo 2024-09-01 00:22:45 +01:00
James Read
b9ce695616 feature: Nicer default colors (catpuccin/frappe (#395) 2024-08-30 20:48:17 +00:00
James Read
3f1dbf1130 bugfix: System users can now be used in ACLs (#396) 2024-08-30 20:48:07 +00:00
James Read
6413e51cf5 feature: Exec permission denied now marks the action as "blocked" and adds a log message. Timeouts also add a documentation link to the command output. Tags always set. (#398) 2024-08-30 20:36:13 +00:00
James Read
defcf6d26e fmt: Rebuilt ACL code to have less boilerplate (#397) 2024-08-30 20:30:44 +00:00
James Read
2671983c43 bugfix: Execution dialog duration no longer has issues displaying actions that were never started. (#394) 2024-08-30 20:28:27 +00:00
James Read
dc9653307b feature: Logs, column for tag, and left align status. Stop nav flash (#393) 2024-08-30 20:28:12 +00:00
jamesread
eb91eb33d5 bugfix: Log for http request mangling changed to DEBUG level 2024-08-30 21:27:56 +01:00
James Read
ddb803d9b5 feature: Sidebar hiding now pure CSS & theme reloading for devs (#391) 2024-08-25 23:47:43 +00:00
dependabot[bot]
c9095b4d67 build(deps): bump axios from 1.6.8 to 1.7.4 in /integration-tests (#387)
Bumps [axios](https://github.com/axios/axios) from 1.6.8 to 1.7.4.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v1.6.8...v1.7.4)

---
updated-dependencies:
- dependency-name: axios
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-14 22:14:35 +00:00
James Read
40158eda71 bugfix: Race condition on executor logs (#385) 2024-08-14 22:06:50 +00:00
dependabot[bot]
bbbbfceeb3 build(deps): bump github.com/docker/docker (#386)
Bumps [github.com/docker/docker](https://github.com/docker/docker) from 26.1.4+incompatible to 26.1.5+incompatible.
- [Release notes](https://github.com/docker/docker/releases)
- [Commits](https://github.com/docker/docker/compare/v26.1.4...v26.1.5)

---
updated-dependencies:
- dependency-name: github.com/docker/docker
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-14 22:06:06 +00:00
James Read
9ca1940834 feature: change all navigation to path based (#384)
* feature: change all navigation to path based

* bugfix: load default dashboard if actions is empty
2024-08-14 11:35:10 +00:00
James Read
37160a91f3 feature: take full page and display footer at bottom pf window (#383) 2024-08-14 06:56:30 +00:00
James Read
274d036f74 fmt: Cleanup terminal code (#381)
* fmt: Clean up OutputTerminal code

* fmt: Clean up OutputTerminal code
2024-08-09 19:04:57 +00:00
James Read
1fe0e49adb bugfix: #337 - Entity overwriting (#382) 2024-08-09 19:04:50 +00:00
James Read
7a7a07d9ad feature: Minor change to action timeout message to be more consistent with other logs (#380) 2024-08-09 14:53:02 +00:00
James Read
5b5fca0837 feature: enableCustomJs toggle to allow loading ./custom-webui/custom.js (#379) 2024-08-09 09:21:50 +00:00
James Read
fab0264d9b feature: Cleanup execution dialog, and log display status. (#378)
* feature: Cleanup execution dialog, and log display status.

* fmt: Remove empty block

* cicd: Use ActionStatusDisplay in test

* bugfix: missed promise

* bugfix: missed promise

* bugfix: Didnt return getText on ActionStatusDisplay
2024-08-08 20:46:46 +00:00
jamesread
8d839ee6ce bugfix: fsnotify event logs moved to DEBUG level, because info was spammy 2024-08-07 00:22:49 +01:00
jamesread
90efbf3159 fmt: #368 2024-08-07 00:21:02 +01:00
jamesread
17dd1b4158 Merge branch 'main' of ssh://github.com/OliveTin/OliveTin 2024-08-07 00:17:42 +01:00
dependabot[bot]
e5f6d8ff50 build(deps): bump github.com/docker/docker (#372)
Bumps [github.com/docker/docker](https://github.com/docker/docker) from 26.0.2+incompatible to 26.1.4+incompatible.
- [Release notes](https://github.com/docker/docker/releases)
- [Commits](https://github.com/docker/docker/compare/v26.0.2...v26.1.4)

---
updated-dependencies:
- dependency-name: github.com/docker/docker
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-06 16:02:13 +00:00
jamesread
e183910b88 Merge branch 'main' of ssh://github.com/OliveTin/OliveTin 2024-08-06 16:35:10 +01:00
James Read
8cd5b9fb46 feature: Trace log message on ExecRequest for debugging (#375) 2024-08-06 13:46:49 +00:00
wushuzh
6958445f83 Use windows compatible cmds and wait more time (#374)
* bugfix: change action request cnt type as Counter

* bugfix: fix type of act req count and typo

* bugfix: change to correct type of act req in TC

* bugfix: use Windows compatible cmds for test

* bugfix: wait more for slow test env

---------

Co-authored-by: wushuzh <wushuzh@outlook.com>
2024-08-06 13:43:16 +00:00
James Read
ef91294d5e cicd: Make grpc to fix codeql (#373) 2024-07-30 13:03:56 +00:00
jamesread
a36f286f8a cicd: Make grpc to fix codeql 2024-07-30 13:58:12 +01:00
wushuzh
d9e921950f bugfix: fix type of the prometheus metric act req as count (#370)
* bugfix: change action request cnt type as Counter

* bugfix: fix type of act req count and typo

* bugfix: change to correct type of act req in TC

---------

Co-authored-by: wushuzh <wushuzh@outlook.com>
2024-07-26 08:41:42 +00:00
wushuzh
ffcd19e748 bugfix: use cross-platform module filepath to set usedConfigDir (#369)
* bugfix: use filepath to set correct configDir in Windows

* test: improve unittest rep folder creation

---------

Co-authored-by: wushuzh <wushuzh@outlook.com>
2024-07-22 09:33:56 +00:00
jamesread
d0eb132b95 Merge branch 'main' of ssh://github.com/OliveTin/OliveTin 2024-07-18 15:11:19 +01:00
jamesread
1cf971c092 feature: Better format for server dashboard 2024-07-18 15:11:06 +01:00
James Read
49f745be68 fmt: Set for setdir (#367) 2024-07-18 12:08:36 +00:00
wushuzh
b50824a705 feature: support basic dev tasks in Windows (#364)
* feature: support basic dev tasks in Windows

* feature: support front-end targets in Windows

---------

Co-authored-by: wushuzh <wushuzh@outlook.com>
2024-07-17 12:51:08 +00:00
James Read
69d1cc75a7 feature: css classes on displays (#363) 2024-07-16 10:01:31 +00:00
jamesread
a54ea505c9 bugfix: Allow . in enxtended variable paths 2024-07-15 22:20:49 +01:00
James Read
a1563b72ae Feature argument grid layout (#360)
* feature: Argument form now uses a grid layout, making the input boxes take up available room

* feature: Argument form now uses a grid layout, making the input boxes take up available room

* feature: Argument form now uses a grid layout, making the input boxes take up available room

* feature: Argument form now uses a grid layout, making the input boxes take up available room
2024-07-15 16:17:08 +00:00
James Read
31d7168aac feature: #350 Environment variable PORT can be used to override the default 1337, and internal services will calculate their port based on that (#358) 2024-07-15 14:28:53 +00:00
James Read
09016e1d5f feature: Entitiy data now serializes non-strings (#357) 2024-07-15 12:10:13 +00:00
James Read
652882350c cicd: Update login-action (#356) 2024-07-15 12:09:35 +00:00
James Read
20c4423799 feature: Argument labels will end with a :, unless they end with ?, : or . already (#355) 2024-07-15 08:24:54 +00:00
jamesread
bb90a5da92 feature: Use SVG icon for window maximize #343 2024-07-13 20:46:06 +01:00
jamesread
3ca3a2dd3c Merge branch 'main' of ssh://github.com/OliveTin/OliveTin 2024-07-13 20:43:21 +01:00
jamesread
510c48e1af bugfix: Restore default theme 2024-07-13 20:43:04 +01:00
James Read
3ac809c234 feature: Container images upgraded to Fedora 40. (#354) 2024-07-13 18:36:10 +00:00
James Read
9dd33bc3f9 bugfix: Empty environment variable names causing exec failures on Windows, thanks @sirjmann92 ! (#353) 2024-07-12 16:22:03 +00:00
James Read
897cc0e034 depbump: Upgrade Go to 1.21 (#352) 2024-07-11 22:33:36 +00:00
James Read
482ef0e5e8 feature: Helper scripts for ssh, themes, etc (#349)
* feature: Helper scripts for ssh, themes, etc

* cicd: Update goreleaser to support v2 .goreleaser.yml files
2024-07-07 22:10:33 +00:00
dependabot[bot]
e0678fc0a9 build(deps): bump github.com/rs/cors from 1.10.1 to 1.11.0 (#348)
Bumps [github.com/rs/cors](https://github.com/rs/cors) from 1.10.1 to 1.11.0.
- [Commits](https://github.com/rs/cors/compare/v1.10.1...v1.11.0)

---
updated-dependencies:
- dependency-name: github.com/rs/cors
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: James Read <contact@jread.com>
2024-07-07 07:01:33 +00:00
jamesread
9cb5574b99 feature: easy-ssh-docs 2024-07-07 01:06:43 +01:00
jamesread
c18b91f684 feature: Easy SSH 2024-07-07 00:55:22 +01:00
James Read
6622a6ded4 bugfix: Kill the process group, not just the parent process (#328), , thanks @ioqy, @vvrein (#346)
* bugfix: Kill the process group, not just the parent process (#328), thanks @ioqy, @vvrein

* bugfix: Kill the process group, not just the parent process (#328), thanks @ioqy, @vvrein

* bugfix: Kill the process group, not just the parent process (#328), thanks @ioqy, @vvrein
2024-07-04 00:12:28 +00:00
James Read
fb6aaa52c7 feature: New diagnostics page, showing SSH keys found. (#344) 2024-07-03 23:34:07 +00:00
James Read
a1adc2a85d bugfix: Output width in chrome (#345) 2024-07-03 23:33:58 +00:00
dependabot[bot]
3d9cb621dd build(deps-dev): bump braces from 3.0.2 to 3.0.3 in /integration-tests
Bumps [braces](https://github.com/micromatch/braces) from 3.0.2 to 3.0.3.
- [Changelog](https://github.com/micromatch/braces/blob/master/CHANGELOG.md)
- [Commits](https://github.com/micromatch/braces/compare/3.0.2...3.0.3)

---
updated-dependencies:
- dependency-name: braces
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-04 00:34:34 +01:00
James Read
943b4c75aa feature: Password type (no validation) (#340) 2024-07-01 08:31:53 +00:00
James Read
fb972fae55 bugfix: Handle update failed update checks silently (#342) 2024-06-30 22:05:04 +00:00
dependabot[bot]
eb4f28dfda build(deps-dev): bump ws from 8.16.0 to 8.17.1 in /integration-tests (#335)
Bumps [ws](https://github.com/websockets/ws) from 8.16.0 to 8.17.1.
- [Release notes](https://github.com/websockets/ws/releases)
- [Commits](https://github.com/websockets/ws/compare/8.16.0...8.17.1)

---
updated-dependencies:
- dependency-name: ws
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-19 05:25:03 +00:00
James Read
e36fedf4b2 Check for updates default disabled (#333)
* feature: Checking for updates is now disabled by default (#93)

* feature: Default config has checkForUpdates: false (it defaults to false anyway) to make people aware of the update checking feature
2024-06-04 18:37:59 +00:00
James Read
362a97c59e feature: No more tracking in update checking! :-) (#93) (#332) 2024-06-04 12:21:11 +00:00
James Read
c82beb61a9 feature: Vastly improved logs and tests for killing actions (#330)
* bugfix: Sleep testing

* feature: Vastly improved testing for killing actions (#328)

* cicd: Add find-flakey-tests target

* cicd: Better debugging for domStatus

* cicd: Debug flakey test

* cicd: Debug flakey test

* cicd: Is cors messing with killaction?

* cicd: Slip broken test in CI

* cicd: Skip broken test in CI

* cicd: Skip broken test in CI
2024-06-03 16:19:15 +00:00
James Read
00a8a0bf69 fmt: Center argument-wrapper #327, point 5 (#331) 2024-06-02 22:09:24 +00:00
James Read
ffc17dd73b bugfix: Terminal width fitting (#329)
* bugfix: Terminal width fitting

* bugfix: Terminal width fitting

* fmt: css codestyle issue presumably from pkg upgrade
2024-06-02 11:44:23 +00:00
James Read
238abc95ad feature: Customizable default directory, back and action icons (#326) 2024-05-31 22:43:05 +00:00
James Read
ac0f3ab6f8 feature: Live command output! (#325)
* feature: Live command output WIP

* cicd: Fix live command output build errors

* feature: Live command output WIP, remove stdout/stderr split

* feature: Live command output

* Restore output im stepExecAfter, rename so to ost for OutputStreamer

* Update executor.go

* feature: Remove output from log message, bad for long messages, binary output, and security

* feature: Live command output WIP
2024-05-31 20:56:49 +00:00
James Read
4bac315568 feature: The executor now controls the public action map - meaning actions work without the API, and pages load faster (and much more!) (#324)
* feature: The executor now controls the public action map

* bugfix: Build the action map if there are no entities!

* bugfix: Sort by title if the actions have the same config order
2024-05-27 22:52:06 +00:00
James Read
daa48b5a73 feature: OliveTin now relies on websocket, and will show a big error if it isn't available (#323)
* feature: OliveTin now relies on websocket, and will show a big error if it isnt available

* feature: OliveTin now relies on websocket, and will show a big error if it isnt available
2024-05-26 18:21:54 +00:00
James Read
c70cc864ee feature: Websocket URL is now relocatable (#322)
* feature: Websocket URL is now relocatable

* feature: Websocket URL is now relocatable
2024-05-26 17:03:12 +00:00
James Read
dc7ff40da6 bugfix: #316 Entity file change debouce timer now uses delay after initial event (#321) 2024-05-26 17:01:37 +00:00
James Read
046ffaecf4 feature: Displays now have centered text, and "fit in" a bit better (#320) 2024-05-26 16:54:11 +00:00
James Read
3904f8563d feature: Refresh dashboards when entity files change. (#319)
* feature: Refresh dashboards when entities change

* feature: Refresh dashboards when entities change

* bugfix: Concurrency, lock around websocket write
2024-05-24 22:10:38 +00:00
James Read
18423a9888 bugfix: Default load dashboard when it contains multiple spaces (#318) 2024-05-23 08:22:08 +00:00
James Read
8fbbd9b32c feature: displays have basic styling (#317) 2024-05-23 08:21:57 +00:00
guangwu
7e34efd453 chore: remove refs to deprecated io/ioutil (#309)
Signed-off-by: guoguangwu <guoguangwug@gmail.com>
Co-authored-by: James Read <contact@jread.com>
2024-05-13 15:20:58 +00:00
jamesread
9d33e62d34 Merge branch 'main' of ssh://github.com/OliveTin/OliveTin 2024-05-13 15:39:54 +01:00
jamesread
6b2bc0adc0 cicd: run build-snapshot against prs 2024-05-13 15:39:42 +01:00
James Read
80083fedab bugfix: Race condition in stringvariables (#311) 2024-05-13 14:10:44 +00:00
James Read
1ab35fdb36 feature: #Logs and similar in the address bar to load relevant section (#306) (#310) 2024-05-13 12:12:04 +00:00
James Read
f43ece4263 feature: Better sosreports (#308) 2024-05-13 07:01:45 +00:00
jamesread
ea1ce82ded fmt: Change publicid map debug URL 2024-05-13 01:04:21 +01:00
jamesread
c24adaafcb Merge branch 'main' of ssh://github.com/OliveTin/OliveTin 2024-05-06 23:01:09 +01:00
jamesread
c42875b107 cicd: update package-lock.json 2024-05-06 23:00:35 +01:00
jamesread
447ad36d4d cicd: update package-lock.json 2024-05-06 23:00:05 +01:00
jamesread
71dc467b31 cicd: Quick target to run default integration tests 2024-05-06 22:59:49 +01:00
James Read
8625e1fc0a fmt: Clean REST API Testing (#307)
* fmt: Clean REST API Testing

* fmt: Clean REST API Testing
2024-05-06 21:44:12 +00:00
James Read
638e5b7fe1 doc: fix default licenses in package.json (#304) 2024-05-01 07:38:47 +00:00
James Read
f467c69e8f Argument suggestions (#301)
* feature: Suggestions for inputs

* feature: #103 Completed suggestions support
2024-04-28 08:55:40 +00:00
James Read
8fd98874e2 feature: Save logs! #174 (#300) 2024-04-28 07:58:25 +00:00
James Read
dc6f6c2896 feature: Kill support in the UI (#297)
* feature: Kill command API (wip)

* feature: Kill support in the UI
2024-04-27 21:51:45 +00:00
James Read
a783fc8cd4 feature: Arguments are now also passed as ENV variables (#296) 2024-04-26 23:03:17 +00:00
James Read
6a16a7c6c2 fmt: onfileindir (#295) 2024-04-26 23:02:40 +00:00
James Read
ceb215a6dc bugfix: #272 if the dialog is already open, close it (#294) 2024-04-26 23:02:17 +00:00
James Read
d6cb634824 feature: Extra file info using onfileindir (#293) 2024-04-26 22:05:28 +00:00
James Read
500419307b Nullable args (#292)
* feature: Argument values can be null be default. Use RejectNull to change.

* feature: Argument values can be null be default. Use RejectNull to change.
2024-04-26 17:36:33 +00:00
James Read
12cf0013e2 feature: extra args for fileindir (#291) 2024-04-25 23:32:58 +00:00
James Read
318d4fe0d0 cicd: Reduce test flake and add lots more debugging info (#290) 2024-04-24 15:22:15 +00:00
James Read
a3aee3603f More screenshots link (#289)
* More screenshots link

* bugfix: wip

* cicd: Test flakey tests

* cicd: more tests

* cicd: more tests

* cicd: Try a 10s timeout

* cicd: Better debugging of process start / stop

* cicd: Better debugging of process start / stop

* cicd: Possibly unflaky tests
2024-04-23 20:54:54 +00:00
jamesread
7c6f36c600 doc: Update images 2024-04-22 23:34:36 +01:00
James Read
dde8a9cbb6 cicd: More test coverage that would have caught recent issues (#287)
* cicd: More test coverage that would have caught recent issues

* bugfix: test flake

* bugfix: test flake

* cicd: Reduce test flake

* cicd: more test coverage

* depbump: testing packages
2024-04-22 21:40:48 +00:00
James Read
dfca712cb1 bugfix: Update icon on buttons (#288) 2024-04-22 19:02:59 +00:00
James Read
5057ba2e1c bugfix: Start filewatcher on different goroutines (#286) 2024-04-21 19:51:49 +00:00
jamesread
8f1dfffa49 fmt: Clean up log statement 2024-04-21 20:12:47 +01:00
James Read
6991724258 bugfix: Unexpected message #283 (#284) 2024-04-20 19:58:38 +00:00
James Read
09e1de06ce bugfix: Dont rename logo filenames (#282) 2024-04-19 23:27:13 +00:00
James Read
5a644b0856 bugfix: The web interface now refreshes when the config file is changed! (#281) 2024-04-19 23:11:40 +00:00
James Read
86b2187236 cicd: Increase coverage (#280) 2024-04-19 21:56:45 +00:00
James Read
fafacfeafb bugfix: Move all inotify to filehelper (#279) 2024-04-19 21:56:21 +00:00
James Read
362019738d feature: #267 add jq to containers (#278) 2024-04-19 16:16:23 +00:00
dependabot[bot]
1a0ba6c6b1 build(deps): bump golang.org/x/net from 0.22.0 to 0.23.0 (#277)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.22.0 to 0.23.0.
- [Commits](https://github.com/golang/net/compare/v0.22.0...v0.23.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-19 16:16:09 +00:00
jamesread
4c2cc5da1c bugfix: Remove debug log left in the code 2024-04-19 00:58:05 +01:00
James Read
30e2aa141b feature: Rate limit for actions (#76) (#275) 2024-04-18 23:51:59 +00:00
dependabot[bot]
3768a57eaa build(deps): bump github.com/docker/docker (#274)
Bumps [github.com/docker/docker](https://github.com/docker/docker) from 26.0.0+incompatible to 26.0.2+incompatible.
- [Release notes](https://github.com/docker/docker/releases)
- [Commits](https://github.com/docker/docker/compare/v26.0.0...v26.0.2)

---
updated-dependencies:
- dependency-name: github.com/docker/docker
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-18 23:49:41 +00:00
James Read
db5de9be97 feature: #135 Permissions for logs (#273) 2024-04-18 20:52:04 +00:00
James Read
f60ab6ce85 feature: Show username in header (#270) 2024-04-15 00:09:55 +00:00
James Read
43c48aef17 feature: Renamed authJwtSecret to authJwtHmacSecret (#271)
* feature: Renamed authJwtSecret to authJwtHmacSecret

* feature: rename authJwtSecret
2024-04-15 00:01:30 +00:00
James Read
8341b1d6d0 feature: Huge JWT improvements for CloudFlare, etc (#269) 2024-04-14 22:59:45 +00:00
jamesread
dd11961a11 bugfix: custom-webui prefix 2024-04-12 22:07:06 +01:00
James Read
3de819a0e9 feature: add prometheus metrics (#268)
* feature: add prometheus metrics

* bugfix: ixed issue with path
2024-04-11 23:23:40 +00:00
James Read
0b546eaeb5 Improve screenshots visibility 2024-04-11 11:13:10 +01:00
jamesread
558e1819bf doc: Updated mobile screenshot 2024-04-11 10:20:03 +01:00
James Read
9d6afa2fe7 doc: typo #266 (#267) 2024-04-11 06:23:24 +00:00
James Read
5f3a967515 depbump: 2024-04 (#265)
* depbump: 2024-04

* go mod tidy
2024-04-10 23:03:25 +00:00
jamesread
179f1be19a bugfix: Null JWT verifier 2024-04-11 00:04:34 +01:00
James Read
f13a5c070a feature: JWT Validation wip for Cloudflare (#264) 2024-04-10 22:14:33 +00:00
James Read
9476d052b6 cicd: Dont build snapshot on tag (#263) 2024-04-10 20:14:18 +00:00
James Read
1d446ace04 custom-webui and faster theme loading (#262)
* feature: custom-webui and faster theme loading

* feature: custom-webui and faster theme loading
2024-04-09 08:26:18 +00:00
jamesread
3a8d8706a6 bugfix: remove underline from sidebar links 2024-04-04 20:37:25 +01:00
James Read
910418925a bugfix: dark mode headr (#261) 2024-04-02 22:57:54 +00:00
James Read
1b539df2aa bugfix: rounded buttons for folders (#260) 2024-04-02 22:41:59 +00:00
James Read
f5794e57ee bugfix: sidebar nav wont tab when hidden (#259) 2024-04-02 22:40:46 +00:00
James Read
7dd1d0a7fc Markup optimisations (#258)
* feature: Cleanup markup for accessibility

* feature: Cleanup markup for accessibility

* feature: Cleanup markup for accessibility

* feature: Cleanup markup for accessibility

* bugfix: Broken test

* feature: Cleanup markup for accessibility
2024-04-02 21:58:23 +00:00
James Read
ce670cf58c Bug ignored signals (#257)
* bugfix: Signals, like SIGKILL etc were ignored. Now we capture them and add the err to stderr

* bugfix: Signals, like SIGKILL etc were ignored. Now we capture them and add the err to stderr
2024-04-01 20:30:34 +00:00
jamesread
c4d1a2a105 cicd: Bug in dockerfile.arm64 2024-03-24 23:06:37 +00:00
James Read
709223bd46 feature: Removed unimplemented CSS rules from config, but added ID for buttons to allow for easy theming (#255) 2024-03-24 22:35:19 +00:00
jamesread
19b4340e18 Merge branch 'main' of ssh://github.com/OliveTin/OliveTin 2024-03-24 22:20:58 +00:00
jamesread
a5a1c64dcb bugfix: Entity buttons didnt support popupOnStart 2024-03-24 22:20:39 +00:00
dependabot[bot]
2b7bdffe41 build(deps): bump follow-redirects in /integration-tests (#248)
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.15.4 to 1.15.6.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.15.4...v1.15.6)

---
updated-dependencies:
- dependency-name: follow-redirects
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-24 21:32:11 +00:00
dependabot[bot]
a2df96354e build(deps): bump github.com/docker/docker (#249)
Bumps [github.com/docker/docker](https://github.com/docker/docker) from 25.0.3+incompatible to 25.0.5+incompatible.
- [Release notes](https://github.com/docker/docker/releases)
- [Commits](https://github.com/docker/docker/compare/v25.0.3...v25.0.5)

---
updated-dependencies:
- dependency-name: github.com/docker/docker
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-24 21:31:58 +00:00
James Read
8b49eeff98 bugfix: Regex validation client side was not running (#254) 2024-03-24 21:31:23 +00:00
James Read
a8c4db197d Bug webui search order (#253)
* bugfix: Search order of webui directories was not deterministic

* bugfix: webui dir search order was not deterministic

* bugfix: webui dir search order was not deterministic

* bugfix: webui dir search order was not deterministic
2024-03-23 22:14:08 +00:00
James Read
1319f314ff Argument suggestions (#252)
* feature: Suggestions for inputs

* feature: #103 Completed suggestions support
2024-03-23 21:52:53 +00:00
James Read
781abaaf40 feature: Entitiys in dropdowns #247 (#251) 2024-03-22 23:49:41 +00:00
James Read
744debc00a feature: add apprise (#250) 2024-03-22 19:00:30 +00:00
James Read
77321c1bcd cicd: Try {configdir}/var/entities/ when looking for entity files as well (#246) 2024-03-15 10:07:51 +00:00
James Read
c97fd60c25 feature: #56 custom tegex for arguments (#244) 2024-03-08 22:17:10 +00:00
James Read
2fb40ff443 feature: Exec on Calendar file support (#243)
* feature: Exec on calendar file

* feature: support for datetime args

* feature: Schedule commands based on seconds
2024-03-08 20:31:51 +00:00
James Read
a992ef84f5 doc: YouTube Banner (#242) 2024-03-07 17:26:15 +00:00
James Read
8fce4c79b6 Update README.md 2024-03-07 17:11:33 +00:00
jamesread
7e4aa9ebbe doc: Updated demo video 2024-03-06 08:27:54 +00:00
James Read
522e5bb129 cicd: Flakey test on dropdowns (#241) 2024-03-05 17:23:17 +00:00
James Read
1a97836dc3 feature: erm, added support 200,000 icons via iconify (#240) 2024-03-05 16:59:46 +00:00
jamesread
e1bc9276bc doc: Add rounded screenshot 2024-03-05 16:10:43 +00:00
James Read
555b6929e4 feature: Rounded buttons... because apparently they look modern?! (#239)
* feature: Rounded buttons... because apparently they look modern?!

* bugfix: Logo in readme
2024-03-05 14:53:50 +00:00
jamesread
ee4d61e476 bugfix: Logo in readme 2024-03-05 14:48:48 +00:00
James Read
f15235d120 Msot recent execution output on the dashboard. (#238)
* feature: Support for most recent action output on dashboard

* feature: Support for most recent action output on dashboard
2024-03-04 23:33:50 +00:00
jamesread
471d5726f6 bugfix: Switch to first dashboard if actions page is empty 2024-03-04 23:37:53 +00:00
jamesread
e344530fc0 bugfix: Switch to first dashboard if actions page is empty 2024-03-04 22:47:45 +00:00
jamesread
29fe38eff4 bugfix: Switch to first dashboard if actions page is empty 2024-03-03 23:09:15 +00:00
jamesread
25e643371e Merge branch 'main' of ssh://github.com/OliveTin/OliveTin 2024-02-29 23:21:16 +00:00
jamesread
32cb8dd873 feature: entity filepaths are relative to the config dir 2024-02-29 23:20:48 +00:00
James Read
12cc61fba5 feature: Nicer container dashboard example in the default config (#235) 2024-02-29 21:27:54 +00:00
James Read
fe40731df3 bugfix: PageTitle works again (#236) 2024-02-29 21:27:34 +00:00
jamesread
7464ca5543 fmt: typo in "dashboards" 2024-02-29 16:28:04 +00:00
jamesread
ce83521429 Merge branch 'main' of ssh://github.com/OliveTin/OliveTin 2024-02-28 23:18:40 +00:00
jamesread
27ab530ba6 bugfix: StartAction responds with used tracking ID, not necessarily the requested tracking ID 2024-02-28 23:18:31 +00:00
James Read
843121f5fd bugfix: #233 switch sidebar to 100dvh (#234) 2024-02-28 21:32:44 +00:00
jamesread
d26c469107 bugfix: #233 switch sidebar to 100dvh 2024-02-28 21:27:29 +00:00
James Read
97453260eb bugfix: #230 Set public-url in parcel to . (#231) 2024-02-28 17:40:43 +00:00
jamesread
06b85c5769 cicd: I am tired and cannot type it seems 2024-02-27 01:18:28 +00:00
jamesread
5bb21031ac cicd: extra files for all ghe dockers 2024-02-27 01:09:13 +00:00
jamesread
256d6139b7 cicd: Add extra files needed for docker 2024-02-27 00:24:25 +00:00
jamesread
aa342047ed cicd: Dont run the docker ps in default config 2024-02-27 00:10:07 +00:00
jamesread
ea663f8286 cicd: Vagrant, need /opt/OliveTin-configs created 2024-02-25 23:04:31 +00:00
jamesread
e953dfb017 fmt: Ansible -> Automation, robot icon 2024-02-25 22:45:12 +00:00
jamesread
54170f3da6 feature: Better default config file, restart icons 2024-02-25 22:39:29 +00:00
jamesread
77ec2fea63 feature: sosreport in the browser outputs text/plain 2024-02-25 21:10:47 +00:00
jamesread
83beab4c92 bugfix: onExecutionFinished ignores actions it cannot find 2024-02-25 00:49:17 +00:00
jamesread
29b6d12454 feature: Trigger another action after first action completes 2024-02-25 00:29:55 +00:00
jamesread
866a38f286 bugfix: All execution tracking IDs must now be unique 2024-02-25 00:11:35 +00:00
jamesread
0ec2e7069b bugfix: All previous entity files are removed when file is updated 2024-02-25 00:01:51 +00:00
jamesread
f3934b1906 bugfix: Default path for entities files 2024-02-24 22:28:52 +00:00
jamesread
fb2bb63d15 feature: comfig file provided with docker volume, upgrade to f38 2024-02-24 07:27:54 +00:00
jamesread
58cc04298f feature: Popup execution dialog from logs 2024-02-23 23:52:41 +00:00
jamesread
42535feadf feature: Hide actions in sidebar if it is empty 2024-02-23 22:43:52 +00:00
jamesread
baa690ffc2 feature: include entities in packages 2024-02-23 22:19:45 +00:00
jamesread
d13f0a7acf fmt: regex raw string 2024-02-23 21:36:28 +00:00
jamesread
b747199528 fmt: Removed uneeded assign 2024-02-23 21:35:55 +00:00
jamesread
6a1af44aa0 fmt: actionName -> actionTitle 2024-02-23 21:30:35 +00:00
jamesread
6da050e3b9 bugfix: Allow static IDs 2024-02-23 21:30:13 +00:00
jamesread
b8f23ce80c cicd: fix broken test, hopefully 2024-02-23 17:19:36 +00:00
jamesread
865bef532a bugfix: Titles on every button 2024-02-23 16:55:09 +00:00
jamesread
6fb158190d cicd: make grpc is not working 2024-02-23 16:27:31 +00:00
jamesread
0e3f9c8ceb bugfix: #192 repeating logs 2024-02-23 16:09:58 +00:00
jamesread
4dba6fd0f9 depbump: everything 2024-02-23 00:33:02 +00:00
jamesread
fbbf168e88 cicd: fix tests 2024-02-23 00:08:50 +00:00
jamesread
2cd739c3b4 cicd: make grpc for codeql 2024-02-22 23:57:50 +00:00
jamesread
a8e770726a bugfix: unittests 2024-02-22 23:42:40 +00:00
jamesread
0c5a99cc03 bugfix: Use better hash algo 2024-02-22 23:31:13 +00:00
jamesread
2dee246593 bugfix: sidebar fixed 2024-02-22 23:22:21 +00:00
jamesread
381bf59fbd feature: The mega dashboards & entities commit. 2024-02-22 23:16:43 +00:00
James Read
fddf83f27d https everywhere 2024-02-10 12:23:49 +00:00
James Read
3d3e19e26a Create devskim.yml (#229) 2024-02-10 10:44:39 +00:00
jamesread
c082a5438a cicd: wip 2024-02-09 23:47:27 +00:00
jamesread
5adab1091f cicd: Utility to get the latest snapshot 2024-02-09 23:01:24 +00:00
jamesread
290a2ec91b cicd: upgrade codeql 2024-02-09 22:38:18 +00:00
jamesread
9ebeabac51 cicd: update upload-artifact 2024-02-09 22:27:59 +00:00
jamesread
b5e2c8d6b8 cicd: Include all of webui/* in packages now it is processed by parcel 2024-02-09 22:09:13 +00:00
jamesread
a482b6a3c2 cicd: Upgrade goreleaser, fix deprecations 2024-02-09 22:08:45 +00:00
jamesread
f348de6a03 cicd: Need to build webui-dist before integration tests 2024-02-09 21:57:10 +00:00
jamesread
8df8978516 cicd: upgrade actions 2024-02-09 21:22:10 +00:00
jamesread
086d8fd21c feature: HTML/JS/CSS minifiction and cache busting 2024-02-09 17:01:39 +00:00
jamesread
7dce77adcf cicd: These are just testing certs, but they do not need to be in git 2024-02-09 16:17:42 +00:00
James Read
917a0469d8 Proxy integration tests (#228)
* cicd: Proxies included in integration testing

* cicd: proxy configs for integration testing
2024-02-09 16:12:25 +00:00
James Read
c12431d8a3 feature: popupOnStart allows for many feedback options when actions are started (#227) 2024-02-08 21:54:08 +00:00
jamesread
dc0cf33d37 feature: center align the fieldset titles 2024-02-07 10:58:49 +00:00
jamesread
15d332012f feature: Buttons are now fixed-width, so align to center 2024-02-07 10:53:46 +00:00
jamesread
99460beafd feature: box icon for containers 2024-02-07 10:52:46 +00:00
James Read
5b0cfb5c33 feature: entities wip (#226) 2024-02-07 09:36:57 +00:00
James Read
759e747f54 feature: readyz API endpoint support (#225) 2024-02-07 09:36:50 +00:00
James Read
6892a679ee feature: Dashboards, at long last (#224)
* feature: Dashboards, at long last

* fmt: action button IDs now use hypens. Removed ;
2024-02-07 08:54:22 +00:00
jamesread
1b13a2bc4b cicd: setup-go v5 2024-02-07 08:47:48 +00:00
jamesread
63e8e10a6d cicd: checkout v4 2024-02-07 08:47:00 +00:00
James Read
0615a7e353 feature: #202 Cron seconds support optional. Example in default config. (#223) 2024-02-02 21:58:30 +00:00
James Read
16acf9db91 bugfix: Execution dialog would only show first result (#222)
* bugfix: execution-dialog

* bugfix: execution-dialog
2024-02-02 21:22:29 +00:00
James Read
0c19ba59d2 Markup improvements (#220)
* bugfix: various markup improvements, accessibility

* bugfix: various markup improvements, accessibility
2024-01-27 23:43:30 +00:00
James Read
143528d919 bugfix: #216 - fix scroll in chrome (#219) 2024-01-27 22:09:12 +00:00
dependabot[bot]
07cdf378f6 build(deps): bump follow-redirects in /integration-tests (#214)
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.15.3 to 1.15.4.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.15.3...v1.15.4)

---
updated-dependencies:
- dependency-name: follow-redirects
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-11 08:35:50 +00:00
James Read
f559a2f9c9 feature: #69 - Add action confirmation (#208)
* feature: #69 - Add action confirmation

* fmt: style
2023-12-28 23:50:39 +00:00
James Read
6b3e9e4676 feature: clock and ashtonished emoji (#207) 2023-12-28 23:48:27 +00:00
James Read
3f72b7cc0d bugfix: Argument form on smaller screens (#209) 2023-12-28 23:47:02 +00:00
James Read
4ce5b0e645 cicd: Improve tests (#205)
* cicd: make it easier to grab snapshot builds

* cicd: Better support for running tests against VMs

* Update multipleDropdowns.js
2023-12-28 22:04:47 +00:00
James Read
e92ab8d741 #201 - startActionByAlias should be HTTP GET (#206) 2023-12-28 20:34:55 +00:00
jamesread
6b0e414932 More debug info for auth 2023-12-21 01:26:17 +00:00
jamesread
00927f3ba3 Revert "ACL check was broken, addToEveryAction should come first (#204)"
This reverts commit b0faecfa75.
2023-12-21 01:22:18 +00:00
James Read
b0faecfa75 ACL check was broken, addToEveryAction should come first (#204) 2023-12-21 00:07:40 +00:00
James Read
c15449f99a #197 Dialog has min-width, but should be max-width (#203) 2023-12-20 22:07:56 +00:00
dependabot[bot]
68f04c0912 build(deps): bump golang.org/x/crypto from 0.14.0 to 0.17.0 (#199)
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.14.0 to 0.17.0.
- [Commits](https://github.com/golang/crypto/compare/v0.14.0...v0.17.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-19 13:22:07 +00:00
James Read
c3c010443f feature: Better UI for execution buttons (#195) 2023-12-17 04:19:48 +00:00
dependabot[bot]
15c8abf3d6 build(deps): bump axios and wait-on in /integration-tests (#196)
Bumps [axios](https://github.com/axios/axios) to 1.6.2 and updates ancestor dependency [wait-on](https://github.com/jeffbski/wait-on). These dependencies need to be updated together.


Updates `axios` from 0.27.2 to 1.6.2
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v0.27.2...v1.6.2)

Updates `wait-on` from 7.0.1 to 7.2.0
- [Release notes](https://github.com/jeffbski/wait-on/releases)
- [Commits](https://github.com/jeffbski/wait-on/compare/v7.0.1...v7.2.0)

---
updated-dependencies:
- dependency-name: axios
  dependency-type: indirect
- dependency-name: wait-on
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-01 11:53:11 +00:00
James Read
d0f74c1ab7 feature: Additional APIs for StartAction (#188) 2023-10-31 18:17:35 +00:00
dependabot[bot]
ca921c1890 build(deps): bump github.com/docker/docker (#186)
Bumps [github.com/docker/docker](https://github.com/docker/docker) from 24.0.5+incompatible to 24.0.7+incompatible.
- [Release notes](https://github.com/docker/docker/releases)
- [Commits](https://github.com/docker/docker/compare/v24.0.5...v24.0.7)

---
updated-dependencies:
- dependency-name: github.com/docker/docker
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-31 10:05:37 +00:00
jamesread
3b60bbce0a feature: Reduce websocket logging 2023-10-25 23:03:34 +01:00
James Read
8f6b384fe6 Notification support (#183)
* feature: #158 shellAfterComplete support for notifications

* fmt: reduce cyclo
2023-10-25 21:32:18 +00:00
dependabot[bot]
4d04264caa build(deps): bump google.golang.org/grpc from 1.57.0 to 1.57.1 (#181)
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.57.0 to 1.57.1.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.57.0...v1.57.1)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-25 21:31:31 +00:00
James Read
912fd8089e Feature search logs (#182)
* feature: search logs wip

* feature: Log search
2023-10-25 21:29:41 +00:00
James Read
5739091773 feature: Add actionTitle to blocked log messages (#179) 2023-10-25 19:38:27 +00:00
James Read
a09c278585 bugfix: #172 Recent UI changes totally broke dark mode (#180) 2023-10-25 19:37:43 +00:00
jamesread
a7fb49a11b feature: Tidy up error display, and refresh loop 2023-10-24 12:33:32 +01:00
James Read
3db8ae53b5 bugfix: #173 Websocket fixes 1) The upgrader was refusing reverse proxies, 2) The upgrader was "listening" on /, not /websocket (#177) 2023-10-24 04:48:16 +00:00
James Read
311f9a1d00 feature: Reduce debug log spam in ACL/getDashboardComponents (#176) 2023-10-24 04:43:38 +00:00
jamesread
50204f8180 feature: #175 Print verison with --version 2023-10-23 15:59:38 +01:00
jamesread
d639a802dc cicd: Fix syntax issue with setting date 2023-10-12 23:35:37 +01:00
jamesread
f41eafe3bd cicd: Fix date in artifact name 2023-10-12 23:22:42 +01:00
dependabot[bot]
8b080eb3cc build(deps): bump golang.org/x/net from 0.14.0 to 0.17.0 (#171)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.14.0 to 0.17.0.
- [Commits](https://github.com/golang/net/compare/v0.14.0...v0.17.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: James Read <contact@jread.com>
2023-10-12 21:30:14 +00:00
jamesread
268d8a3a90 Merge branch 'main' of ssh://github.com/OliveTin/OliveTin 2023-10-12 09:23:58 +01:00
jamesread
44d6c40c27 cicd: Try to add date to integration test artifacts - easier to sort when downloaded 2023-10-11 22:03:40 +01:00
jamesread
9522e25b1d cicd: bugfix variable name 2023-10-09 23:11:04 +01:00
jamesread
db475895ca Merge branch 'main' of ssh://github.com/OliveTin/OliveTin 2023-10-09 22:55:28 +01:00
jamesread
dc33509127 cicd: Minor tweaks in workflow, version bump 2023-10-09 22:54:02 +01:00
James Read
2ada67be04 Migrate integration-test infrastucture from Cypress to Selenium+Mocha (#170)
* cicd: Move cypress to selenium+mocha

* cicd: with is not supported unless using an a container

* cicd: relative path to mocha

* cicd: the integration-tests runner now starts/stops OliveTin

* cicd: Knowing the CWD helps debugging

* cicd: Headless chrome

* cicd: wait for integration-test server to be started

* fmt: Mostly fix isses from eslint

* cicd: #169 - Test multiple combo boxes

* fmt: let should be const

* cicd: Remove cypress entirely
2023-10-09 21:44:29 +00:00
jamesread
77b17604f3 feature: WIP - a functioning history popup 2023-10-06 09:27:44 +01:00
James Read
d169b3f2b1 feature: #146 Support for maxConcurrent (#168) 2023-10-05 01:22:08 +01:00
James Read
c8210568eb feature: #146 Support for maxConcurrent (#164) 2023-10-04 23:55:35 +00:00
James Read
2ea6430a3b cicd: Remote node_modules from integration tests upload (#167) 2023-10-04 23:50:58 +00:00
dependabot[bot]
ac5f997f85 build(deps-dev): bump postcss from 8.4.23 to 8.4.31 in /webui (#165)
Bumps [postcss](https://github.com/postcss/postcss) from 8.4.23 to 8.4.31.
- [Release notes](https://github.com/postcss/postcss/releases)
- [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md)
- [Commits](https://github.com/postcss/postcss/compare/8.4.23...8.4.31)

---
updated-dependencies:
- dependency-name: postcss
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: James Read <contact@jread.com>
2023-10-04 23:44:24 +00:00
jamesread
e1930c0899 fmt: codestyle 2023-10-05 00:21:02 +01:00
jamesread
020281c1e6 cicd: integration tests against main config 2023-10-04 23:54:34 +01:00
jamesread
9dc81a6280 cicd: . not in path 2023-10-04 22:12:39 +01:00
jamesread
a9906addff cicd: start/stop server for integration tests, update checkout action 2023-10-04 22:06:54 +01:00
jamesread
37269cef02 fmt: reduce cyclo 2023-10-04 21:42:35 +01:00
James Read
186ec00de7 cicd: cypress path to exe 2023-10-04 14:34:39 +01:00
jamesread
abd13b756c cicd: Disable a broken test for now 2023-10-04 10:33:37 +01:00
jamesread
6851683d94 cicd: Include unit tests and integration tests 2023-10-03 23:24:57 +01:00
jamesread
2b4a3ab137 bugfix: broken unit tests 2023-10-03 23:24:36 +01:00
jamesread
6d21bbe03a bugfix: style lint 2023-09-28 23:16:37 +01:00
jamesread
6f4a0e68a7 bugfix: sidebar link style 2023-09-28 23:05:12 +01:00
jamesread
3de0f38049 fmt: trailing spaces 2023-09-28 22:32:16 +01:00
jamesread
216ccbfcef bugfix: Timestamp "IDs" need to be a string 2023-09-28 22:24:35 +01:00
jamesread
66b012cd55 Merge branch 'jamesread-patch-1' 2023-09-27 22:59:03 +01:00
jamesread
8339bd3cb1 Merge branch 'exec-on-new-file' 2023-09-27 22:57:35 +01:00
dependabot[bot]
e93c62b06d build(deps): bump @cypress/request and cypress in /integration-tests (#159)
Bumps [@cypress/request](https://github.com/cypress-io/request) to 3.0.1 and updates ancestor dependency [cypress](https://github.com/cypress-io/cypress). These dependencies need to be updated together.


Updates `@cypress/request` from 2.88.12 to 3.0.1
- [Release notes](https://github.com/cypress-io/request/releases)
- [Changelog](https://github.com/cypress-io/request/blob/master/CHANGELOG.md)
- [Commits](https://github.com/cypress-io/request/compare/v2.88.12...v3.0.1)

Updates `cypress` from 12.17.4 to 13.2.0
- [Release notes](https://github.com/cypress-io/cypress/releases)
- [Changelog](https://github.com/cypress-io/cypress/blob/develop/CHANGELOG.md)
- [Commits](https://github.com/cypress-io/cypress/compare/v12.17.4...v13.2.0)

---
updated-dependencies:
- dependency-name: "@cypress/request"
  dependency-type: indirect
- dependency-name: cypress
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: James Read <contact@jread.com>
2023-09-26 21:30:20 +00:00
James Read
43ceb33b53 feature: onfileindir (#161)
* feature: onfileindir

* fmt: ActionButton.js
2023-09-26 21:29:11 +00:00
James Read
ae0b45c308 Merge branch 'main' into exec-on-new-file 2023-09-26 22:24:48 +01:00
James Read
bbaeff00f3 fmt: ActionButton.js (#162) 2023-09-26 21:24:28 +00:00
jamesread
75a9697586 fmt: ActionButton.js 2023-09-26 22:09:42 +01:00
jamesread
77bd37ca90 feature: onfileindir 2023-09-26 22:04:58 +01:00
James Read
3a44a6a3b4 fmt: ActionButton.js (#160) 2023-09-24 23:57:23 +01:00
James Read
604d956d0c fmt: ActionButton.js 2023-09-24 23:57:01 +01:00
jamesread
822327edfc Now works in insecure contexts 2023-09-24 23:29:51 +01:00
jamesread
3ea14f1353 Merge branch 'main' of github.com:OliveTin/OliveTin 2023-09-23 06:43:52 +01:00
jamesread
7dbc077f76 cicd: Cypress upgrade 2023-09-23 06:42:20 +01:00
James Read
e8cb661938 feature: #146 Support for maxConcurrent (#156) 2023-08-25 15:30:58 +00:00
jamesread
e5a870ed94 feature: websocket implemenation at last! 2023-08-25 15:11:29 +01:00
jamesread
6116e954ba cicd: Useful for development settings 2023-08-25 15:09:24 +01:00
jamesread
56ef7ce95c fmt: fix cyclo complexity in recent jwt key merge 2023-08-24 12:56:03 +01:00
Bernard Crnković
6e2e585175 add support for asymmetric crypto based jwt signatures (#145)
* add support for asymmetric crypto based jwt signatures

* wip: e2e test example

* wip: rsa key parse error...

* fix: test works

---------

Co-authored-by: James Read <contact@jread.com>
2023-08-24 12:33:10 +01:00
jamesread
8c1c0c6029 cicd: Ensure consistency in setup-go 2023-08-24 12:20:35 +01:00
jamesread
11dad79794 fmt: Added pre-commit, fixed a few lint errors, npn updated webui 2023-08-24 12:17:55 +01:00
jamesread
4b3485145f fmt: ws -> window.ws 2023-08-24 12:12:47 +01:00
jamesread
4b5a579b0b fmt: codestyle formatting 2023-08-24 12:11:28 +01:00
jamesread
07bd09473c Merge branch 'websocket' 2023-08-24 11:54:44 +01:00
jamesread
adba3b0d4b cicd: air config file 2023-08-24 11:54:14 +01:00
jamesread
f6162c58f2 feature: websocket progress 2023-08-24 11:52:23 +01:00
jamesread
ed949d1dd8 feature: websocket progress 2023-08-24 11:45:29 +01:00
dependabot[bot]
5d94de418b build(deps): bump tough-cookie and @cypress/request (#154)
Bumps [tough-cookie](https://github.com/salesforce/tough-cookie) and [@cypress/request](https://github.com/cypress-io/request). These dependencies needed to be updated together.

Updates `tough-cookie` from 2.5.0 to 4.1.3
- [Release notes](https://github.com/salesforce/tough-cookie/releases)
- [Changelog](https://github.com/salesforce/tough-cookie/blob/master/CHANGELOG.md)
- [Commits](https://github.com/salesforce/tough-cookie/compare/v2.5.0...v4.1.3)

Updates `@cypress/request` from 2.88.10 to 2.88.12
- [Release notes](https://github.com/cypress-io/request/releases)
- [Changelog](https://github.com/cypress-io/request/blob/master/CHANGELOG.md)
- [Commits](https://github.com/cypress-io/request/compare/v2.88.10...v2.88.12)

---
updated-dependencies:
- dependency-name: tough-cookie
  dependency-type: indirect
- dependency-name: "@cypress/request"
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-09 20:29:49 +00:00
jamesread
f7fd8af124 feature: Websocket early impl 2023-08-09 21:24:05 +01:00
jamesread
d74734972b depbump: General dependency bump 2023-08-09 21:23:25 +01:00
jamesread
8736f5e387 feature: websocket wip 2023-08-09 21:15:47 +01:00
jamesread
89f08ae6c7 feature: log-popup 2023-08-09 21:15:47 +01:00
jamesread
0b75b3847d depbump: General dependency bump 2023-08-09 21:14:14 +01:00
dependabot[bot]
6d27c3db11 build(deps-dev): bump word-wrap from 1.2.3 to 1.2.4 in /webui (#150)
Bumps [word-wrap](https://github.com/jonschlinkert/word-wrap) from 1.2.3 to 1.2.4.
- [Release notes](https://github.com/jonschlinkert/word-wrap/releases)
- [Commits](https://github.com/jonschlinkert/word-wrap/compare/1.2.3...1.2.4)

---
updated-dependencies:
- dependency-name: word-wrap
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-07-19 22:29:27 +00:00
jamesread
b78065e23c cicd: Goreleaser removed support for archive name replacements 2023-07-14 11:14:41 +01:00
dependabot[bot]
c611c5c749 build(deps): bump yaml, stylelint and stylelint-config-standard (#138)
Removes [yaml](https://github.com/eemeli/yaml). It's no longer used after updating ancestor dependencies [yaml](https://github.com/eemeli/yaml), [stylelint](https://github.com/stylelint/stylelint) and [stylelint-config-standard](https://github.com/stylelint/stylelint-config-standard). These dependencies need to be updated together.


Removes `yaml`

Updates `stylelint` from 14.14.0 to 15.6.0
- [Release notes](https://github.com/stylelint/stylelint/releases)
- [Changelog](https://github.com/stylelint/stylelint/blob/main/CHANGELOG.md)
- [Commits](https://github.com/stylelint/stylelint/compare/14.14.0...15.6.0)

Updates `stylelint-config-standard` from 25.0.0 to 33.0.0
- [Release notes](https://github.com/stylelint/stylelint-config-standard/releases)
- [Changelog](https://github.com/stylelint/stylelint-config-standard/blob/main/CHANGELOG.md)
- [Commits](https://github.com/stylelint/stylelint-config-standard/compare/25.0.0...33.0.0)

---
updated-dependencies:
- dependency-name: yaml
  dependency-type: indirect
- dependency-name: stylelint
  dependency-type: direct:development
- dependency-name: stylelint-config-standard
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: James Read <contact@jread.com>
2023-05-13 19:30:33 +00:00
jamesread
5b637154ea fmt: Fieldset media query wasnt doing anything useful 2023-05-13 20:21:20 +01:00
jamesread
0b6edb3c38 fmt: Removed unecessary CSS rules from keyframes 2023-05-13 20:15:21 +01:00
jamesread
f1ba1c55a6 fmt: Minor CSS optimization needed for stylelint 2023-05-13 19:54:32 +01:00
James Read
2940a63d09 Add upgrade warnings + breaking changes (#136)
Recommebded by bestpractices.dev
2023-04-24 13:12:32 +00:00
dependabot[bot]
cc311f88a5 build(deps): bump github.com/docker/docker (#137)
Bumps [github.com/docker/docker](https://github.com/docker/docker) from 23.0.1+incompatible to 23.0.3+incompatible.
- [Release notes](https://github.com/docker/docker/releases)
- [Commits](https://github.com/docker/docker/compare/v23.0.1...v23.0.3)

---
updated-dependencies:
- dependency-name: github.com/docker/docker
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-04-24 12:54:35 +00:00
jamesread
0911df0442 bugfix: console.log made it into a commit 2023-04-03 09:29:08 +01:00
jamesread
86e5dfe2ee cicd: copy/paaye fail 2023-03-24 23:32:48 +00:00
jamesread
34b5570563 cicd: try CONTAINER_TOKEN 2023-03-24 22:23:12 +00:00
jamesread
2d806d8557 cicd: Cleanup image tags, try pass correct credentials to goreleaser 2023-03-24 17:45:19 +00:00
jamesread
a792a0aa55 cicd: fetch-depth 0 needed for goreleaser changelog 2023-03-24 17:06:48 +00:00
jamesread
e7ab8441d7 cicd: upgrade old release of goreleaser 2023-03-24 17:04:20 +00:00
jamesread
d0b7efa24c cicd: docker login v1 is soon to be deprecated. ghcr login needed. 2023-03-24 17:00:40 +00:00
jamesread
d89d198f96 feature: ghcr 2023-03-24 16:03:58 +00:00
jamesread
b638a7c992 cicd: Revert to default dependency 2023-03-21 13:40:22 +00:00
dependabot[bot]
74936a2c13 build(deps): bump github.com/go-critic/go-critic from 0.6.7 to 0.7.0 (#131)
Bumps [github.com/go-critic/go-critic](https://github.com/go-critic/go-critic) from 0.6.7 to 0.7.0.
- [Release notes](https://github.com/go-critic/go-critic/releases)
- [Commits](https://github.com/go-critic/go-critic/compare/v0.6.7...v0.7.0)

---
updated-dependencies:
- dependency-name: github.com/go-critic/go-critic
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-21 10:21:54 +00:00
jamesread
63fe78b4fe feature: webui directory is now configurable 2023-03-21 10:20:17 +00:00
jamesread
8ed3db0170 Merge branch 'main' of ssh://github.com/OliveTin/OliveTin 2023-03-20 14:51:57 +00:00
jamesread
908d97e593 bugfix: #124 essential webui files missing due to change in goreleaser 2023-03-20 14:51:37 +00:00
jamesread
0d137fe121 doc: sosreport in support request 2023-03-20 12:48:36 +00:00
James Read
afe0f0986c feature: sosreport (#132) 2023-03-20 12:47:52 +00:00
dependabot[bot]
edd7935199 build(deps): bump google.golang.org/protobuf from 1.29.1 to 1.30.0 (#129)
Bumps [google.golang.org/protobuf](https://github.com/protocolbuffers/protobuf-go) from 1.29.1 to 1.30.0.
- [Release notes](https://github.com/protocolbuffers/protobuf-go/releases)
- [Changelog](https://github.com/protocolbuffers/protobuf-go/blob/master/release.bash)
- [Commits](https://github.com/protocolbuffers/protobuf-go/compare/v1.29.1...v1.30.0)

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-17 17:31:44 +00:00
dependabot[bot]
b2cc62059e build(deps): bump github.com/bufbuild/buf from 1.14.0 to 1.15.1 (#126)
Bumps [github.com/bufbuild/buf](https://github.com/bufbuild/buf) from 1.14.0 to 1.15.1.
- [Release notes](https://github.com/bufbuild/buf/releases)
- [Changelog](https://github.com/bufbuild/buf/blob/main/CHANGELOG.md)
- [Commits](https://github.com/bufbuild/buf/compare/v1.14.0...v1.15.1)

---
updated-dependencies:
- dependency-name: github.com/bufbuild/buf
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-15 13:23:39 +00:00
dependabot[bot]
cf56150b1e build(deps): bump google.golang.org/protobuf (#128)
Bumps google.golang.org/protobuf from 1.28.2-0.20220831092852-f930b1dc76e8 to 1.29.1.

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-15 09:59:22 +00:00
dependabot[bot]
db93539e89 build(deps): bump google.golang.org/grpc/cmd/protoc-gen-go-grpc (#122)
Bumps [google.golang.org/grpc/cmd/protoc-gen-go-grpc](https://github.com/grpc/grpc-go) from 1.2.0 to 1.3.0.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.2.0...v1.3.0)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc/cmd/protoc-gen-go-grpc
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-09 20:03:45 +00:00
dependabot[bot]
230cf358c9 build(deps): bump github.com/grpc-ecosystem/grpc-gateway/v2 (#120)
Bumps [github.com/grpc-ecosystem/grpc-gateway/v2](https://github.com/grpc-ecosystem/grpc-gateway) from 2.15.1 to 2.15.2.
- [Release notes](https://github.com/grpc-ecosystem/grpc-gateway/releases)
- [Changelog](https://github.com/grpc-ecosystem/grpc-gateway/blob/main/.goreleaser.yml)
- [Commits](https://github.com/grpc-ecosystem/grpc-gateway/compare/v2.15.1...v2.15.2)

---
updated-dependencies:
- dependency-name: github.com/grpc-ecosystem/grpc-gateway/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-09 20:03:15 +00:00
dependabot[bot]
8889808794 build(deps): bump github.com/stretchr/testify from 1.8.1 to 1.8.2 (#117)
Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.8.1 to 1.8.2.
- [Release notes](https://github.com/stretchr/testify/releases)
- [Commits](https://github.com/stretchr/testify/compare/v1.8.1...v1.8.2)

---
updated-dependencies:
- dependency-name: github.com/stretchr/testify
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-09 20:02:58 +00:00
dependabot[bot]
4855d16139 build(deps): bump github.com/spf13/viper from 1.12.0 to 1.15.0 (#116)
Bumps [github.com/spf13/viper](https://github.com/spf13/viper) from 1.12.0 to 1.15.0.
- [Release notes](https://github.com/spf13/viper/releases)
- [Commits](https://github.com/spf13/viper/compare/v1.12.0...v1.15.0)

---
updated-dependencies:
- dependency-name: github.com/spf13/viper
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-28 08:20:42 +00:00
dependabot[bot]
b1baaeddc5 build(deps): bump github.com/bufbuild/buf from 1.3.1 to 1.14.0 (#119)
Bumps [github.com/bufbuild/buf](https://github.com/bufbuild/buf) from 1.3.1 to 1.14.0.
- [Release notes](https://github.com/bufbuild/buf/releases)
- [Changelog](https://github.com/bufbuild/buf/blob/main/CHANGELOG.md)
- [Commits](https://github.com/bufbuild/buf/compare/v1.3.1...v1.14.0)

---
updated-dependencies:
- dependency-name: github.com/bufbuild/buf
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-27 13:46:24 +00:00
dependabot[bot]
8e27543ca5 build(deps): bump github.com/grpc-ecosystem/grpc-gateway/v2 (#108)
Bumps [github.com/grpc-ecosystem/grpc-gateway/v2](https://github.com/grpc-ecosystem/grpc-gateway) from 2.10.3 to 2.15.1.
- [Release notes](https://github.com/grpc-ecosystem/grpc-gateway/releases)
- [Changelog](https://github.com/grpc-ecosystem/grpc-gateway/blob/main/.goreleaser.yml)
- [Commits](https://github.com/grpc-ecosystem/grpc-gateway/compare/v2.10.3...v2.15.1)

---
updated-dependencies:
- dependency-name: github.com/grpc-ecosystem/grpc-gateway/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-24 17:44:29 +00:00
dependabot[bot]
77a6d4ddb0 build(deps): bump github.com/golang-jwt/jwt/v4 from 4.4.2 to 4.5.0 (#115)
Bumps [github.com/golang-jwt/jwt/v4](https://github.com/golang-jwt/jwt) from 4.4.2 to 4.5.0.
- [Release notes](https://github.com/golang-jwt/jwt/releases)
- [Changelog](https://github.com/golang-jwt/jwt/blob/main/VERSION_HISTORY.md)
- [Commits](https://github.com/golang-jwt/jwt/compare/v4.4.2...v4.5.0)

---
updated-dependencies:
- dependency-name: github.com/golang-jwt/jwt/v4
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-24 09:18:44 +00:00
dependabot[bot]
413f184eec build(deps): bump github.com/sirupsen/logrus from 1.8.1 to 1.9.0 (#114)
Bumps [github.com/sirupsen/logrus](https://github.com/sirupsen/logrus) from 1.8.1 to 1.9.0.
- [Release notes](https://github.com/sirupsen/logrus/releases)
- [Changelog](https://github.com/sirupsen/logrus/blob/master/CHANGELOG.md)
- [Commits](https://github.com/sirupsen/logrus/compare/v1.8.1...v1.9.0)

---
updated-dependencies:
- dependency-name: github.com/sirupsen/logrus
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-24 09:18:13 +00:00
dependabot[bot]
338c0617a8 build(deps): bump github.com/fzipp/gocyclo from 0.5.0 to 0.6.0 (#113)
Bumps [github.com/fzipp/gocyclo](https://github.com/fzipp/gocyclo) from 0.5.0 to 0.6.0.
- [Release notes](https://github.com/fzipp/gocyclo/releases)
- [Changelog](https://github.com/fzipp/gocyclo/blob/main/CHANGELOG.md)
- [Commits](https://github.com/fzipp/gocyclo/compare/v0.5.0...v0.6.0)

---
updated-dependencies:
- dependency-name: github.com/fzipp/gocyclo
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-24 09:17:58 +00:00
dependabot[bot]
8e2cb4ee74 build(deps): bump google.golang.org/grpc/cmd/protoc-gen-go-grpc (#112)
Bumps [google.golang.org/grpc/cmd/protoc-gen-go-grpc](https://github.com/grpc/grpc-go) from 1.1.0 to 1.2.0.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/cmd/protoc-gen-go-grpc/v1.1.0...v1.2.0)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc/cmd/protoc-gen-go-grpc
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-24 09:17:44 +00:00
dependabot[bot]
c01c497b03 build(deps): bump golang.org/x/net (#111)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.0.0-20220722155237-a158d28d115b to 0.7.0.
- [Release notes](https://github.com/golang/net/releases)
- [Commits](https://github.com/golang/net/commits/v0.7.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-23 17:35:14 +00:00
dependabot[bot]
1d8f7fe1ab build(deps): bump github.com/go-critic/go-critic from 0.6.2 to 0.6.7 (#110)
Bumps [github.com/go-critic/go-critic](https://github.com/go-critic/go-critic) from 0.6.2 to 0.6.7.
- [Release notes](https://github.com/go-critic/go-critic/releases)
- [Commits](https://github.com/go-critic/go-critic/compare/v0.6.2...v0.6.7)

---
updated-dependencies:
- dependency-name: github.com/go-critic/go-critic
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-23 17:09:00 +00:00
dependabot[bot]
199a2e3729 build(deps): bump google.golang.org/protobuf from 1.28.0 to 1.28.1 (#109)
Bumps [google.golang.org/protobuf](https://github.com/protocolbuffers/protobuf-go) from 1.28.0 to 1.28.1.
- [Release notes](https://github.com/protocolbuffers/protobuf-go/releases)
- [Changelog](https://github.com/protocolbuffers/protobuf-go/blob/master/release.bash)
- [Commits](https://github.com/protocolbuffers/protobuf-go/compare/v1.28.0...v1.28.1)

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-23 17:08:36 +00:00
dependabot[bot]
dd8ad3466b build(deps): bump github.com/stretchr/testify from 1.7.2 to 1.8.1 (#107)
Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.7.2 to 1.8.1.
- [Release notes](https://github.com/stretchr/testify/releases)
- [Commits](https://github.com/stretchr/testify/compare/v1.7.2...v1.8.1)

---
updated-dependencies:
- dependency-name: github.com/stretchr/testify
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: James Read <contact@jread.com>
2023-02-23 17:08:00 +00:00
jamesread
7b7ec8f1e0 cicd: ref_name in artifacts breaks when ref includes / 2023-02-23 17:04:58 +00:00
jamesread
141efcdbbf cicd: Skip docker in snapshot builds 2023-02-23 12:58:10 +00:00
jamesread
5192484494 Merge branch 'main' of ssh://github.com/OliveTin/OliveTin 2023-02-23 12:36:44 +00:00
jamesread
e7750e1c5c cicd: Upgrade goreleaser action 2023-02-23 12:36:30 +00:00
dependabot[bot]
359775adde build(deps): bump golang.org/x/text from 0.3.7 to 0.3.8 (#106)
Bumps [golang.org/x/text](https://github.com/golang/text) from 0.3.7 to 0.3.8.
- [Release notes](https://github.com/golang/text/releases)
- [Commits](https://github.com/golang/text/compare/v0.3.7...v0.3.8)

---
updated-dependencies:
- dependency-name: golang.org/x/text
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: James Read <contact@jread.com>
2023-02-23 12:12:37 +00:00
jamesread
33375059bf cicd: Schedule is required 2023-02-23 10:21:38 +00:00
jamesread
a7a6ad53cc cicd: #106 Dependabot / in branch name breaking snapshot build 2023-02-23 10:19:55 +00:00
jamesread
9df61ab044 feature: Rudimentary "windows support" 2023-02-16 16:40:24 +00:00
jamesread
11bb3f129f fmt: gofmt 2023-02-16 16:21:53 +00:00
jamesread
0335e58b12 feature: Cleaned up jwt auth, and added header auth 2023-02-15 17:41:33 +00:00
James Read
2d173266df feature: Cron support (#102) 2023-02-03 08:53:10 +00:00
James Read
f05de1c726 feature: Execute actions on startup. Externalize the executor and add… (#101)
* feature: Execute actions on startup. Externalize the executor and add tags to logs.

* fmt: Codestyle fixes
2023-02-02 09:35:18 +00:00
Bernard Crnković
b6196a4b3f fix wrong source of "usergroup" metadata (#99)
* extract hardcoded jwt field names into config

* Update config.go

* bugfix: source group from usergroup field instead of password

Co-authored-by: James Read <contact@jread.com>
2023-01-11 20:41:52 +00:00
Bernard Crnković
e18df74d5a extract hardcoded jwt claim names into config (#97)
* extract hardcoded jwt field names into config

* Update config.go

Co-authored-by: James Read <contact@jread.com>
2023-01-10 22:29:31 +00:00
dependabot[bot]
edf10fdd9f build(deps): bump json5 from 1.0.1 to 1.0.2 in /webui (#94)
Bumps [json5](https://github.com/json5/json5) from 1.0.1 to 1.0.2.
- [Release notes](https://github.com/json5/json5/releases)
- [Changelog](https://github.com/json5/json5/blob/main/CHANGELOG.md)
- [Commits](https://github.com/json5/json5/compare/v1.0.1...v1.0.2)

---
updated-dependencies:
- dependency-name: json5
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-10 21:12:52 +00:00
jamesread
81934355ed cicd: Vagrant fedora update 2023-01-02 10:49:20 +00:00
jamesread
e318084300 feature: Changlog format now groups issues 2022-11-13 21:06:07 +00:00
jamesread
476f74efa8 cicd: Wrong path to var 2022-11-13 20:34:20 +00:00
jamesread
e309dd6e67 feature: man page 2022-11-13 20:24:27 +00:00
jamesread
49cb2e5c0a fmt: Ineffectual assignment 2022-11-13 14:23:35 +04:00
jamesread
66370508ff cicd: yamllint github workflows 2022-11-13 13:55:12 +04:00
jamesread
875186ee24 cicd: Actions version bump 2022-11-13 13:34:14 +04:00
jamesread
384ef9787b Merge branch 'main' of ssh://github.com/OliveTin/OliveTin 2022-11-13 12:54:52 +04:00
jamesread
dbcd39e91b cicd: Bump codeql from v1 to v2 2022-11-13 12:40:41 +04:00
jamesread
2244d2c3ef Merge branch 'main' of ssh://github.com/OliveTin/OliveTin 2022-11-13 08:36:20 +00:00
jamesread
37c54b5f77 feature: Look for webui in more directories 2022-11-13 12:35:47 +04:00
jamesread
f48e7dac83 fmt: gofmt 2022-11-13 07:54:50 +00:00
jamesread
21b33086ae fmt: ST1005 https://staticcheck.io/docs/checks/#ST1005 2022-11-13 07:34:23 +00:00
jamesread
6143b2f8ed cicd: Jiggeled targets 2022-11-12 12:55:15 +04:00
jamesread
d53c88f314 fmt: gofmt 2022-11-12 12:20:17 +04:00
jamesread
f42ac5d744 depbump: Migrate from go-cron to cron (the underlying lib) 2022-11-12 12:19:20 +04:00
jamesread
58f3aaf47d cicd: githooks no longer triggered by grpc. protoc added as alternative to buf. 2022-11-12 12:12:09 +04:00
James Read
244404afbc Sidebar (#88)
* feature: allow configurable title

* feature: sidebar prototype

* Spelling fix - Thanks @jonny7read

Spelling fix - Thanks @jonny7read

Co-authored-by: Jonny Read <jonny7read@gmail.com>

* feature: sidebar support

* bugfix: pagetitle

* fmt: hide sidebar under feature toggle

* fmt: Codestyle in style.css and main.js

Co-authored-by: Matthew Strasiotto <39424834+matthewstrasiotto@users.noreply.github.com>
Co-authored-by: Jonny Read <jonny7read@gmail.com>
2022-11-11 12:13:47 +04:00
jamesread
eca3145b1a bugfix: OliveTin openRC chmod +x 2022-10-26 19:59:49 +01:00
jamesread
b560f9c749 bugfix: openrc for apk wasnt being installed properly 2022-10-26 17:24:47 +01:00
jamesread
cf582e7152 feature: #83 - Restore Alpine packages 2022-10-26 11:54:58 +01:00
James Read
8b9932f85c Merge pull request #65 from EzequielBruni/main
Created a service file for OpenRC (Alpine Linux/Gentoo)
2022-10-26 09:28:20 +01:00
James Read
4ec6f8f014 Merge pull request #82 from OliveTin/exec-coc-simplication
fmt: fmt
2022-10-26 09:05:07 +01:00
jamesread
1a8659f81f feature: Error messages now link to that error in docs 2022-10-24 23:28:35 +01:00
jamesread
e950a00a1e fmt: fmt 2022-10-24 23:17:31 +01:00
jamesread
f9ac78d27f fmt: Remove unused sliceutil 2022-10-24 22:51:31 +01:00
James Read
65afdeca36 Merge pull request #81 from OliveTin/feature-input-description
feature: #61 - Descriptions in arguments
2022-10-21 15:47:12 +01:00
jamesread
8e59ac9fb4 bugfix: Fixing CSS class name convention 2022-10-21 15:21:07 +01:00
jamesread
984dae7450 feature: #61 - Descriptions in arguments 2022-10-21 15:14:58 +01:00
jamesread
be7effd317 cicd: Upgrade checkout action from v2 -> v3 https://github.blog/changelog/2022-09-22-github-actions-all-actions-will-begin-running-on-node16-instead-of-node12/ 2022-10-20 09:15:06 +01:00
jamesread
9e7d785ede feature: #68 Remove version from .rpm and .deb 2022-10-19 17:51:41 +01:00
jamesread
434e998151 cicd: Ignore errors when creating podman-container target - the container may not exist 2022-10-19 15:14:13 +01:00
jamesread
b97e75dd67 depbump: webui 2022-10-19 15:13:06 +01:00
jamesread
5424aff946 bugfix: Remove microdnf cache 2022-10-19 15:12:42 +01:00
jamesread
08a1ac2591 feature: #71 URL argument type! 2022-10-19 13:26:22 +01:00
jamesread
fb70cf7dfe cicd: Remove cicd and refactor from changelog - as they dont deliver end user benefits 2022-10-19 12:42:53 +01:00
jamesread
b14a1f411d test: Unsupported argument type 2022-10-19 12:42:22 +01:00
jamesread
fadc616a67 doc: Allow "test" for unittests, integration tests, etc 2022-10-19 10:02:41 +01:00
jamesread
cfde9e5148 bugfix: Safer handling of username/usergroup metadata 2022-10-19 09:48:36 +01:00
jamesread
cc8cad10db feature: drop arm32v7 container because Fedora dropped it 2022-10-01 09:06:41 +01:00
jamesread
d15461a67b Merge branch 'main' of ssh://github.com/OliveTin/OliveTin 2022-09-29 17:18:02 +02:00
jamesread
917857d213 cicd: I hate multiarch docker builds 2022-09-29 17:17:54 +02:00
jamesread
770c6c06bf cicd: useradd after installing it 2022-09-29 08:57:16 +01:00
jamesread
95c589478d cicd: need shadow-utils for useradd 2022-09-29 08:46:59 +01:00
jamesread
b51c139264 feature: Switched to fedora-minimal 2022-09-29 02:21:53 +02:00
jamesread
2ef86beea5 cicd: buf mod update seemed to be needed 2022-09-29 01:21:12 +02:00
jamesread
d029f7b5bd cicd: Manual trigger of workflow to test docker images 2022-09-29 01:12:05 +02:00
jamesread
dfaec75e94 feature: #62 Warn users on detecting PUID or PGID 2022-08-28 22:05:16 +01:00
jamesread
9df1a127c2 feature: #68 - Remove version number from package names 2022-08-27 17:55:40 +01:00
jamesread
d28c24d8c9 cicd: Build in serial to help with GitHub memory limits 2022-08-27 17:55:08 +01:00
jamesread
70c4f0db50 Merge branch 'feature-jwt' 2022-08-27 17:53:45 +01:00
jamesread
061033e5da cicd: kaniko, I am sorry, it was my fault 2022-08-27 15:46:21 +01:00
jamesread
7cea239a43 cicd: Kaniko seems super fussy 2022-08-27 15:35:31 +01:00
jamesread
736c9dee2d cicd: try workspace 2022-08-27 15:34:47 +01:00
jamesread
28753bfabd cicd: print cwd, and probably put go files in correct place 2022-08-27 15:33:44 +01:00
jamesread
2d9335a1b9 cicd: Go install verbose so its better for cicd logs 2022-08-27 15:22:12 +01:00
jamesread
40acb86e10 cicd: kaniko is stupider 2022-08-27 15:18:23 +01:00
jamesread
9cbeb9edc6 cicd: Docker is stupid 2022-08-27 15:17:08 +01:00
jamesread
051ede1280 cicd: adding go files back 2022-08-27 14:48:27 +01:00
jamesread
bb67ea58b6 cicd: go.mod 2022-08-27 13:28:06 +01:00
jamesread
e3a5312614 cicd: go.mod needed for go install 2022-08-27 13:25:32 +01:00
jamesread
adbed0d037 cicd: Remove tabs in Dockerfile 2022-08-27 13:22:46 +01:00
jamesread
99238d21b3 cicd: Move make instructions to dockerfile 2022-08-27 13:13:52 +01:00
jamesread
bfe38e03c3 cicd: copy makefile 2022-08-27 13:10:51 +01:00
jamesread
cfb337f992 cicd: gobuilder cannot install iptuils in a container 2022-08-27 13:06:21 +01:00
jamesread
e7b7a73176 cicd: makefile for gobuilder 2022-08-27 12:50:23 +01:00
jamesread
31db838bfd cicd: go-image-builder 2022-08-27 12:42:14 +01:00
jamesread
1c0a57abea cicd: sha full version 2022-08-24 22:15:39 +01:00
jamesread
f4fdde9b54 cicd: sha full version 2022-08-24 22:13:08 +01:00
jamesread
462efe74c4 cicd: short github hash in dist name 2022-08-24 22:03:33 +01:00
jamesread
e9fbcce220 fmt: Reduce cyclo complexity 2022-08-24 20:52:13 +01:00
jamesread
c38b0351e4 cicd: Short commit hash 2022-08-24 20:50:42 +01:00
jamesread
a92ac09235 cicd: Include branch name in snapshots 2022-08-24 19:46:40 +01:00
jamesread
8a7bf7d565 cicd: upgrade github actions quite considerably 2022-08-24 14:44:10 +01:00
jamesread
0949999840 feature: Initial version of JWT/ACLs 2022-08-24 12:00:33 +01:00
jamesread
7824ce176d depbump: Forgot go-mod-tidy 2022-06-10 19:44:31 +01:00
jamesread
83ce4a3165 doc: Needed more commit types to be clearer 2022-06-10 19:41:52 +01:00
jamesread
a140201267 depbump: June 10th 2022-06-10 19:41:14 +01:00
jamesread
f9b6d60ca8 cicd: integration test for hidden footer 2022-06-10 19:25:23 +01:00
Ezequiel Bruni
9286536fda Created service file for OpenRC 2022-05-15 02:46:42 -05:00
jamesread
496cb400d0 Merge branch 'main' of ssh://github.com/OliveTin/OliveTin 2022-05-01 10:03:23 +01:00
jamesread
b816884e94 feature: Default to relative API path, which should help reverse proxy configs 2022-05-01 10:03:11 +01:00
jamesread
ed7fd2e6ef cicd: ignore installation-id 2022-05-01 10:02:38 +01:00
James Read
c322a50329 Update CONTRIBUTING.adoc
Added description for buf.
2022-04-26 15:39:04 +01:00
jamesread
0b75bce275 doc: Simplified make deps 2022-04-26 15:33:19 +01:00
James Read
d3581e4829 Merge pull request #57 from Fale/rename
bugfix: update code to reflect new repository name. Thank you very much @Fale
2022-04-26 15:10:06 +01:00
Fabio Alessandro Locati
8485a0e446 bugfix: update code to reflect new repository name 2022-04-26 13:43:40 +02:00
jamesread
4f708648e8 cicd: dep bump integration-tests 2022-04-18 09:19:39 +01:00
jamesread
2d7157703c bugfix: Goreleaser now requires explicit bindir. As we are not a distro package, use /usr/local/bin/, which matches the systemd unit file, and the Linux filesystem standard. 2022-04-07 17:13:59 +01:00
jamesread
17c1804129 bugfix: Issue #33 - argument names now allow a-zA-Z0-9_ 2022-04-05 22:58:00 +01:00
jamesread
e05083ebeb bugfix: installation ID now follows configdir 2022-04-05 14:24:40 +01:00
jamesread
0a77f89374 Merge branch 'main' of ssh://github.com/OliveTin/OliveTin 2022-04-05 12:19:30 +01:00
jamesread
ebd1c4e938 security: Use quoted values for origin, URL, and force quoting. 2022-04-05 12:19:24 +01:00
James Read
5115aa1142 Merge pull request #52 from OliveTin/issue-38-config-flag
feature: issue #38 - Config Directories
2022-04-05 12:18:14 +01:00
jamesread
76971603b4 feature: General dependency bump 2022-04-05 11:47:51 +01:00
jamesread
6f5fd20a2c fmt: gofmt cleanup 2022-04-05 11:28:35 +01:00
jamesread
42cde3ba70 security: CVE-2021-44906 npm minimist 2022-04-05 11:13:46 +01:00
jamesread
86a48f4ace security: webui npm packages should have a package-lock.json 2022-04-05 11:10:32 +01:00
jamesread
a725b377cc feature: Customizable external rest address 2022-04-05 11:06:41 +01:00
jamesread
63068dfdea cicd: Remove old dependencies - no longer need machineid 2022-04-05 11:04:57 +01:00
jamesread
3ad640aee0 feature: issue #38 - Config Directories 2022-03-23 16:17:45 +00:00
James Read
bb96072682 Merge pull request #50 from jonesrussell/rej_shownavigation
bugfix: Rename "HideNavigation" to "ShowNavigation"
2022-03-23 15:44:00 +00:00
James Read
24b8857165 Merge branch 'main' into rej_shownavigation 2022-03-23 15:43:43 +00:00
James Read
51112aa2f4 Merge pull request #48 from jonesrussell/rej_showfooter
feature: Show/Hide footer - thank you @jonesrussell !
2022-03-22 22:08:56 +00:00
Russell Jones
5bd90adc1f bugfix: Rename "HideNavigation" to "ShowNavigation" 2022-03-22 20:04:27 +00:00
jamesread
81c26d997d cicd: Make the checklist a little easier to go through. 2022-03-22 10:50:56 +00:00
Russell Jones
af78385759 feature: Show/Hide footer 2022-03-22 00:43:28 +00:00
jamesread
420105ec99 fmt: css ids/classes to use kebab-case 2022-02-23 15:29:56 +00:00
jamesread
10e380a03b cicd: dep bump in integration-tests 2022-02-23 14:49:56 +00:00
jamesread
c985d31e4a cicd: dep bump 2022-02-23 14:47:03 +00:00
jamesread
cabf045202 feature: Switch from MachineID to InstanceID in update checks 2022-02-23 14:18:12 +00:00
jamesread
8ea431b2e4 doc: Added links to install instructions 2022-01-24 20:38:19 +00:00
jamesread
62a45bd214 feature: Send isInContainer to update service 2022-01-11 23:17:50 +00:00
jamesread
59f214fd45 cicd: Add target integration testing image 2022-01-06 00:09:25 +00:00
jamesread
acaf28e200 cicd: Fix broken unit test for hidden nav 2022-01-06 00:00:54 +00:00
James Read
c263a84aa7 Merge pull request #32 from OliveTin/exec-without-shell
bugfix: Working stderr!
2022-01-05 10:53:40 +00:00
jamesread
f09501278a fmt: ActionButton - single quote strings 2022-01-05 09:59:45 +00:00
jamesread
5edeace62e bugfix: Working stderr! 2022-01-05 09:54:40 +00:00
James Read
07ca9f21bc Add CII Best Practices badge
A reminder to focus on project quality.
2022-01-03 00:28:31 +00:00
jamesread
4c7b4ee7de cicd: Build armv6 package, remove docker from arm builds to speed things up. 2021-11-26 19:39:54 +00:00
jamesread
908edc352f cicd: Remove Jenkins 2021-11-23 23:23:49 +00:00
jamesread
fbea7ba928 cicd: Remove APK support - it isnt tested, and probably does not work. It slows down the build and uses disk space. 2021-11-23 15:38:01 +00:00
jamesread
edb0ebbda4 cicd: rename arm builds to arm32 2021-11-23 15:30:27 +00:00
jamesread
d8fa35087e cicd: Build v5 and v7 2021-11-23 14:53:08 +00:00
jamesread
299f492675 bugfix: Regression, goarm was reset to 7, it should be 5 2021-11-22 23:08:20 +00:00
jamesread
f4ff0a209d cicd: Accidently didnt comment out swagger 2021-11-22 11:14:05 +00:00
jamesread
f91e5a7751 cicd: Comment out swagger, as not going to update it very often 2021-11-21 01:22:19 +00:00
jamesread
2b9e763e02 feature: Apple touch icons (#24), accessibility 2021-11-21 01:15:48 +00:00
jamesread
7bdb99764c doc: openapi/swagger Generation for docs 2021-11-21 01:01:06 +00:00
James Read
7d0b73c169 doc: Better short config.yaml in README 2021-11-20 01:07:54 +00:00
James Read
8d3a2ad223 docs: Updated 1-line description + use cases
Tried to more clearly describe the value of OliveTin - safe and simple access to shell commands. It's also not just a Linux tool - so removed the reference to Linux. Simplied the use cases with clearer, non-personal examples based on what members of the community have been doing.
2021-11-20 00:21:59 +00:00
jamesread
a1501ebbe3 doc: Add useful links to release notes 2021-11-19 17:31:03 +00:00
jamesread
9ca94756e5 cicd: Require commit messages are tagged 2021-11-19 15:09:02 +00:00
jamesread
54d6855b3d qemu setup for tag 2021-11-19 12:33:50 +00:00
jamesread
f3231655fa Oh for the love of YAML. 2021-11-19 11:56:05 +00:00
jamesread
aef1e4db1b try qemu static from GH action 2021-11-19 11:54:31 +00:00
jamesread
d5c008188e qemu-user-static 2021-11-19 00:16:37 +00:00
jamesread
d139f24d13 Try only running code checks when code changes. 2021-11-18 12:56:18 +00:00
jamesread
5b4f51f698 Possible solution for multiarch container images 2021-11-18 12:20:08 +00:00
jamesread
af5889a04d Refining the docker images 2021-11-17 20:42:57 +00:00
jamesread
e1ccf444ce platform tag on docker images 2021-11-17 18:46:15 +00:00
jamesread
9943d1ced5 Updated name of token 2021-11-17 15:22:48 +00:00
jamesread
31411d0e95 The release builder 2021-11-17 13:43:27 +00:00
jamesread
8e3112ee16 Vagrant testing support 2021-11-17 13:29:10 +00:00
jamesread
395c5bea99 Still set currentVersion if update checking is disabled 2021-11-17 11:48:24 +00:00
jamesread
7ee404f44c Restored goreleaser packages for now, as I just want to release. Still want them separate later. 2021-11-17 00:38:52 +00:00
jamesread
c7bc22ac7e Rename jenkins job to match build-snapshot 2021-11-15 22:00:54 +00:00
jamesread
e3f4cd8113 Force codestyle to use go 1.16+ 2021-11-15 21:59:09 +00:00
jamesread
1b94e29721 #17 Fixing UI after change to Autonomous Custom Elements 2021-11-15 21:56:39 +00:00
jamesread
fd04922e59 Bringing up to date with head 2021-11-15 20:09:09 +00:00
jamesread
8ecaf33b1a Merge branch 'main' of ssh://github.com/OliveTin/OliveTin 2021-11-15 19:46:18 +00:00
jamesread
2771f58469 Added sensible error messages for no module support, or javascript disabled. 2021-11-15 19:45:25 +00:00
James Read
ff5d60a2dc Add snapshot badge 2021-11-09 10:45:25 +00:00
jamesread
2f6a975bb3 Update workflows 2021-11-09 10:43:22 +00:00
jamesread
b029c7f0ac An init script (!), and ignore bins in root 2021-11-08 11:09:50 +00:00
jamesread
9b2b866701 Only archive the... archives (not the individual bins) 2021-11-04 11:51:37 +00:00
jamesread
d2c25a35f0 make grpc 2021-11-04 11:38:47 +00:00
jamesread
16b43b7b4f goreleaser action 2021-11-04 11:35:07 +00:00
jamesread
e37a653655 rc build action fix syntax 2021-11-04 10:58:11 +00:00
jamesread
a23d5265b8 Try to use a more recent version of go 2021-11-04 10:56:39 +00:00
jamesread
850fe8d704 typo here too :/ 2021-11-04 10:52:40 +00:00
jamesread
5be6934ca6 typo 2021-11-04 10:51:48 +00:00
jamesread
4e8f20e1e6 dont need to run make grpc on every build 2021-11-04 10:51:00 +00:00
jamesread
f9526749eb Add github rc build 2021-11-04 10:50:47 +00:00
jamesread
2e45f9304f Added awesome badge 2021-11-04 10:40:54 +00:00
jamesread
41dc1d9b72 remove errcheck 2021-11-04 10:34:25 +00:00
jamesread
acde5f1fd5 gocritic dep in makefile 2021-11-04 10:33:47 +00:00
jamesread
b3b5b6fe60 codestyle stuff 2021-11-04 10:32:53 +00:00
jamesread
91ce4e93a2 codestyle, fmt, unit tests, etc 2021-11-04 09:35:51 +00:00
jamesread
08eff24dda Spelling mistake 2021-11-04 08:45:15 +00:00
jamesread
78efc5c94e Yay more unit tests 2021-11-04 00:30:36 +00:00
jamesread
3aa7c97bfb Search additional dirs for the webui dir 2021-11-03 22:52:38 +00:00
jamesread
12475cd310 gofmt emoji.go 2021-11-03 22:17:37 +00:00
jamesread
80f3b29d2b formatted tools.go 2021-11-03 22:16:56 +00:00
jamesread
6357c9dc61 Add default title to choices 2021-11-02 22:01:01 +00:00
jamesread
4b2ef44959 remove old target 2021-11-02 19:34:32 +00:00
jamesread
b97fa9ed4a configs path 2021-11-02 17:14:18 +00:00
jamesread
bc73ba340c emojis are not runes 2021-11-02 16:30:54 +00:00
jamesread
c4b6c39dc9 Updated path to config 2021-11-02 16:30:00 +00:00
jamesread
08f32627fc Consolidated configs 2021-11-02 16:27:53 +00:00
jamesread
666d29cd03 Consolidated configs 2021-11-02 16:27:47 +00:00
jamesread
2a767199e2 Improved default config to include TA examples 2021-11-02 16:04:34 +00:00
186 changed files with 20426 additions and 3277 deletions

44
.air.toml Normal file
View File

@@ -0,0 +1,44 @@
root = "."
testdata_dir = "testdata"
tmp_dir = "tmp"
[build]
args_bin = []
bin = "./OliveTin"
cmd = "go build -o OliveTin github.com/OliveTin/OliveTin/cmd/OliveTin"
delay = 1
exclude_dir = ["assets", "tmp", "vendor", "testdata", "webui.dev", "webui"]
exclude_file = []
exclude_regex = ["_test.go"]
exclude_unchanged = false
follow_symlink = false
full_bin = ""
include_dir = []
include_ext = ["go", "tpl", "tmpl", "html"]
include_file = []
kill_delay = "0s"
log = "build-errors.log"
poll = false
poll_interval = 0
rerun = false
rerun_delay = 500
send_interrupt = false
stop_on_error = true
[color]
app = ""
build = "yellow"
main = "magenta"
runner = "green"
watcher = "cyan"
[log]
main_only = false
time = false
[misc]
clean_on_exit = false
[screen]
clear_on_rebuild = false
keep_scroll = true

31
.githooks/commit-msg Executable file
View File

@@ -0,0 +1,31 @@
#!/usr/bin/env python
import sys
commitmsg = ""
with open('.git/COMMIT_EDITMSG', mode='r') as f:
commitmsg = f.readline().strip()
print("Commit message is: " + commitmsg)
ALLOWED_COMMIT_TYPES = [
"cicd",
"test",
"refactor",
"depbump",
"typo",
"fmt",
"doc",
"bugfix",
"security",
"feature",
]
for allowedType in ALLOWED_COMMIT_TYPES:
if commitmsg.startswith(allowedType + ":"):
print("Allowing commit type: ", allowedType)
sys.exit(0)
print("Commit message should start with commit type. One of: ", ", ".join(ALLOWED_COMMIT_TYPES))
sys.exit(1)

View File

@@ -2,7 +2,8 @@
name: Bug report
about: Create a report to help us improve
title: ""
labels: bug
labels:
- "type: bug"
assignees: ''
---

View File

@@ -2,7 +2,8 @@
name: Feature request
about: Suggest an idea for this project
title: ''
labels: enhancement
labels:
- "type: feature-request"
assignees: ''
---

View File

@@ -2,7 +2,8 @@
name: Support request
about: Need some help? Got an error message?
title: ""
labels: support
labels:
- "type: support"
assignees: ''
---
@@ -12,6 +13,12 @@ assignees: ''
If you are getting an error message, then please copy/paste, or better, provide
a screenshot to show us exactly what is wrong.
**Can you provide a sosreport?**
A sosreport really helps us to help you, by providing critical information about your install. If you can generate a sosreport, please copy and paste the output here.
How to generate a sosreport: https://docs.olivetin.app/sosreport.html
**What package/file/container did you use to install OliveTin?**
eg: OliveTin-1234-x86_64.rpm
@@ -33,7 +40,7 @@ If possible, please copy and paste your OliveTin logs from when the error happen
**Screenshot of WebDeveloper console logs**
If you know how, and if you think it's relevant, a screenshot of the
WebDeveloper console from when you clicked a button is often really helpful.
WebDeveloper console from when you clicked a button is often really helpful.
**Anything else?**

View File

@@ -4,16 +4,19 @@ First of all, thank you for considering to raise a pull request!
Dont be afraid to ask for advice before working on a contribution. If youre thinking about a bigger change, especially that might affect the core working or architecture, its almost essential to talk and ask about what youre planning might affect things. Some of the larger future plans may not be documented well so its difficult to understand how your change might affect the general direction and roadmap of this project without asking.
The preferred way to communicate is probably via Discord or GitHub issues.
The preferred way to communicate is probably via Discord or GitHub issues.
Helpful information to understand the project can be found here: [CONTRIBUTING](https://github.com/jamesread/OliveTin/blob/main/CONTRIBUTING.adoc)
Helpful information to understand the project can be found here: [CONTRIBUTING](https://github.com/OliveTin/OliveTin/blob/main/CONTRIBUTING.adoc)
^^^ please delete the lines above when raising a PR to keep the request to the bare essentials ^^^
# Checklist
Please put a X in the boxes as evidence of reading through the checklist.
- [ ] I have forked the project, and raised this PR on a freature branch.
- [ ] `make daemon-compile`, `make daemon-codestyle` and `make daemon-unittests` runs cleanly.
- [ ] `make webui-codestyle` should runs cleanly.
- [ ] I understand and accept the [AGPL-3.0 license](LICENSE) and [code of conduct](CODE_OF_CONDUCT.md), and my contributions fall under these.
- [ ] I have forked the project, and raised this PR on a feature branch.
- [ ] `make githooks` has been run, and my git commit message was accepted by the git hook.
- [ ] `make daemon-compile` runs without any issues.
- [ ] `make daemon-codestyle` runs without any issues.
- [ ] `make daemon-unittests` runs without any issues.
- [ ] `make webui-codestyle` runs without any issues.
- [ ] I understand and accept the [AGPL-3.0 license](LICENSE) and [code of conduct](CODE_OF_CONDUCT.md), and my contributions fall under these.

80
.github/workflows/build-snapshot.yml vendored Normal file
View File

@@ -0,0 +1,80 @@
---
name: "Build Snapshot"
on:
push:
workflow_dispatch:
pull_request:
branches: [main]
jobs:
build-snapshot:
runs-on: ubuntu-latest
if: github.ref_type != 'tag'
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up QEMU
id: qemu
uses: docker/setup-qemu-action@v3
with:
image: tonistiigi/binfmt:latest
platforms: arm64,arm
- name: Setup node
uses: actions/setup-node@v4
with:
cache: 'npm'
cache-dependency-path: webui.dev/package-lock.json
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version-file: 'go.mod'
cache: true
- name: Print go version
run: go version
- name: grpc
run: make -w grpc
- name: make daemon
run: make -w daemon-compile-x64-lin
- name: make webui
run: make -w webui-dist
- name: unit tests
run: make -w daemon-unittests
- name: integration tests
run: cd integration-tests && make -w
- name: goreleaser
uses: goreleaser/goreleaser-action@v6
with:
distribution: goreleaser
version: latest
args: release --snapshot --clean --parallelism 1 --skip=docker
- name: get date
run: |
echo "DATE=$(date +'%Y-%m-%d')" >> "$GITHUB_ENV"
- name: Archive binaries
uses: actions/upload-artifact@v4.3.1
with:
name: "OliveTin-snapshot-${{ env.DATE }}-${{ github.sha }}"
path: dist/OliveTin*.*
- name: Archive integration tests
uses: actions/upload-artifact@v4.3.1
with:
name: "OliveTin-integration-tests-${{ env.DATE }}-${{ github.sha }}"
path: |
integration-tests
!integration-tests/node_modules

80
.github/workflows/build-tag.yml vendored Normal file
View File

@@ -0,0 +1,80 @@
---
name: "Build Tag"
on:
push:
tags:
- '*'
jobs:
build-tag:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up QEMU
id: qemu
uses: docker/setup-qemu-action@v3
with:
image: tonistiigi/binfmt:latest
platforms: arm64,arm
- name: Setup node
uses: actions/setup-node@v4
with:
cache: 'npm'
cache-dependency-path: webui.dev/package-lock.json
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version-file: 'go.mod'
cache: true
- name: Print go version
run: go version
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_KEY }}
- name: Login to ghcr
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.CONTAINER_TOKEN }}
- name: grpc
run: make -w grpc
- name: make webui
run: make -w webui-dist
- name: goreleaser
uses: goreleaser/goreleaser-action@v6
with:
distribution: goreleaser
version: latest
args: release --clean --parallelism 1
env:
GITHUB_TOKEN: ${{ secrets.CONTAINER_TOKEN }}
- name: Archive binaries
uses: actions/upload-artifact@v4.3.1
with:
name: "OliveTin-${{ github.ref_name }}"
path: dist/OliveTin*.*
- name: Archive integration tests
uses: actions/upload-artifact@v4.3.1
with:
name: integration-tests
path: |
integration-tests
!integration-tests/node_modules

View File

@@ -1,3 +1,4 @@
---
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
@@ -13,10 +14,15 @@ name: "CodeQL"
on:
push:
branches: [ main ]
paths:
- 'cmd/**'
- 'internal/**'
- 'webui.dev/**'
- 'integration-tests/**'
- 'OliveTin.proto'
branches: [main]
pull_request:
# The branches below must be a subset of the branches above
branches: [ main ]
branches: [main]
schedule:
- cron: '25 10 * * 5'
@@ -32,40 +38,28 @@ jobs:
strategy:
fail-fast: false
matrix:
language: [ 'go', 'javascript' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
# Learn more:
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
language: ['go', 'javascript']
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Checkout repository
uses: actions/checkout@v4
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version-file: 'go.mod'
cache: true
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
#- name: Autobuild
# uses: github/codeql-action/autobuild@v1
- name: grpc
run: make -w grpc
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
with:
category: "/language:${{matrix.language}}"

View File

@@ -1,19 +1,37 @@
---
name: "Codestyle checks"
on: [push]
on:
push:
paths:
- 'cmd/**'
- 'internal/**'
- 'webui.dev/**'
- 'integration-tests/**'
- 'OliveTin.proto'
jobs:
codestyle:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
uses: actions/checkout@v4
- name: Setup Go
uses: actions/setup-go@v2
uses: actions/setup-go@v5
with:
go-version-file: 'go.mod'
cache: true
- name: Print go version
run: go version
- name: deps
run: make -w grpc
- name: daemon
run: make daemon-codestyle
run: make -w daemon-codestyle
- name: webui
run: make webui-codestyle
run: make -w webui-codestyle

34
.github/workflows/devskim.yml vendored Normal file
View File

@@ -0,0 +1,34 @@
# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.
name: DevSkim
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
schedule:
- cron: '34 21 * * 2'
jobs:
lint:
name: DevSkim
runs-on: ubuntu-20.04
permissions:
actions: read
contents: read
security-events: write
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Run DevSkim scanner
uses: microsoft/DevSkim-Action@v1
- name: Upload DevSkim scan results to GitHub Security tab
uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: devskim-results.sarif

View File

@@ -1,23 +0,0 @@
name: "Jenkins RC Build"
on:
push:
branches: [ main ]
workflow_dispatch:
inputs:
reason:
description: "Reason"
required: true
default: "no reason given"
jobs:
jenkins-trigger:
runs-on: ubuntu-latest
steps:
- name: Trigger jenkins job
uses: appleboy/jenkins-action@master
with:
url: ${{ secrets.JENKINS_URL }}
user: ${{ secrets.JENKINS_USER }}
token: ${{ secrets.JENKINS_TOKEN }}
job: "OliveTin/OliveTin-rc-builder"

14
.gitignore vendored
View File

@@ -1,11 +1,15 @@
webui/package-lock.json
webui/node_modules
**/*.swp
**/*.swo
gen/
OliveTin
OliveTin.armhf
OliveTin.exe
/OliveTin
/OliveTin.armhf
/OliveTin.exe
reports
releases/
dist/
installation-id.txt
tmp/
webui/
webui.dev/node_modules
webui.dev/.parcel-cache
custom-webui

View File

@@ -1,8 +1,9 @@
project_name: OliveTin
version: 2
before:
hooks:
- go mod tidy
- make grpc
- go mod download
builds:
- env:
- CGO_ENABLED=0
@@ -18,6 +19,8 @@ builds:
goarm:
- 5 # For old RPIs
- 6
- 7
main: cmd/OliveTin/main.go
@@ -32,38 +35,189 @@ builds:
goarch: arm64
ldflags:
- -s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{ .CommitDate }}
- -s -w -X main.version={{.Version}} -X main.commit={{.ShortCommit}} -X main.date={{ .CommitDate }}
checksum:
name_template: 'checksums.txt'
snapshot:
name_template: "{{ .Commit }}"
name_template: "{{ .Branch }}-{{ .ShortCommit }}"
changelog:
sort: asc
groups:
- title: 'Security'
regexp: '^.*?security(\([[:word:]]+\))??!?:.+$'
order: 0
- title: 'Features'
regexp: '^.*?feature(\([[:word:]]+\))??!?:.+$'
order: 1
- title: 'Bug fixes'
regexp: '^.*?bugfix(\([[:word:]]+\))??!?:.+$'
order: 2
- title: Others
order: 999
filters:
exclude:
- '^docs:'
- '^test:'
- '^cicd:'
- '^refactor:'
archives:
-
-
format: tar.gz
files:
- configs/config.yaml
files:
- config.yaml
- LICENSE
- README.md
- Dockerfile
- webui
- OliveTin.service
- ./var/
replacements:
darwin: macOS
name_template: "{{ .ProjectName }}-{{ .Version }}-{{ .Os }}-{{ .Arch }}"
name_template: "{{ .ProjectName }}-{{ .Os }}-{{ .Arch }}{{ .Arm }}"
wrap_in_directory: true
format_overrides:
- goos: windows
format: zip
dockers:
- image_templates:
- "docker.io/jamesread/olivetin:{{ .Tag }}-amd64"
- "ghcr.io/olivetin/olivetin:{{ .Tag }}-amd64"
dockerfile: Dockerfile
goos: linux
goarch: amd64
skip_push: false
build_flag_templates:
- "--platform=linux/amd64"
- "--label=org.opencontainers.image.revision={{.FullCommit}}"
- "--label=org.opencontainers.image.version={{.Tag}}"
extra_files:
- webui
- var/entities/
- config.yaml
- var/helper-actions/
- image_templates:
- "docker.io/jamesread/olivetin:{{ .Tag }}-arm64"
- "ghcr.io/olivetin/olivetin:{{ .Tag }}-arm64"
dockerfile: Dockerfile.arm64
goos: linux
goarch: arm64
skip_push: false
build_flag_templates:
- "--platform=linux/arm64"
- "--label=org.opencontainers.image.revision={{.FullCommit}}"
- "--label=org.opencontainers.image.version={{.Tag}}"
extra_files:
- webui
- var/entities/
- config.yaml
- var/helper-actions/
docker_manifests:
- name_template: docker.io/jamesread/olivetin:{{ .Version }}
image_templates:
- docker.io/jamesread/olivetin:{{ .Version }}-amd64
- docker.io/jamesread/olivetin:{{ .Version }}-arm64
- name_template: docker.io/jamesread/olivetin:latest
image_templates:
- docker.io/jamesread/olivetin:{{ .Version }}-amd64
- docker.io/jamesread/olivetin:{{ .Version }}-arm64
- name_template: ghcr.io/olivetin/olivetin:{{ .Version }}
image_templates:
- ghcr.io/olivetin/olivetin:{{ .Version }}-amd64
- ghcr.io/olivetin/olivetin:{{ .Version }}-arm64
- name_template: ghcr.io/olivetin/olivetin:latest
image_templates:
- ghcr.io/olivetin/olivetin:{{ .Version }}-amd64
- ghcr.io/olivetin/olivetin:{{ .Version }}-arm64
nfpms:
- id: default
maintainer: James Read <contact@jread.com>
description: OliveTin is a web interface for running Linux shell commands.
homepage: https://github.com/OliveTin/OliveTin
license: AGPL-3.0
formats:
- deb
- rpm
bindir: /usr/local/bin/
file_name_template: '{{ .PackageName }}_{{ .Os }}_{{ .Arch }}{{ with .Arm }}v{{ . }}{{ end }}{{ with .Mips }}_{{ . }}{{ end }}{{ if not (eq .Amd64 "v1") }}{{ .Amd64 }}{{ end }}'
contents:
- src: OliveTin.service
dst: /etc/systemd/system/OliveTin.service
- src: webui/*
dst: /var/www/olivetin/
- src: config.yaml
dst: /etc/OliveTin/config.yaml
type: "config|noreplace"
- src: var/entities/*
dst: /etc/OliveTin/entities/
type: "config|noreplace"
- src: var/manpage/OliveTin.1.gz
dst: /usr/share/man/man1/OliveTin.1.gz
- id: openrc
maintainer: James Read <contact@jread.com>
description: OliveTin is a web interface for running Linux shell commands.
homepage: https://github.com/OliveTin/OliveTin
license: AGPL-3.0
formats:
- apk
bindir: /usr/local/bin/
file_name_template: '{{ .PackageName }}_{{ .Os }}_{{ .Arch }}{{ with .Arm }}v{{ . }}{{ end }}{{ with .Mips }}_{{ . }}{{ end }}{{ if not (eq .Amd64 "v1") }}{{ .Amd64 }}{{ end }}'
contents:
- src: var/openrc/OliveTin
dst: /etc/init.d/OliveTin
- src: webui/*
dst: /var/www/olivetin/
- src: config.yaml
dst: /etc/OliveTin/config.yaml
type: "config|noreplace"
- src: var/entities/*
dst: /etc/OliveTin/entities/
type: "config|noreplace"
- src: var/manpage/OliveTin.1.gz
dst: /usr/share/man/man1/OliveTin.1.gz
release:
footer: |
## Container images (from GitHub)
- `docker pull ghcr.io/olivetin/olivetin:{{ .Version }}`
## Container images ([on Docker Hub](https://hub.docker.com/r/jamesread/olivetin/tags?page=1&ordering=last_updated))
- `docker pull docker.io/jamesread/olivetin:{{ .Version }}`
## Upgrade warnings, or breaking changes
- No such issues between the last release and this version.
## Useful links
- [Which download do I need?](https://docs.olivetin.app/choose-package.html)
- [Ask for help and chat with others users in the Discord community](https://discord.gg/jhYWWpNJ3v)
Thanks for your interest in OliveTin!

10
.pre-commit-config.yaml Normal file
View File

@@ -0,0 +1,10 @@
# See https://pre-commit.com for more information
# See https://pre-commit.com/hooks.html for more hooks
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v3.2.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- id: check-added-large-files

View File

@@ -7,9 +7,9 @@ of multiple projects, your time and interest in contributing is most welcome.
If you're not sure where to get started, raise an issue in the project.
Ideas may be discussed, purely on their merits and issues. Our Code of Conduct
(CoC) is straightforward - it's important that contributors feel comfortable in
discussion throughout the whole process. This project respects the
link:https://www.kernel.org/doc/html/latest/process/code-of-conduct.html[Linux Kernel code of conduct].
(CoC) is straightforward - it's important that contributors feel comfortable in
discussion throughout the whole process. This project respects the
link:https://www.kernel.org/doc/html/latest/process/code-of-conduct.html[Linux Kernel code of conduct].
== If you're not sure, ask!
@@ -18,33 +18,39 @@ contribution. If you're thinking about a bigger change, especially that might
affect the core working or architecture, it's almost essential to talk and ask
about what you're planning might affect things. Some of the larger future plans may not be
documented well so it's difficult to understand how your change might affect
the general direction and roadmap of this project without asking.
the general direction and roadmap of this project without asking.
The preferred way to communicate is probably via Discord or GitHub issues.
The preferred way to communicate is probably via Discord or GitHub issues.
=== Dev environment setup and clean build - Fedora
=== Dev environment setup and clean build
```
dnf install git go protobuf-compiler make -y
# Step1: setup compile env
# - Fedora
dnf install git go protobuf-compiler make -y
# - Windows with chocolatey
choco install git go protoc make python nodejs-lts -y
# Step2: clone and setup repo
git clone https://github.com/OliveTin/OliveTin.git
cd OliveTin
make githooks
# For each dependency in tools.go;
go install "github.com/bufbuild/buf/cmd/buf"
go install ...
buf generate
# Step3: compile binary for current dev env (OS, ARCH)
# `make grpc` will also run `make go-tools`, which installs "buf". This binary
# will be put in your GOPATH/bin/, which should be on your path. buf is used to
# generate the protobuf / grpc stubs.
make grpc
make
./OliveTin
```
=== Getting started to contribute;
The project layout is reasonably straightforward;
The project layout is reasonably straightforward;
* See the `Makefile` for common targets. This project was originally created on top of Fedora, but it should be usable on Debian/your faveourite distro with minor changes (if any).
* The API is defined in protobuf+grpc - you will need to `make grpc`.
* The API is defined in protobuf+grpc - you will need to `make grpc`.
* The Go daemon is built from the `cmd` and `internal` directories mostly.
* The webui is just a single page application with a bit of Javascript in the `webui` directory. This can happily be hosted on another webserver.

View File

@@ -1,21 +1,31 @@
FROM fedora
FROM --platform=linux/amd64 registry.fedoraproject.org/fedora-minimal:40-x86_64
RUN useradd -rm olivetin -u 1000
LABEL org.opencontainers.image.source https://github.com/OliveTin/OliveTin
LABEL org.opencontainers.image.title=OliveTin
RUN mkdir -p /config /var/www/olivetin/ && \
dnf install -y \
RUN mkdir -p /config /config/entities/ /var/www/olivetin \
&& \
microdnf install -y --nodocs --noplugins --setopt=keepcache=0 --setopt=install_weak_deps=0 \
iputils \
openssh-clients \
shadow-utils \
apprise \
jq \
git \
docker \
&& dnf clean all && \
rm -rf /var/cache/yum # install ping
&& microdnf clean all
EXPOSE 1337/tcp
RUN useradd --system --create-home olivetin -u 1000
EXPOSE 1337/tcp
COPY config.yaml /config
COPY var/entities/* /config/entities/
VOLUME /config
COPY OliveTin /usr/bin/OliveTin
COPY webui /var/www/olivetin/
COPY var/helper-actions/* /usr/bin/
USER olivetin

32
Dockerfile.arm64 Normal file
View File

@@ -0,0 +1,32 @@
FROM --platform=linux/arm64 registry.fedoraproject.org/fedora-minimal:40-aarch64
LABEL org.opencontainers.image.source https://github.com/OliveTin/OliveTin
LABEL org.opencontainers.image.title=OliveTin
RUN mkdir -p /config /config/entities/ /var/www/olivetin \
&& \
microdnf install -y --nodocs --noplugins --setopt=keepcache=0 --setopt=install_weak_deps=0 \
iputils \
openssh-clients \
shadow-utils \
apprise \
jq \
git \
docker \
&& microdnf clean all
RUN useradd --system --create-home olivetin -u 1000
EXPOSE 1337/tcp
COPY config.yaml /config
COPY var/entities/* /config/entities/
VOLUME /config
COPY OliveTin /usr/bin/OliveTin
COPY webui /var/www/olivetin/
COPY var/helper-actions/* /usr/bin/
USER olivetin
ENTRYPOINT [ "/usr/bin/OliveTin" ]

27
Dockerfile.armv7 Normal file
View File

@@ -0,0 +1,27 @@
FROM --platform=linux/armhfp registry.fedoraproject.org/fedora-minimal:36-armhfp
LABEL org.opencontainers.image.source https://github.com/OliveTin/OliveTin
LABEL org.opencontainers.image.title=OliveTin
RUN mkdir -p /config /config/entities /var/www/olivetin \
&& \
microdnf install -y --nodocs --noplugins --setopt=keepcache=0 --setopt=install_weak_deps=0 \
iputils \
shadow-utils \
openssh-clients
RUN useradd --system --create-home olivetin -u 1000
EXPOSE 1337/tcp
COPY config.yaml /config
COPY var/entities/* /config/entities/
VOLUME /config
COPY OliveTin /usr/bin/OliveTin
COPY webui /var/www/olivetin/
COPY var/helper-actions/* /usr/bin/
USER olivetin
ENTRYPOINT [ "/usr/bin/OliveTin" ]

50
Jenkinsfile vendored
View File

@@ -1,50 +0,0 @@
pipeline {
agent any
options {
skipDefaultCheckout(true)
}
stages {
stage ('Pre-Build') {
steps {
cleanWs()
checkout scm
sh 'make go-tools'
}
}
stage('Compile') {
steps {
withEnv(["PATH+GO=/root/go/bin/"]) {
sh 'go env'
sh 'echo $PATH'
sh 'buf generate'
sh 'make daemon-compile'
}
}
}
stage ('Post-Compile') {
parallel {
stage('Codestyle') {
steps {
withEnv(["PATH+GO=/root/go/bin/"]) {
sh 'make daemon-codestyle'
sh 'make webui-codestyle'
}
}
}
stage('UnitTests') {
steps {
withEnv(["PATH+GO=/root/go/bin/"]) {
sh 'make daemon-unittests'
}
}
}
}
}
}
}

View File

@@ -1,46 +1,79 @@
compile: daemon-compile-x64-lin
define delete-files
python -c "import shutil;shutil.rmtree('$(1)', ignore_errors=True)"
endef
daemon-compile-armhf:
GOARCH=arm GOARM=6 go build -o OliveTin.armhf github.com/jamesread/OliveTin/cmd/OliveTin
compile: daemon-compile-currentenv
daemon-compile-x64-lin:
GOOS=linux go build -o OliveTin github.com/jamesread/OliveTin/cmd/OliveTin
daemon-compile-currentenv:
go build github.com/OliveTin/OliveTin/cmd/OliveTin
daemon-compile-armhf:
go env -w GOARCH=arm GOARM=6
go build -o OliveTin.armhf github.com/OliveTin/OliveTin/cmd/OliveTin
go env -u GOARCH GOARM
daemon-compile-x64-lin:
go env -w GOOS=linux
go build -o OliveTin github.com/OliveTin/OliveTin/cmd/OliveTin
go env -u GOOS
daemon-compile-x64-win:
GOOS=windows GOARCH=amd64 go build -o OliveTin.exe github.com/jamesread/OliveTin/cmd/OliveTin
go env -w GOOS=windows GOARCH=amd64
go build -o OliveTin.exe github.com/OliveTin/OliveTin/cmd/OliveTin
go env -u GOOS GOARCH
daemon-compile: daemon-compile-armhf daemon-compile-x64-lin daemon-compile-x64-win
daemon-codestyle:
go fmt ./...
go vet ./...
gocyclo -over 4 cmd internal
gocyclo -over 4 cmd internal
gocritic check ./...
daemon-unittests:
mkdir -p reports
$(call delete-files,reports)
mkdir reports
go test ./... -coverprofile reports/unittests.out
go tool cover -html=reports/unittests.out -o reports/unittests.html
it:
cd integration-tests && make
githooks:
git config --local core.hooksPath .githooks
go-tools:
go install "github.com/bufbuild/buf/cmd/buf"
go install "github.com/fzipp/gocyclo/cmd/gocyclo"
go install "github.com/go-critic/go-critic/cmd/gocritic"
go install "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway"
go install "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2"
go install "google.golang.org/grpc/cmd/protoc-gen-go-grpc"
go install "google.golang.org/protobuf/cmd/protoc-gen-go"
go install "github.com/fzipp/gocyclo/cmd/gocyclo"
grpc: go-tools
buf generate
dist: protoc
protoc:
protoc --go_out=. --go-grpc_out=. --grpc-gateway_out=. -I .:/usr/include/ OliveTin.proto
podman-image:
buildah bud -t olivetin
podman-container:
podman kill olivetin
podman rm olivetin
podman kill olivetin || true
podman rm olivetin || true
podman create --name olivetin -p 1337:1337 -v /etc/OliveTin/:/config:ro olivetin
podman start olivetin
integration-tests-docker-image:
docker rm -f olivetin && docker rmi -f olivetin
docker build -t olivetin:latest .
docker create --name olivetin -p 1337:1337 -v `pwd`/integration-tests/configs/:/config/ olivetin
devrun: compile
killall OliveTin || true
./OliveTin &
@@ -48,11 +81,24 @@ devrun: compile
devcontainer: compile podman-image podman-container
webui-codestyle:
cd webui && npm install
cd webui && ./node_modules/.bin/eslint main.js js/*
cd webui && ./node_modules/.bin/stylelint style.css
cd webui.dev && npm install
cd webui.dev && npx eslint main.js js/*
cd webui.dev && npx stylelint style.css
webui-dist:
$(call delete-files,webui)
$(call delete-files,webui.dev/dist)
cd webui.dev && npm install
cd webui.dev && npx parcel build --public-url "."
python -c "import shutil;shutil.move('webui.dev/dist', 'webui')"
python -c "import shutil;import glob;[shutil.copy(f, 'webui') for f in glob.glob('webui.dev/*.png')]"
clean:
rm -rf dist OliveTin OliveTin.armhf OliveTin.exe reports gen
$(call delete-files,dist)
$(call delete-files,OliveTin)
$(call delete-files,OliveTin.armhf)
$(call delete-files,OliveTin.exe)
$(call delete-files,reports)
$(call delete-files,gen)
.PHONY: grpc
.PHONY: grpc

View File

@@ -3,23 +3,28 @@ syntax = "proto3";
option go_package = "gen/grpc";
import "google/api/annotations.proto";
import "google/api/httpbody.proto";
message Action {
string id = 1;
string title = 2;
string icon = 3;
bool canExec = 4;
bool can_exec = 4;
repeated ActionArgument arguments = 5;
string popup_on_start = 6;
int32 order = 7;
}
message ActionArgument {
string name = 1;
string title = 2;
string title = 2;
string type = 3;
string defaultValue = 4;
string default_value = 4;
repeated ActionArgumentChoice choices = 5;
string description = 6;
map<string, string> suggestions = 7;
}
message ActionArgumentChoice {
@@ -35,16 +40,31 @@ message Entity {
message GetDashboardComponentsResponse {
string title = 1;
repeated Action actions = 2;
repeated Entity entities = 3;
repeated DashboardComponent dashboards = 4;
string authenticated_user = 5;
string authenticated_user_provider = 6;
}
message GetDashboardComponentsRequest {}
message DashboardComponent {
string title = 1;
string type = 2;
repeated DashboardComponent contents = 3;
string icon = 4;
string css_class = 5;
}
message StartActionRequest {
string actionName = 1;
string action_id = 1;
repeated StartActionArgument arguments = 2;
string unique_tracking_id = 3;
}
message StartActionArgument {
@@ -53,21 +73,51 @@ message StartActionArgument {
}
message StartActionResponse {
LogEntry logEntry = 1;
string execution_tracking_id = 2;
}
message StartActionAndWaitRequest {
string action_id = 1;
}
message StartActionAndWaitResponse {
LogEntry log_entry = 1;
}
message StartActionByGetRequest {
string action_id = 1;
}
message StartActionByGetResponse {
string execution_tracking_id = 2;
}
message StartActionByGetAndWaitRequest {
string action_id = 1;
}
message StartActionByGetAndWaitResponse {
LogEntry log_entry = 1;
}
message GetLogsRequest{};
message LogEntry {
string datetime = 1;
string actionTitle = 2;
string stdout = 3;
string stderr = 4;
bool timedOut = 5;
int32 exitCode = 6;
string datetime_started = 1;
string action_title = 2;
string output = 3;
bool timed_out = 5;
int32 exit_code = 6;
string user = 7;
string userClass = 8;
string actionIcon = 9;
string user_class = 8;
string action_icon = 9;
repeated string tags = 10;
string execution_tracking_id = 11;
string datetime_finished = 12;
string action_id = 13;
bool execution_started = 14;
bool execution_finished = 15;
bool blocked = 16;
}
message GetLogsResponse {
@@ -84,7 +134,101 @@ message ValidateArgumentTypeResponse {
string description = 2;
}
service OliveTinApi {
message WatchExecutionRequest {
string execution_tracking_id = 1;
}
message WatchExecutionUpdate {
string update = 1;
}
message ExecutionStatusRequest {
string execution_tracking_id = 1;
string action_id = 2;
}
message ExecutionStatusResponse {
LogEntry log_entry = 1;
}
message WhoAmIRequest {}
message WhoAmIResponse {
string authenticated_user = 1;
}
message SosReportRequest {}
message SosReportResponse {
string alert = 1;
}
message DumpVarsRequest {}
message DumpVarsResponse {
string alert = 1;
map<string, string> contents = 2;
}
message ActionEntityPair {
string action_title = 1;
string entity_prefix = 2;
}
message DumpPublicIdActionMapRequest {}
message DumpPublicIdActionMapResponse {
string alert = 1;
map<string, ActionEntityPair> contents = 2;
}
message GetReadyzRequest {}
message GetReadyzResponse {
string status = 1;
}
message EventOutputChunk {
string execution_tracking_id = 1;
string output = 2;
}
message EventEntityChanged {}
message EventConfigChanged {}
message EventExecutionFinished {
LogEntry log_entry = 1;
}
message KillActionRequest {
string execution_tracking_id = 1;
}
message KillActionResponse {
string execution_tracking_id = 1;
bool killed = 2;
bool already_completed = 3;
bool found = 4;
}
message LocalUserLoginRequest {
string username = 1;
string password = 2;
}
message LocalUserLoginResponse {
bool success = 1;
}
message PasswordHashRequest {
string password = 1;
}
message PasswordHashResponse {
}
message LogoutRequest {}
service OliveTinApiService {
rpc GetDashboardComponents(GetDashboardComponentsRequest) returns (GetDashboardComponentsResponse) {
option (google.api.http) = {
get: "/api/GetDashboardComponents"
@@ -98,6 +242,39 @@ service OliveTinApi {
};
}
rpc StartActionAndWait(StartActionAndWaitRequest) returns (StartActionAndWaitResponse) {
option (google.api.http) = {
post: "/api/StartActionAndWait"
body: "*"
};
}
rpc StartActionByGet(StartActionByGetRequest) returns (StartActionByGetResponse) {
option (google.api.http) = {
get: "/api/StartActionByGet/{action_id}"
};
}
rpc StartActionByGetAndWait(StartActionByGetAndWaitRequest) returns (StartActionByGetAndWaitResponse) {
option (google.api.http) = {
get: "/api/StartActionByGetAndWait/{action_id}"
};
}
rpc KillAction(KillActionRequest) returns (KillActionResponse) {
option (google.api.http) = {
post: "/api/KillAction"
body: "*"
};
}
rpc ExecutionStatus(ExecutionStatusRequest) returns (ExecutionStatusResponse) {
option (google.api.http) = {
post: "/api/ExecutionStatus"
body: "*"
};
}
rpc GetLogs(GetLogsRequest) returns (GetLogsResponse) {
option (google.api.http) = {
get: "/api/GetLogs"
@@ -110,4 +287,54 @@ service OliveTinApi {
body: "*"
};
}
rpc WhoAmI(WhoAmIRequest) returns (WhoAmIResponse) {
option (google.api.http) = {
get: "/api/WhoAmI"
};
}
rpc SosReport(SosReportRequest) returns (google.api.HttpBody) {
option (google.api.http) = {
get: "/api/sosreport"
};
}
rpc DumpVars(DumpVarsRequest) returns (DumpVarsResponse) {
option (google.api.http) = {
get: "/api/DumpVars"
};
}
rpc DumpPublicIdActionMap(DumpPublicIdActionMapRequest) returns (DumpPublicIdActionMapResponse) {
option (google.api.http) = {
get: "/api/DumpActionMap"
};
}
rpc GetReadyz(GetReadyzRequest) returns (GetReadyzResponse) {
option (google.api.http) = {
get: "/api/readyz"
};
}
rpc LocalUserLogin(LocalUserLoginRequest) returns (LocalUserLoginResponse) {
option (google.api.http) = {
post: "/api/LocalUserLogin"
body: "*"
};
}
rpc PasswordHash(PasswordHashRequest) returns (google.api.HttpBody) {
option (google.api.http) = {
post: "/api/PasswordHash"
body: "*"
};
}
rpc Logout(LogoutRequest) returns (google.api.HttpBody) {
option (google.api.http) = {
get: "/api/Logout"
};
}
}

103
README.md
View File

@@ -1,32 +1,46 @@
# OliveTin
<img alt = "project logo" src = "https://github.com/OliveTin/OliveTin/blob/main/webui/OliveTinLogo.png" align = "right" width = "160px" />
<img alt = "project logo" src = "https://github.com/OliveTin/OliveTin/blob/main/webui.dev/OliveTinLogo.png" align = "right" width = "160px" />
OliveTin is a web interface for running Linux shell commands.
OliveTin gives **safe** and **simple** access to predefined shell commands from a web interface.
[![Discord](https://img.shields.io/discord/846737624960860180?label=Discord%20Server)](https://discord.gg/jhYWWpNJ3v)
[![Awesome](https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg)](https://github.com/awesome-selfhosted/awesome-selfhosted#automation)
[![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/5050/badge)](https://bestpractices.coreinfrastructure.org/projects/5050)
[![Go Report Card](https://goreportcard.com/badge/github.com/Olivetin/OliveTin)](https://goreportcard.com/report/github.com/OliveTin/OliveTin)
[![Build Snapshot](https://github.com/OliveTin/OliveTin/actions/workflows/build-snapshot.yml/badge.svg)](https://github.com/OliveTin/OliveTin/actions/workflows/build-snapshot.yml)
<img alt = "screenshot" src = "https://github.com/OliveTin/OliveTin/blob/main/var/marketing/screenshotDesktop.png" />
<a href = "#screenshots">More screenshots below</a>
Some example **use cases**;
## Use cases
1. Give controlled access to run shell commands to less technical folks who cannot be trusted with SSH. I use this so my family can `podman restart plex` without asking me, and without giving them shell access!
2. Great for home automation tablets stuck on walls around your house - I use this to turn Hue lights on and off for example.
3. Sometimes SSH access isn't possible to a server, or you are feeling too lazy to type a long command you run regularly! I use this to send Wake on Lan commands to servers around my house.
**Safely** give access to commands, for less technical people;
[Join the community on Discord.](https://discord.gg/jhYWWpNJ3v)
* eg: Give your family a button to `podman restart plex`
* eg: Give junior admins a simple web form with dropdowns, to start your custom script. `backupScript.sh --folder {{ customerName }}`
* eg: Enable SSH access to the server for the next 20 mins `firewall-cmd --add-service ssh --timeout 20m`
## YouTube video demo (6 mins)
**Simplify** complex commands, make them accessible and repeatable;
[![6 minute demo video](https://img.youtube.com/vi/Ej6NM9rmZtk/0.jpg)](https://www.youtube.com/watch?v=Ej6NM9rmZtk)
* eg: Expose complex commands on touchscreen tablets stuck on walls around your house. `wake-on-lan aa:bb:cc:11:22:33`
* eg: Run long-lived commands on your servers from your cell phone. `dnf update -y`
* eg: Define complex commands with lots of preset arguments, and turn a few arguments into dropdown select boxes. `docker rm {{ container }} && docker create {{ container }} && docker start {{ container }}`
[Join the community on Discord](https://discord.gg/jhYWWpNJ3v) to talk with other users about use cases, or to ask for support in getting started.
## YouTube demo video
[![YouTube demo video](https://raw.githubusercontent.com/OliveTin/OliveTin/main/var/marketing/YouTubeBanner.png)](https://www.youtube.com/watch?v=UBgOfNrzId4)
## Features
* **Responsive, touch-friendly UI** - great for tablets and mobile
* **Super simple config in YAML** - because if it's not YAML now-a-days, it's not "cloud native" :-)
* **Super simple config in YAML** - because if it's not YAML now-a-days, it's not "cloud native" :-)
* **Dark mode** - for those of you that roll that way.
* **Accessible** - passes all the accessibility checks in Firefox, and issues with accessibility are taken seriously.
* **Container** - available for quickly testing and getting it up and running, great for the selfhosted community.
* **Accessible** - passes all the accessibility checks in Firefox, and issues with accessibility are taken seriously.
* **Container** - available for quickly testing and getting it up and running, great for the selfhosted community.
* **Integrate with anything** - OliveTin just runs Linux shell commands, so theoretially you could integrate with a bunch of stuff just by using curl, ping, etc. However, writing your own shell scripts is a great way to extend OliveTin.
* **Lightweight on resources** - uses only a few MB of RAM and barely any CPU. Written in Go, with a web interface written as a modern, responsive, Single Page App that uses the REST/gRPC API.
* **Good amount of unit tests and style checks** - helps potential contributors be consistent, and helps with maintainability.
@@ -35,51 +49,74 @@ Some example **use cases**;
Desktop web browser;
![Desktop screenshot](media/screenshotDesktop.png)
<p align = "center">
<img alt = "screenshot" src = "https://github.com/OliveTin/OliveTin/blob/main/var/marketing/screenshotDesktop.png" />
</p>
Desktop web browser (dark mode);
Desktop web browser (dark mode);
![Desktop screenshot](media/screenshotDesktopDark.png)
<p align = "center">
<img alt = "screenshot" src = "https://github.com/OliveTin/OliveTin/blob/main/var/marketing/screenshotDesktopDark.png" />
</p>
Mobile screen size (responsive layout);
Mobile screen size (responsive layout);
![Mobile screenshot](media/screenshotMobile.png)
<p align = "center">
<img alt = "screenshot" src = "https://github.com/OliveTin/OliveTin/blob/main/var/marketing/screenshotMobile.png" style = "height: 700px;" />
</p>
## Documentation
All documentation can be found at http://docs.olivetin.app . This includes installation and usage guide, etc.
All documentation can be found at http://docs.olivetin.app . This includes installation and usage guide, etc.
### Quickstart reference for `config.yaml`
This is a quick example of `config.yaml` - but again, lots of documentation for how to write your `config.yaml` can be found at [the documentation site.](https://docs.olivetin.app)
* (Recommended) [Linux package install (.rpm/.deb)](https://docs.olivetin.app/install-linuxpackage.html) install instructions
* [Container (podman/docker)](https://docs.olivetin.app/install-container.html) install instructions
* [Docker compose](https://docs.olivetin.app/install-compose.html) install instructions
* [Helm on Kubernetes](https://docs.olivetin.app/install-helm.html) install instructions
* [Kubernetes (manual)](https://docs.olivetin.app/install-k8s.html) install instructions
* [.tar.gz (manual)](https://docs.olivetin.app/install-targz.html) install instructions
Put this `config.yaml` in `/etc/OliveTin/` if you're running a standard service, or mount it at `/config` if running in a container.
```yaml
# Listen on all addresses available, port 1337
listenAddressSingleHTTPFrontend: 0.0.0.0:1337
listenAddressSingleHTTPFrontend: 0.0.0.0:1337
# Choose from INFO (default), WARN and DEBUG
logLevel: "INFO"
# Actions (buttons) to show up on the WebUI:
actions:
# Docs: https://docs.olivetin.app/action-container-control.html
actions:
# Docs: https://docs.olivetin.app/action-container-control.html
- title: Restart Plex
icon: smile
icon: restart
shell: docker restart plex
# This will send 1 ping
# This will send 1 ping
# Docs: https://docs.olivetin.app/action-ping.html
- title: Ping Google.com
shell: ping google.com -c 1
# Restart lightdm on host "overseer"
- title: Ping host
shell: ping {{ host }} -c {{ count }}
icon: ping
arguments:
- name: host
title: host
type: ascii_identifier
default: example.com
- name: count
title: Count
type: int
default: 1
# Restart http on host "webserver1"
# Docs: https://docs.olivetin.app/action-ssh.html
- title: restart lightdm
icon: poop
shell: ssh root@overseer 'service lightdm restart'
- title: restart httpd
icon: restart
shell: ssh root@webserver1 'service httpd restart'
```
A full example config can be found at in this repository - [config.yaml](https://github.com/OliveTin/OliveTin/blob/main/var/config.yaml).
A full example config can be found at in this repository - [config.yaml](https://github.com/OliveTin/OliveTin/blob/main/config.yaml).

View File

@@ -2,7 +2,7 @@
## Supported Versions
Currently, only the `main` branch is "supported".
Currently, only the `main` branch is "supported".
| Version | Supported |
| ------- | ------------------ |
@@ -10,4 +10,4 @@ Currently, only the `main` branch is "supported".
## Reporting a Vulnerability
Please email `contact@jread.com` for responsible disclosure. Accepted issues will be made public once patched, and you will be given credit.
Please email `contact@jread.com` for responsible disclosure. Accepted issues will be made public once patched, and you will be given credit.

View File

@@ -11,3 +11,9 @@ plugins:
- name: grpc-gateway
out: gen/grpc/
opt: paths=source_relative
# - name: swagger
# out: reports/swagger
# - name: openapiv2
# out: reports/openapiv2

View File

@@ -4,7 +4,4 @@ deps:
- remote: buf.build
owner: googleapis
repository: googleapis
branch: main
commit: d1a849b8f8304950832335723096e954
digest: b1-zJkwX0YeOp1Wa0Jaj_RqMLa2-oEzePH6PJEK8aaMeI4=
create_time: 2021-08-26T15:07:19.652533Z
commit: e9fcfb66f77242e5b8fd4564d7a01033

View File

@@ -1,5 +1,5 @@
version: v1
deps:
deps:
- buf.build/googleapis/googleapis
lint:
use:

View File

@@ -1,17 +1,27 @@
package main
import (
"flag"
log "github.com/sirupsen/logrus"
grpcapi "github.com/jamesread/OliveTin/internal/grpcapi"
updatecheck "github.com/jamesread/OliveTin/internal/updatecheck"
"github.com/jamesread/OliveTin/internal/httpservers"
"github.com/OliveTin/OliveTin/internal/entityfiles"
"github.com/OliveTin/OliveTin/internal/executor"
grpcapi "github.com/OliveTin/OliveTin/internal/grpcapi"
"github.com/OliveTin/OliveTin/internal/httpservers"
"github.com/OliveTin/OliveTin/internal/installationinfo"
"github.com/OliveTin/OliveTin/internal/oncalendarfile"
"github.com/OliveTin/OliveTin/internal/oncron"
"github.com/OliveTin/OliveTin/internal/onfileindir"
"github.com/OliveTin/OliveTin/internal/onstartup"
updatecheck "github.com/OliveTin/OliveTin/internal/updatecheck"
"github.com/OliveTin/OliveTin/internal/websocket"
config "github.com/OliveTin/OliveTin/internal/config"
"github.com/fsnotify/fsnotify"
config "github.com/jamesread/OliveTin/internal/config"
"github.com/spf13/viper"
"os"
"strconv"
)
var (
@@ -22,18 +32,82 @@ var (
)
func init() {
initLog()
initViperConfig(initCliFlags())
initCheckEnvironment()
initInstallationInfo()
log.Info("OliveTin initialization complete")
}
func initLog() {
log.SetFormatter(&log.TextFormatter{
ForceQuote: true,
DisableTimestamp: true,
})
// Use debug this early on to catch details about startup errors. The
// default config will raise the log level later, if not set.
log.SetLevel(log.DebugLevel) // Default to debug, to catch cfg issue
}
func initCliFlags() string {
var configDir string
flag.StringVar(&configDir, "configdir", ".", "Config directory path")
var printVersion bool
flag.BoolVar(&printVersion, "version", false, "Prints the version number and exits")
flag.Parse()
// This log message should be the first log message OliveTin prints.
if printVersion {
logStartupMessage("OliveTin is just printing the startup message")
os.Exit(1)
} else {
logStartupMessage("OliveTin initializing")
}
log.WithFields(log.Fields{
"version": version,
"commit": commit,
"date": date,
}).Info("OliveTin initializing")
"value": configDir,
}).Debugf("Value of -configdir flag")
log.SetLevel(log.DebugLevel) // Default to debug, to catch cfg issues
return configDir
}
func getBasePort() int {
var err error
defaultPort := 1337
basePort := defaultPort
envPort := os.Getenv("PORT")
if envPort != "" {
basePort, err = strconv.Atoi(os.Getenv("PORT"))
if err != nil {
log.Errorf("Error converting port to int. %s", err)
os.Exit(1)
}
}
if defaultPort != basePort {
log.WithFields(log.Fields{
"basePort": basePort,
}).Debug("Base port")
}
return basePort
}
func initViperConfig(configDir string) {
viper.AutomaticEnv()
viper.SetConfigName("config.yaml")
viper.SetConfigType("yaml")
viper.AddConfigPath(".")
viper.AddConfigPath(configDir)
viper.AddConfigPath("/config") // For containers.
viper.AddConfigPath("/etc/OliveTin/")
@@ -42,38 +116,69 @@ func init() {
os.Exit(1)
}
cfg = config.DefaultConfig()
cfg = config.DefaultConfigWithBasePort(getBasePort())
viper.WatchConfig()
viper.OnConfigChange(func(e fsnotify.Event) {
if e.Op == fsnotify.Write {
log.Info("Config file changed:", e.String())
reloadConfig()
config.Reload(cfg)
}
})
reloadConfig()
log.Info("Init complete")
config.Reload(cfg)
}
func reloadConfig() {
if err := viper.UnmarshalExact(&cfg); err != nil {
log.Errorf("Config unmarshal error %+v", err)
os.Exit(1)
}
func initInstallationInfo() {
installationinfo.Config = cfg
installationinfo.Build.Version = version
installationinfo.Build.Commit = commit
installationinfo.Build.Date = date
}
config.Sanitize(cfg)
func logStartupMessage(message string) {
log.WithFields(log.Fields{
"version": version,
"commit": commit,
"date": date,
}).Info(message)
}
func initCheckEnvironment() {
warnIfPuidGuid()
}
func warnIfPuidGuid() {
if os.Getenv("PUID") != "" || os.Getenv("PGID") != "" {
log.Warnf("PUID or PGID seem to be set to something, but they are ignored by OliveTin. Please check https://docs.olivetin.app/no-puid-pgid.html")
}
}
func main() {
log.Info("OliveTin started")
log.WithFields(log.Fields{
"configDir": cfg.GetDir(),
}).Infof("OliveTin started")
log.Debugf("Config: %+v", cfg)
go updatecheck.StartUpdateChecker(version, commit, cfg)
executor := executor.DefaultExecutor(cfg)
executor.RebuildActionMap()
executor.AddListener(websocket.ExecutionListener)
config.AddListener(executor.RebuildActionMap)
go grpcapi.Start(cfg)
go onstartup.Execute(cfg, executor)
go oncron.Schedule(cfg, executor)
go onfileindir.WatchFilesInDirectory(cfg, executor)
go oncalendarfile.Schedule(cfg, executor)
entityfiles.AddListener(websocket.OnEntityChanged)
entityfiles.AddListener(executor.RebuildActionMap)
go entityfiles.SetupEntityFileWatchers(cfg)
go updatecheck.StartUpdateChecker(cfg)
go grpcapi.Start(cfg, executor)
httpservers.StartServers(cfg)
}

303
config.yaml Normal file
View File

@@ -0,0 +1,303 @@
# There is a built-in micro proxy that will host the webui and REST API all on
# one port (this is called the "Single HTTP Frontend") and means you just need
# one open port in the container/firewalls/etc.
#
# Listen on all addresses available, port 1337
listenAddressSingleHTTPFrontend: 0.0.0.0:1337
# Choose from INFO (default), WARN and DEBUG
logLevel: "INFO"
# Checking for updates https://docs.olivetin.app/update-checks.html
checkForUpdates: false
# Actions are commands that are executed by OliveTin, and normally show up as
# buttons on the WebUI.
#
# Docs: https://docs.olivetin.app/create-your-first-action.html
actions:
# This is the most simple action, it just runs the command and flashes the
# button to indicate status.
#
# If you are running OliveTin in a container remember to pass through the
# docker socket! https://docs.olivetin.app/action-container-control.html
- title: Ping the Internet
shell: ping -c 3 1.1.1.1
icon: ping
popupOnStart: execution-dialog-stdout-only
# This uses `popupOnStart: execution-dialog-stdout-only` to simply show just
# the command output.
- title: Check disk space
icon: disk
shell: df -h /media
popupOnStart: execution-dialog-stdout-only
# This uses `popupOnStart: execution-dialog` to show a dialog with more
# information about the command that was run.
- title: check dmesg logs
shell: dmesg | tail
icon: logs
popupOnStart: execution-dialog
# This uses `popupOnStart: execution-button` to display a mini button that
# links to the logs.
- title: date
shell: date
timeout: 6
icon: clock
popupOnStart: execution-button
# You are not limited to operating system commands, and of course you can run
# your own scripts. Here `maxConcurrent` stops the script running multiple
# times in parallel. There is also a timeout that will kill the command if it
# runs for too long.
- title: Run backup script
shell: /opt/backupScript.sh
shellAfterCompleted: "apprise -t 'Notification: Backup script completed' -b 'The backup script completed with code {{ exitCode}}. The log is: \n {{ output }} '"
maxConcurrent: 1
timeout: 10
icon: backup
popupOnStart: execution-dialog
# When you want to prompt users for input, that is when you should use
# `arguments` - this presents a popup dialog and asks for argument values.
#
# Docs: https://docs.olivetin.app/action-ping.html
- title: Ping host
shell: ping {{ host }} -c {{ count }}
icon: ping
timeout: 100
popupOnStart: execution-dialog-stdout-only
arguments:
- name: host
title: Host
type: ascii_identifier
default: example.com
description: The host that you want to ping
- name: count
title: Count
type: int
default: 3
description: How many times to do you want to ping?
# OliveTin can control containers - docker is just a command line app.
#
# However, if you are running in a container you will need to do some setup,
# see the docs below.
#
# Docs: https://docs.olivetin.app/action-container-control.html
- title: Restart Docker Container
icon: restart
shell: docker restart {{ container }}
arguments:
- name: container
title: Container name
choices:
- value: plex
- value: traefik
- value: grafana
# There is a special `confirmation` argument to help against accidental clicks
# on "dangerous" actions.
#
# Docs: https://docs.olivetin.app/confirmation.html
- title: Delete old backups
icon: ashtonished
shell: rm -rf /opt/oldBackups/
arguments:
- type: confirmation
title: Are you sure?!
# This is an action that runs a script included with OliveTin, that will
# download themes. You will still need to set theme "themeName" in your config.
#
# Docs: https://docs.olivetin.app/themes.html
- title: Get OliveTin Theme
shell: olivetin-get-theme {{ themeGitRepo }} {{ themeFolderName }}
icon: theme
arguments:
- name: themeGitRepo
title: Theme's Git Repository
description: Find new themes at https://olivetin.app/themes
type: url
- name: themeFolderName
title: Theme's Folder Name
type: ascii_identifier
# Sometimes you want to run actions on other servers - don't overcomplicate
# it, just use SSH! OliveTin includes a helper to make this easier, which is
# entirely optional. You can also setup SSH manually.
#
# Docs: https://docs.olivetin.app/action-ssh-easy.html
# Docs: https://docs.olivetin.app/action-ssh.html
- title: "Setup easy SSH"
icon: ssh
shell: olivetin-setup-easy-ssh
popupOnStart: execution-dialog
# Here's how to use SSH with the "easy" config, to restart a service on
# another server.
#
# Docs: https://docs.olivetin.app/action-ssh-easy.html
# Docs: https://docs.olivetin.app/action-service.html
- title: Restart httpd on server1
id: restart_httpd
icon: restart
timeout: 1
shell: ssh -F /config/ssh/easy.cg root@server1 'service httpd restart'
# Lots of people use OliveTin to build web interfaces for their electronics
# projects. It's best to install OliveTin as a native package (eg, .deb), and
# then you can use either a python script or the `gpio` command.
- title: Toggle GPIO light
shell: gpioset gpiochip1 9=1
icon: light
# There are several built-in shortcuts for the `icon` option, but you
# can also just specify any HTML, this includes any unicode character,
# or a <img = "..." /> link to a custom icon.
#
# Docs: https://docs.olivetin.app/icons.html
#
# Lots of people use OliveTin to easily execute ansible-playbooks. You
# probably want a much longer timeout as well (so that ansible completes).
#
# Docs: https://docs.olivetin.app/ansible-playbook.html
- title: "Run Automation Playbook"
icon: '&#129302;'
shell: ansible-playbook -i /etc/hosts /root/myRepo/myPlaybook.yaml
timeout: 120
# The following actions are "dummy" actions, used in a Dashboard. As long as
# you have these referenced in a dashboard, they will not show up in the
# `actions` view.
- title: Ping hypervisor1
shell: echo "hypervisor1 online"
- title: Ping hypervisor2
shell: echo "hypervisor2 online"
- title: "{{ server.name }} Wake on Lan"
shell: echo "Sending Wake on LAN to {{ server.hostname }}"
entity: server
- title: "{{ server.name }} Power Off"
shell: "echo 'Power Off Server: {{ server.hostname }}'"
entity: server
- title: Ping All Servers
shell: "echo 'Ping all servers'"
icon: ping
- title: Start {{ container.Names }}
icon: box
shell: docker start {{ container.Names }}
entity: container
trigger: Update container entity file
- title: Stop {{ container.Names }}
icon: box
shell: docker stop {{ container.Names }}
entity: container
trigger: Update container entity file
# Lastly, you can hide actions from the web UI, this is useful for creating
# background helpers that execute only on startup or a cron, for updating
# entity files.
# - title: Update container entity file
# shell: 'docker ps -a --format json > /etc/OliveTin/entities/containers.json'
# hidden: true
# execOnStartup: true
# execOnCron: '*/1 * * * *'
# An entity is something that exists - a "thing", like a VM, or a Container
# is an entity. OliveTin allows you to then dynamically generate actions based
# around these entities.
#
# This is really useful if you want to generate wake on lan or poweroff actions
# for `server` entities, for example.
#
# A very popular use case that entities were designed for was for `container`
# entities - in a similar way you could generate `start`, `stop`, and `restart`
# container actions.
#
# Entities are just loaded fome files on disk, OliveTin will also watch these
# files for updates while OliveTin is running, and update entities.
#
# Entities can have properties defined in those files, and those can be used
# in your configuration as variables. For example; `container.status`,
# or `vm.hostname`.
#
# Docs: http://docs.olivetin.app/entities.html
entities:
# YAML files are the default expected format, so you can use .yml or .yaml,
# or even .txt, as long as the file contains valid a valid yaml LIST, then it
# will load properly.
#
# Docs: https://docs.olivetin.app/entities.html
- file: entities/servers.yaml
name: server
- file: entities/containers.json
name: container
# Dashboards are a way of taking actions from the default "actions" view, and
# organizing them into groups - either into folders, or fieldsets.
#
# The only way to properly use entities, are to use them with a `fieldset` on
# a dashboard.
dashboards:
# Top level items are dashboards.
- title: My Servers
contents:
- title: All Servers
type: fieldset
contents:
# The contents of a dashboard will try to look for an action with a
# matching title IF the `contents: ` property is empty.
- title: Ping All Servers
# If you create an item with some "contents:", OliveTin will show that as
# directory.
- title: Hypervisors
contents:
- title: Ping hypervisor1
- title: Ping hypervisor2
# If you specify `type: fieldset` and some `contents`, it will show your
# actions grouped together without a folder.
- type: fieldset
entity: server
title: 'Server: {{ server.hostname }}'
contents:
# By default OliveTin will look for an action with a matching title
# and put it on the dashboard.
#
# Fieldsets also support `type: display`, which can display arbitary
# text. This is useful for displaying things like a container's state.
- type: display
title: |
Hostname: <strong>{{ server.name }}</strong>
IP Address: <strong>{{ server.ip }}</strong>
# These are the actions (defined above) that we want on the dashboard.
- title: '{{ server.name }} Wake on Lan'
- title: '{{ server.name }} Power Off'
# This is the second dashboard.
- title: My Containers
contents:
- title: 'Container {{ container.Names }} ({{ container.Image }})'
entity: container
type: fieldset
contents:
- type: display
title: |
{{ container.RunningFor }} <br /><br /><strong>{{ container.State }}</strong>
- title: 'Start {{ container.Names }}'
- title: 'Stop {{ container.Names }}'

View File

@@ -1,53 +0,0 @@
# There is a built-in micro proxy that will host the webui and REST API all on
# one port (this is called the "Single HTTP Frontend") and means you just need
# one open port in the container/firewalls/etc.
#
# Listen on all addresses available, port 1337
listenAddressSingleHTTPFrontend: 0.0.0.0:1337
# Choose from INFO (default), WARN and DEBUG
logLevel: "INFO"
# Actions (buttons) to show up on the WebUI:
actions:
# This will send 1 ping (-c 1)
# Docs: https://docs.olivetin.app/action-ping.html
- title: Ping Google.com
shell: ping google.com -c 1
icon: ping
# Restart lightdm on host "overseer"
# Docs: https://docs.olivetin.app/action-ping.html
- title: restart lightdm
icon: poop
shell: ssh root@overseer 'service lightdm restart'
- title: sleep 2 seconds
shell: sleep 2
icon: "&#x1F971"
- title: sleep 5 seconds (timeout)
shell: sleep 5
icon: "&#x1F62A"
# OliveTin can run long-running jobs like Ansible playbooks.
#
# For such jobs, you will need to install ansible-playbook on the host where
# you are running OliveTin, or in the container.
#
# You probably want a much longer timeout as well (so that ansible completes).
- title: "Run Ansible Playbook"
icon: "&#x1F1E6"
shell: ansible-playbook -i /etc/hosts /root/myRepo/myPlaybook.yaml
timeout: 120
# OliveTin can control containers - docker is just a command line app.
#
# However, if you are running in a container you will need to do some setup,
# see the docs below.
#
# Docs: https://docs.olivetin.app/action-container-control.html
- title: Restart Plex
icon: smile
shell: docker restart plex

View File

@@ -1,14 +0,0 @@
# There is a built-in micro proxy that will host the webui and REST API all on
# one port (this is called the "Single HTTP Frontend") and means you just need
# one open port in the container/firewalls/etc.
#
# Listen on all addresses available, port 1337
listenAddressSingleHTTPFrontend: 0.0.0.0:1337
hideNavigation: true
# Actions (buttons) to show up on the WebUI:
actions:
- title: Ping example.com
shell: ping example.com -c 1
icon: ping

View File

@@ -1,24 +0,0 @@
# WARNING
# This is considered an advanced installation of OliveTin, and 99% of users
# probably won't need this configuration file. If you're just getting started
# with OliveTin, don't use this.
# WARNING
#
# This tells OliveTin to not spawn the internal micro reverse proxy.
#
# This gives you more fine grained control, but requires quite a bit more setup.
# Most users will set this to "true" and use the built-in micro reverse proxy.
useSingleHTTPFrontend: false
# The WebUI is simply a static webserver. OliveTin comes with one builtin to
# make things easy, but you can also host the "webui" directory on a static
# webserver.
#listenAddressWebUI: 0.0.0.0:1340
# The REST API is used by the WebUI.
#listenAddressRestActions: 0.0.0.0:1338
# The gRPC API is unsed by the WebUI, and can also be limited to localhost:1339.
#listenAddressGrpcActions: 0.0.0.0:1339

View File

@@ -1,53 +0,0 @@
# There is a built-in micro proxy that will host the webui and REST API all on
# one port (this is called the "Single HTTP Frontend") and means you just need
# one open port in the container/firewalls/etc.
#
# Listen on all addresses available, port 1337
listenAddressSingleHTTPFrontend: 0.0.0.0:1337
# Choose from INFO (default), WARN and DEBUG
logLevel: "INFO"
# Actions (buttons) to show up on the WebUI:
actions:
# This will send 1 ping (-c 1)
# Docs: https://docs.olivetin.app/action-ping.html
- title: Ping Google.com
shell: ping google.com -c 1
icon: ping
# Restart lightdm on host "overseer"
# Docs: https://docs.olivetin.app/action-ping.html
- title: restart lightdm
icon: poop
shell: ssh root@overseer 'service lightdm restart'
- title: sleep 2 seconds
shell: sleep 2
icon: "&#x1F971"
- title: sleep 5 seconds (timeout)
shell: sleep 5
icon: "&#x1F62A"
# OliveTin can run long-running jobs like Ansible playbooks.
#
# For such jobs, you will need to install ansible-playbook on the host where
# you are running OliveTin, or in the container.
#
# You probably want a much longer timeout as well (so that ansible completes).
- title: "Run Ansible Playbook"
icon: "&#x1F1E6"
shell: ansible-playbook -i /etc/hosts /root/myRepo/myPlaybook.yaml
timeout: 120
# OliveTin can control containers - docker is just a command line app.
#
# However, if you are running in a container you will need to do some setup,
# see the docs below.
#
# Docs: https://docs.olivetin.app/action-container-control.html
- title: Restart Plex
icon: smile
shell: docker restart plex

146
go.mod
View File

@@ -1,19 +1,135 @@
module github.com/jamesread/OliveTin
module github.com/OliveTin/OliveTin
go 1.16
go 1.21
toolchain go1.21.9
require (
github.com/bufbuild/buf v0.54.1
github.com/denisbrodbeck/machineid v1.0.1
github.com/fsnotify/fsnotify v1.4.9
github.com/fzipp/gocyclo v0.3.1
github.com/go-co-op/gocron v1.6.2
github.com/grpc-ecosystem/grpc-gateway/v2 v2.5.0
github.com/sirupsen/logrus v1.8.1
github.com/spf13/viper v1.8.1
github.com/stretchr/testify v1.7.0
google.golang.org/genproto v0.0.0-20210617175327-b9e0b3197ced
google.golang.org/grpc v1.40.0-dev.0.20210708170655-30dfb4b933a5
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0
google.golang.org/protobuf v1.27.1
github.com/MicahParks/keyfunc/v3 v3.3.2
github.com/bufbuild/buf v1.30.1
github.com/fsnotify/fsnotify v1.6.0
github.com/fzipp/gocyclo v0.6.0
github.com/go-critic/go-critic v0.11.1
github.com/golang-jwt/jwt/v4 v4.5.0
github.com/golang-jwt/jwt/v5 v5.2.1
github.com/google/uuid v1.6.0
github.com/gorilla/websocket v1.4.1
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1
github.com/prometheus/client_golang v1.19.0
github.com/robfig/cron/v3 v3.0.1
github.com/sirupsen/logrus v1.9.3
github.com/spf13/viper v1.15.0
github.com/stretchr/testify v1.9.0
golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8
golang.org/x/oauth2 v0.23.0
google.golang.org/genproto/googleapis/api v0.0.0-20240325203815-454cdb8f5daa
google.golang.org/grpc v1.62.1
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0
google.golang.org/protobuf v1.33.0
gopkg.in/yaml.v3 v3.0.1
)
require (
buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.33.0-20240221180331-f05a6f4403ce.1 // indirect
connectrpc.com/connect v1.16.0 // indirect
connectrpc.com/otelconnect v0.7.0 // indirect
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
github.com/MicahParks/jwkset v0.5.17 // indirect
github.com/Microsoft/go-winio v0.6.1 // indirect
github.com/alexedwards/argon2id v1.0.0 // indirect
github.com/antlr4-go/antlr/v4 v4.13.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/bufbuild/protocompile v0.9.0 // indirect
github.com/bufbuild/protovalidate-go v0.6.0 // indirect
github.com/bufbuild/protoyaml-go v0.1.8 // indirect
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/containerd/stargz-snapshotter/estargz v0.15.1 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect
github.com/cristalhq/acmd v0.11.2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/distribution/reference v0.6.0 // indirect
github.com/docker/cli v26.0.0+incompatible // indirect
github.com/docker/distribution v2.8.3+incompatible // indirect
github.com/docker/docker v26.1.5+incompatible // indirect
github.com/docker/docker-credential-helpers v0.8.1 // indirect
github.com/docker/go-connections v0.5.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/felixge/fgprof v0.9.4 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/go-chi/chi/v5 v5.0.12 // indirect
github.com/go-logr/logr v1.4.1 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-toolsmith/astcast v1.1.0 // indirect
github.com/go-toolsmith/astcopy v1.1.0 // indirect
github.com/go-toolsmith/astequal v1.2.0 // indirect
github.com/go-toolsmith/astfmt v1.1.0 // indirect
github.com/go-toolsmith/astp v1.1.0 // indirect
github.com/go-toolsmith/pkgload v1.2.2 // indirect
github.com/go-toolsmith/strparse v1.1.0 // indirect
github.com/go-toolsmith/typep v1.1.0 // indirect
github.com/gofrs/uuid/v5 v5.0.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/cel-go v0.20.1 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/go-containerregistry v0.19.1 // indirect
github.com/google/pprof v0.0.0-20240327155427-868f304927ed // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jdx/go-netrc v1.0.0 // indirect
github.com/klauspost/compress v1.17.7 // indirect
github.com/klauspost/pgzip v1.2.6 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/moby/docker-image-spec v1.3.1 // indirect
github.com/moby/term v0.5.0 // indirect
github.com/morikuni/aec v1.0.0 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.0 // indirect
github.com/pelletier/go-toml/v2 v2.0.6 // indirect
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pkg/profile v1.7.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_model v0.5.0 // indirect
github.com/prometheus/common v0.48.0 // indirect
github.com/prometheus/procfs v0.12.0 // indirect
github.com/quasilyte/go-ruleguard v0.4.2 // indirect
github.com/quasilyte/gogrep v0.5.0 // indirect
github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727 // indirect
github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567 // indirect
github.com/rs/cors v1.11.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/spf13/afero v1.9.3 // indirect
github.com/spf13/cast v1.5.0 // indirect
github.com/spf13/cobra v1.8.0 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/stoewer/go-strcase v1.3.0 // indirect
github.com/subosito/gotenv v1.4.2 // indirect
github.com/vbatts/tar-split v0.11.5 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect
go.opentelemetry.io/otel v1.24.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.24.0 // indirect
go.opentelemetry.io/otel/metric v1.24.0 // indirect
go.opentelemetry.io/otel/sdk v1.24.0 // indirect
go.opentelemetry.io/otel/trace v1.24.0 // indirect
go.opentelemetry.io/proto/otlp v1.1.0 // indirect
go.uber.org/atomic v1.11.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // indirect
golang.org/x/crypto v0.25.0 // indirect
golang.org/x/exp/typeparams v0.0.0-20240222234643-814bf88cf225 // indirect
golang.org/x/mod v0.17.0 // indirect
golang.org/x/net v0.27.0 // indirect
golang.org/x/sync v0.7.0 // indirect
golang.org/x/sys v0.22.0 // indirect
golang.org/x/term v0.22.0 // indirect
golang.org/x/text v0.16.0 // indirect
golang.org/x/time v0.5.0 // indirect
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240325203815-454cdb8f5daa // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
)

576
go.sum
View File

@@ -1,8 +1,11 @@
buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.33.0-20240221180331-f05a6f4403ce.1 h1:0nWhrRcnkgw1kwJ7xibIO8bqfOA7pBzBjGCDBxIHch8=
buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.33.0-20240221180331-f05a6f4403ce.1/go.mod h1:Tgn5bgL220vkFOI0KPStlcClPeOJzAv4uT+V8JXGUnw=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
@@ -15,9 +18,7 @@ cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOY
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg=
cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8=
cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0=
cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
@@ -26,7 +27,6 @@ cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4g
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
@@ -36,72 +36,148 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
connectrpc.com/connect v1.16.0 h1:rdtfQjZ0OyFkWPTegBNcH7cwquGAN1WzyJy80oFNibg=
connectrpc.com/connect v1.16.0/go.mod h1:XpZAduBQUySsb4/KO5JffORVkDI4B6/EYPi7N8xpNZw=
connectrpc.com/otelconnect v0.7.0 h1:ZH55ZZtcJOTKWWLy3qmL4Pam4RzRWBJFOqTPyAqCXkY=
connectrpc.com/otelconnect v0.7.0/go.mod h1:Bt2ivBymHZHqxvo4HkJ0EwHuUzQN6k2l0oH+mp/8nwc=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0=
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1UJrqV3uuy861HCTo708pDMbjHHdCas=
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM=
github.com/bufbuild/buf v0.54.1 h1:k5zYgSOlNg17mZCBgKo6TWktCiSvBdOx+v591t3JPjY=
github.com/bufbuild/buf v0.54.1/go.mod h1:BRVv/lQDPFt0AGt79VgXMQK5HHXRW5VFhAu+mNnOmeM=
github.com/MicahParks/jwkset v0.5.17 h1:DrcwyKwSP5adD0G2XJTvDulnWXjD6gbjROMgMXDbkKA=
github.com/MicahParks/jwkset v0.5.17/go.mod h1:q8ptTGn/Z9c4MwbcfeCDssADeVQb3Pk7PnVxrvi+2QY=
github.com/MicahParks/keyfunc/v3 v3.3.2 h1:YTtwc4dxalBZKFqHhqctBWN6VhbLdGhywmne9u5RQVM=
github.com/MicahParks/keyfunc/v3 v3.3.2/go.mod h1:GJBeEjnv25OnD9y2OYQa7ELU6gYahEMBNXINZb+qm34=
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
github.com/alexedwards/argon2id v1.0.0 h1:wJzDx66hqWX7siL/SRUmgz3F8YMrd/nfX/xHHcQQP0w=
github.com/alexedwards/argon2id v1.0.0/go.mod h1:tYKkqIjzXvZdzPvADMWOEZ+l6+BD6CtBXMj5fnJppiw=
github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI=
github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g=
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/bufbuild/buf v1.30.1 h1:QFtanwsXodoGFAwzXFXGXpzBkb7N2u8ZDyA3jWB4Pbs=
github.com/bufbuild/buf v1.30.1/go.mod h1:7W8DJnj76wQa55EA3z2CmDxS0/nsHh8FqtE00dyDAdA=
github.com/bufbuild/protocompile v0.9.0 h1:DI8qLG5PEO0Mu1Oj51YFPqtx6I3qYXUAhJVJ/IzAVl0=
github.com/bufbuild/protocompile v0.9.0/go.mod h1:s89m1O8CqSYpyE/YaSGtg1r1YFMF5nLTwh4vlj6O444=
github.com/bufbuild/protovalidate-go v0.6.0 h1:Jgs1kFuZ2LHvvdj8SpCLA1W/+pXS8QSM3F/E2l3InPY=
github.com/bufbuild/protovalidate-go v0.6.0/go.mod h1:1LamgoYHZ2NdIQH0XGczGTc6Z8YrTHjcJVmiBaar4t4=
github.com/bufbuild/protoyaml-go v0.1.8 h1:X9QDLfl9uEllh4gsXUGqPanZYCOKzd92uniRtW2OnAQ=
github.com/bufbuild/protoyaml-go v0.1.8/go.mod h1:R8vE2+l49bSiIExP4VJpxOXleHE+FDzZ6HVxr3cYunw=
github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chromedp/cdproto v0.0.0-20230802225258-3cf4e6d46a89/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs=
github.com/chromedp/chromedp v0.9.2/go.mod h1:LkSXJKONWTCHAfQasKFUZI+mxqS4tZqhmtGzzhLsnLs=
github.com/chromedp/sysutil v1.0.0/go.mod h1:kgWmDdq8fTzXYcKIBqIYvRRTnYb9aNS9moAV0xufSww=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
github.com/containerd/stargz-snapshotter/estargz v0.15.1 h1:eXJjw9RbkLFgioVaTG+G/ZW/0kEe2oEKCdS/ZxIyoCU=
github.com/containerd/stargz-snapshotter/estargz v0.15.1/go.mod h1:gr2RNwukQ/S9Nv33Lt6UC7xEx58C+LHRdoqbEKjz1Kk=
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4=
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
github.com/cristalhq/acmd v0.11.2 h1:ITIWtBRiYbmzk+i8xQgH2RzfCVMII+dOd0CtGWVIhaU=
github.com/cristalhq/acmd v0.11.2/go.mod h1:LG5oa43pE/BbxtfMoImHCQN++0Su7dzipdgBjMCBVDQ=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/denisbrodbeck/machineid v1.0.1 h1:geKr9qtkB876mXguW2X6TU4ZynleN6ezuMSRhl4D7AQ=
github.com/denisbrodbeck/machineid v1.0.1/go.mod h1:dJUwb7PTidGDeYyUBmXZ2GphQBbjJCrnectwCyxcUSI=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/docker/cli v26.0.0+incompatible h1:90BKrx1a1HKYpSnnBFR6AgDq/FqkHxwlUyzJVPxD30I=
github.com/docker/cli v26.0.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v26.1.5+incompatible h1:NEAxTwEjxV6VbBMBoGG3zPqbiJosIApZjxlbrG9q3/g=
github.com/docker/docker v26.1.5+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker-credential-helpers v0.8.1 h1:j/eKUktUltBtMzKqmfLB0PAgqYyMHOp5vfsD1807oKo=
github.com/docker/docker-credential-helpers v0.8.1/go.mod h1:P3ci7E3lwkZg6XiHdRKft1KckHiO9a2rNtyFbZ/ry9M=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fzipp/gocyclo v0.3.1 h1:A9UeX3HJSXTBzvHzhqoYVuE0eAhe+aM8XBCCwsPMZOc=
github.com/fzipp/gocyclo v0.3.1/go.mod h1:DJHO6AUmbdqj2ET4Z9iArSuwWgYDRryYt2wASxc7x3E=
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-co-op/gocron v1.6.2 h1:x5g1tWnWcXIZesdosJJcbziRi4XG6tKB92yKLUpoBkU=
github.com/go-co-op/gocron v1.6.2/go.mod h1:DbJm9kdgr1sEvWpHCA7dFFs/PGHPMil9/97EXCRPr4k=
github.com/envoyproxy/protoc-gen-validate v1.0.4 h1:gVPz/FMfvh57HdSJQyvBtF00j8JU4zdyUgIUNhlgg0A=
github.com/envoyproxy/protoc-gen-validate v1.0.4/go.mod h1:qys6tmnRsYrQqIhm2bvKZH4Blx/1gTIZ2UKVY1M+Yew=
github.com/felixge/fgprof v0.9.3/go.mod h1:RdbpDgzqYVh/T9fPELJyV7EYJuHB55UTEULNun8eiPw=
github.com/felixge/fgprof v0.9.4 h1:ocDNwMFlnA0NU0zSB3I52xkO4sFXk80VK9lXjLClu88=
github.com/felixge/fgprof v0.9.4/go.mod h1:yKl+ERSa++RYOs32d8K6WEXCB4uXdLls4ZaZPpayhMM=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
github.com/fzipp/gocyclo v0.6.0 h1:lsblElZG7d3ALtGMx9fmxeTKZaLLpU8mET09yN4BBLo=
github.com/fzipp/gocyclo v0.6.0/go.mod h1:rXPyn8fnlpa0R2csP/31uerbiVBugk5whMdlyaLkLoA=
github.com/go-chi/chi/v5 v5.0.12 h1:9euLV5sTrTNTRUU9POmDUvfxyj6LAABLUcEWO+JJb4s=
github.com/go-chi/chi/v5 v5.0.12/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/go-critic/go-critic v0.11.1 h1:/zBseUSUMytnRqxjlsYNbDDxpu3R2yH8oLXo/FOE8b8=
github.com/go-critic/go-critic v0.11.1/go.mod h1:aZVQR7+gazH6aDEQx4356SD7d8ez8MipYjXbEl5JAKA=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
github.com/go-logr/logr v1.4.1/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-toolsmith/astcast v1.1.0 h1:+JN9xZV1A+Re+95pgnMgDboWNVnIMMQXwfBwLRPgSC8=
github.com/go-toolsmith/astcast v1.1.0/go.mod h1:qdcuFWeGGS2xX5bLM/c3U9lewg7+Zu4mr+xPwZIB4ZU=
github.com/go-toolsmith/astcopy v1.1.0 h1:YGwBN0WM+ekI/6SS6+52zLDEf8Yvp3n2seZITCUBt5s=
github.com/go-toolsmith/astcopy v1.1.0/go.mod h1:hXM6gan18VA1T/daUEHCFcYiW8Ai1tIwIzHY6srfEAw=
github.com/go-toolsmith/astequal v1.0.3/go.mod h1:9Ai4UglvtR+4up+bAD4+hCj7iTo4m/OXVTSLnCyTAx4=
github.com/go-toolsmith/astequal v1.1.0/go.mod h1:sedf7VIdCL22LD8qIvv7Nn9MuWJruQA/ysswh64lffQ=
github.com/go-toolsmith/astequal v1.2.0 h1:3Fs3CYZ1k9Vo4FzFhwwewC3CHISHDnVUPC4x0bI2+Cw=
github.com/go-toolsmith/astequal v1.2.0/go.mod h1:c8NZ3+kSFtFY/8lPso4v8LuJjdJiUFVnSuU3s0qrrDY=
github.com/go-toolsmith/astfmt v1.1.0 h1:iJVPDPp6/7AaeLJEruMsBUlOYCmvg0MoCfJprsOmcco=
github.com/go-toolsmith/astfmt v1.1.0/go.mod h1:OrcLlRwu0CuiIBp/8b5PYF9ktGVZUjlNMV634mhwuQ4=
github.com/go-toolsmith/astp v1.1.0 h1:dXPuCl6u2llURjdPLLDxJeZInAeZ0/eZwFJmqZMnpQA=
github.com/go-toolsmith/astp v1.1.0/go.mod h1:0T1xFGz9hicKs8Z5MfAqSUitoUYS30pDMsRVIDHs8CA=
github.com/go-toolsmith/pkgload v1.2.2 h1:0CtmHq/02QhxcF7E9N5LIFcYFsMR5rdovfqTtRKkgIk=
github.com/go-toolsmith/pkgload v1.2.2/go.mod h1:R2hxLNRKuAsiXCo2i5J6ZQPhnPMOVtU+f0arbFPWCus=
github.com/go-toolsmith/strparse v1.0.0/go.mod h1:YI2nUKP9YGZnL/L1/DLFBfixrcjslWct4wyljWhSRy8=
github.com/go-toolsmith/strparse v1.1.0 h1:GAioeZUK9TGxnLS+qfdqNbA4z0SSm5zVNtCQiyP2Bvw=
github.com/go-toolsmith/strparse v1.1.0/go.mod h1:7ksGy58fsaQkGQlY8WVoBFNyEPMGuJin1rfoPS4lBSQ=
github.com/go-toolsmith/typep v1.1.0 h1:fIRYDyF+JywLfqzyhdiHzRop/GQDxxNhLGQ6gFUNHus=
github.com/go-toolsmith/typep v1.1.0/go.mod h1:fVIw+7zjdsMxDA3ITWnH1yOiw1rnTQKCsF/sk2H/qig=
github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
github.com/gobwas/ws v1.2.1/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY=
github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw=
github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw=
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gofrs/uuid/v5 v5.0.0 h1:p544++a97kEL+svbcFbCQVM9KFu0Yo25UoISXGNNH9M=
github.com/gofrs/uuid/v5 v5.0.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/glog v0.0.0-20210429001901-424d2337a529 h1:2voWjNECnrZRbfwXxHB1/j8wa6xdKn85B5NzgVL/pTU=
github.com/golang/glog v0.0.0-20210429001901-424d2337a529/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
@@ -110,7 +186,6 @@ github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@@ -126,13 +201,12 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/snappy v0.0.3 h1:fHPg5GQYlCeLIPB9BZqMVR5nR9A+IM5zcgeTdjMYmLA=
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
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 v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/cel-go v0.20.1 h1:nDx9r8S3L4pE61eDdt8igGj8rf5kjYR3ILxWIpWNi84=
github.com/google/cel-go v0.20.1/go.mod h1:kWcIzTsPX0zmQ+H3TirHstLLf9ep5QTsZBN9u4dOYLg=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
@@ -141,12 +215,13 @@ github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-containerregistry v0.19.1 h1:yMQ62Al6/V0Z7CqIrrS1iYoA5/oQCm88DeNujc7C1KY=
github.com/google/go-containerregistry v0.19.1/go.mod h1:YCMFNQeeXeLF+dnhhWkqDItx/JSkH01j1Kis4PsjzFI=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
@@ -159,167 +234,189 @@ github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hf
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20211214055906-6f57359322fd/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg=
github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
github.com/google/pprof v0.0.0-20240327155427-868f304927ed h1:n8QtJTrwsv3P7dNxPaMeNkMcxvUpqocsHLr8iDLGlQI=
github.com/google/pprof v0.0.0-20240327155427-868f304927ed/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
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/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gordonklaus/ineffassign v0.0.0-20200309095847-7953dde2c7bf/go.mod h1:cuNKsD1zp2v6XfE/orVX2QE1LC+i254ceGcVeDT3pTU=
github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.5.0 h1:ajue7SzQMywqRjg2fK7dcpc0QhFGpTR2plWfV4EZWR4=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.5.0/go.mod h1:r1hZAcvfFXuYmcKyCJI9wlyOPIZUJl6FCB8Cpca/NLE=
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM=
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1 h1:/c3QmbOGMGTOumP2iT/rCwB7b0QDGLKzqOmktBjT+Is=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1/go.mod h1:5SN9VR2LTsRFsrEC6FHgRbTWrTHu6tqPeKxEQv15giM=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jhump/protoreflect v1.9.1-0.20210817181203-db1a327a393e h1:Yb4fEGk+GtBSNuvy5rs0ZJt/jtopc/z9azQaj3xbies=
github.com/jhump/protoreflect v1.9.1-0.20210817181203-db1a327a393e/go.mod h1:7GcYQDdMU/O/BBrl/cX6PNHpXh6cenjd8pneu5yW7Tg=
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/ianlancetaylor/demangle v0.0.0-20210905161508-09a460cdf81d/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w=
github.com/ianlancetaylor/demangle v0.0.0-20230524184225-eabc099b10ab/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/jdx/go-netrc v1.0.0 h1:QbLMLyCZGj0NA8glAhxUpf1zDg6cxnWgMBbjq40W0gQ=
github.com/jdx/go-netrc v1.0.0/go.mod h1:Gh9eFQJnoTNIRHXl2j5bJXA1u84hQWJWgGh569zF3v8=
github.com/jhump/protoreflect v1.15.6 h1:WMYJbw2Wo+KOWwZFvgY0jMoVHM6i4XIvRs2RcBj5VmI=
github.com/jhump/protoreflect v1.15.6/go.mod h1:jCHoyYQIJnaabEYnbGwyo9hUqfyUMTbJw/tAut5t97E=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.13.4 h1:0zhec2I8zGnjWcKyLl6i3gPqKANCCn5e9xmviEEeX6s=
github.com/klauspost/compress v1.13.4/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE=
github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
github.com/klauspost/compress v1.17.7 h1:ehO88t2UGzQK66LMdE8tibEd1ErmzZjNEqWkjLAKQQg=
github.com/klauspost/compress v1.17.7/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU=
github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls=
github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag=
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/nishanths/predeclared v0.0.0-20200524104333-86fad755b4d3/go.mod h1:nt3d53pc1VYcphSCIaYAJtnPYnr3Zyn8fMq2wvPGPso=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pelletier/go-toml v1.9.3 h1:zeC5b1GviRUyKYd6OJPvBU/mcVDVoL1OhT17FCt5dSQ=
github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs=
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0=
github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU=
github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/profile v1.6.0 h1:hUDfIISABYI59DyeB3OTay/HxSRwTQ8rB/H83k6r5dM=
github.com/pkg/profile v1.6.0/go.mod h1:qBsxPvzyUincmltOk6iyRVxHYg4adc0OFOv72ZdLa18=
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
github.com/pkg/profile v1.7.0 h1:hnbDkaNWPCLMO9wGLdBFTIZvzDrDfBM2072E1S9gJkA=
github.com/pkg/profile v1.7.0/go.mod h1:8Uer0jas47ZQMJ7VD+OHknK4YDY07LPUC6dEvqDjvNo=
github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU=
github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE=
github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc=
github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
github.com/quasilyte/go-ruleguard v0.4.2 h1:htXcXDK6/rO12kiTHKfHuqR4kr3Y4M0J0rOL6CH/BYs=
github.com/quasilyte/go-ruleguard v0.4.2/go.mod h1:GJLgqsLeo4qgavUoL8JeGFNS7qcisx3awV/w9eWTmNI=
github.com/quasilyte/gogrep v0.5.0 h1:eTKODPXbI8ffJMN+W2aE0+oL0z/nh8/5eNdiO34SOAo=
github.com/quasilyte/gogrep v0.5.0/go.mod h1:Cm9lpz9NZjEoL1tgZ2OgeUKPIxL1meE7eo60Z6Sk+Ng=
github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727 h1:TCg2WBOl980XxGFEZSS6KlBGIV0diGdySzxATTWoqaU=
github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727/go.mod h1:rlzQ04UMyJXu/aOvhd8qT+hvDrFpiwqp8MRXDY9szc0=
github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567 h1:M8mH9eK4OUR4lu7Gd+PU1fV2/qnDNfzT635KRSObncs=
github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567/go.mod h1:DWNGW8A4Y+GyBgPuaQJuWiy0XYftx4Xm/y5Jqk9I6VQ=
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.6.0 h1:xoax2sJ2DT8S8xA2paPFjDCScCNeWsg75VG0DLRreiY=
github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng=
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v1.2.1 h1:+KmjbUw1hriSNMF55oPrkZcb27aECyrj8V2ytv7kWDw=
github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
github.com/rs/cors v1.11.0 h1:0B9GE/r9Bc2UxRMMtymBkHTenPkHDv0CW4Y98GBY+po=
github.com/rs/cors v1.11.0/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/spf13/afero v1.9.3 h1:41FoI0fD7OR7mGcKE/aOiLkGreyf8ifIOQmJANWogMk=
github.com/spf13/afero v1.9.3/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y=
github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w=
github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU=
github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.8.1 h1:Kq1fyeebqsBfbjZj4EL7gj2IO0mMaiyjYUWcUsl2O44=
github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns=
github.com/spf13/viper v1.15.0 h1:js3yy885G8xwJa6iOISGFwd+qlUo5AvyXb7CiihdtiU=
github.com/spf13/viper v1.15.0/go.mod h1:fFcTBJxvhhzSJiZy8n+PeW6t8l+KeT/uTARa0jHOQLA=
github.com/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs=
github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/twitchtv/twirp v8.1.0+incompatible h1:KGXanpa9LXdVE/V5P/tA27rkKFmXRGCtSNT7zdeeVOY=
github.com/twitchtv/twirp v8.1.0+incompatible/go.mod h1:RRJoFSAmTEh2weEqWtpPE3vFK5YBhA6bqp2l1kfCC5A=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8=
github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
github.com/vbatts/tar-split v0.11.5 h1:3bHCTIheBm1qFTcgh9oPu+nNBtX+XJIupG/vacinCts=
github.com/vbatts/tar-split v0.11.5/go.mod h1:yZbwRsSeGjusneWgA781EKej9HF8vme8okylkAeNKLk=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M=
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/goleak v1.1.10 h1:z+mqJhf6ss6BSfSM671tgKyZBFPTTJM+HLxnhPC3wu0=
go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/multierr v1.7.0 h1:zaiO/rmgFjbmCXdSYJWQcdvOCsthmdaHfr3Gm2Kx4Ec=
go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak=
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
go.uber.org/zap v1.19.0 h1:mZQZefskPPCMIBCSEH0v2/iUqqLrYtaeqwD6FUGUnFE=
go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw=
go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo=
go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.24.0 h1:t6wl9SPayj+c7lEIFgm4ooDBZVb01IhLB4InpomhRw8=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.24.0/go.mod h1:iSDOcsnSA5INXzZtwaBPrKp/lWu/V14Dd+llD0oI2EA=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.24.0 h1:Xw8U6u2f8DK2XAkGRFV7BBLENgnTGX9i4rQRxJf+/vs=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.24.0/go.mod h1:6KW1Fm6R/s6Z3PGXwSJN2K4eT6wQB3vXX6CVnYX9NmM=
go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI=
go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco=
go.opentelemetry.io/otel/sdk v1.24.0 h1:YMPPDNymmQN3ZgczicBY3B6sf9n62Dlj9pWD3ucgoDw=
go.opentelemetry.io/otel/sdk v1.24.0/go.mod h1:KVrIYw6tEubO9E96HQpcmpTKDVn9gdv35HoYiQWGDFg=
go.opentelemetry.io/otel/sdk/metric v1.19.0 h1:EJoTO5qysMsYCa+w4UghwFV/ptQgqSL/8Ni+hx+8i1k=
go.opentelemetry.io/otel/sdk/metric v1.19.0/go.mod h1:XjG0jQyFJrv2PbMvwND7LwCEhsJzCzV5210euduKcKY=
go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI=
go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
go.opentelemetry.io/proto/otlp v1.1.0 h1:2Di21piLrCqJ3U3eXGCTPHE9R8Nh+0uglSnOyxikMeI=
go.opentelemetry.io/proto/otlp v1.1.0/go.mod h1:GpBHCBWiqvVLDqmHZsoMM3C5ySeKTC7ej/RNTae6MdY=
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
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.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -330,6 +427,12 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 h1:aAcj0Da7eBAtrTp03QXWvm88pSyOt+UgdZw2BFZ+lEw=
golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8/go.mod h1:CQ1k9gNrJ50XIzaKCRR2hssIjF07kZFEiieALBM/ARQ=
golang.org/x/exp/typeparams v0.0.0-20220428152302-39d4317da171/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
golang.org/x/exp/typeparams v0.0.0-20230203172020-98cc5a0785f9/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
golang.org/x/exp/typeparams v0.0.0-20240222234643-814bf88cf225 h1:BzKNaIRXh1bD+1557OcFIHlpYBiVbK4zEyn8zBHi1SE=
golang.org/x/exp/typeparams v0.0.0-20240222234643-814bf88cf225/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@@ -343,8 +446,6 @@ golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRu
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug=
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
@@ -355,11 +456,12 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
@@ -386,14 +488,14 @@ golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81R
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210825183410-e898025ed96a h1:bRuuGXV8wwSdGTB+CtJf+FjgO1APK1CoO39T4BN/XBw=
golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -403,10 +505,8 @@ golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ
golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210615190721-d04028783cf1/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs=
golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -417,11 +517,11 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -431,8 +531,6 @@ golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -454,40 +552,53 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf h1:2ucpDCmfkl8Bd/FsLtiD653Wf96cW37s+iGx93zsu4k=
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b h1:9zKuko04nR4gjZ4+DNjHqRlAJqbJETHwiNKDqTfOjfE=
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk=
golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
@@ -497,8 +608,6 @@ golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgw
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
@@ -520,10 +629,8 @@ golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWc
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200522201501-cb1345f3a375/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200717024301-6ddee64345a6/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
@@ -533,15 +640,15 @@ golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4f
golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.5 h1:ouewzE6p+/VEB31YYnTbEJdi8pFqKp4P4n85vwo3DHA=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
@@ -562,9 +669,6 @@ google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz513
google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU=
google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94=
google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
@@ -595,7 +699,6 @@ google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfG
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
@@ -607,14 +710,12 @@ google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6D
google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
google.golang.org/genproto v0.0.0-20210617175327-b9e0b3197ced h1:c5geK1iMU3cDKtFrCVQIcjR3W+JOZMuhIyICMCTbtus=
google.golang.org/genproto v0.0.0-20210617175327-b9e0b3197ced/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24=
google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto/googleapis/api v0.0.0-20240325203815-454cdb8f5daa h1:Jt1XW5PaLXF1/ePZrznsh/aAUvI7Adfc3LY1dAKlzRs=
google.golang.org/genproto/googleapis/api v0.0.0-20240325203815-454cdb8f5daa/go.mod h1:K4kfzHtI0kqWA79gecJarFtDn/Mls+GxQcg3Zox91Ac=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240325203815-454cdb8f5daa h1:RBgMaUMP+6soRkik4VoN8ojR2nex2TqZwjSSogic+eo=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240325203815-454cdb8f5daa/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
@@ -628,17 +729,13 @@ google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3Iji
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
google.golang.org/grpc v1.40.0-dev.0.20210708170655-30dfb4b933a5 h1:jeEzNnOogdiVxvaPNbt/QFOggkBTaUPBkQ2/NIngeyk=
google.golang.org/grpc v1.40.0-dev.0.20210708170655-30dfb4b933a5/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0 h1:M1YKkFIboKNieVO5DLUEVzQfGwJD30Nv2jfUgzb5UcE=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
google.golang.org/grpc v1.62.1 h1:B4n+nfKzOICUXMgyrNd19h/I9oH0L1pizfk1d4zSgTk=
google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0 h1:rNBFJjBCOgVr9pWD7rs/knKL4FRTKgpZmsRfV214zcA=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0/go.mod h1:Dk1tviKTvMCz5tvh7t+fh94dhmQVHuCt2OzJB3CTW9Y=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@@ -649,25 +746,22 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.25.1-0.20200805231151-a709e31e5d12/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/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/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU=
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0=
gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

View File

@@ -0,0 +1,8 @@
env:
browser: true
es2021: true
extends: 'eslint:recommended'
parserOptions:
ecmaVersion: 12
sourceType: module
rules: {}

View File

@@ -1,2 +1,3 @@
results
node_modules
.vagrant

View File

@@ -0,0 +1,3 @@
---
require:
- mochaSetup.mjs

View File

@@ -1,14 +1,23 @@
container:
rm -rf *.tar.gz
mv dist/*.tar.gz ./
tar xavf OliveTin-*linux-amd64.tar.gz
docker rm -f olivetin && docker rmi -f olivetin
docker build -t olivetin:latest OliveTin-*linux-amd64/
docker create --name olivetin -p 1337:1337 -v `pwd`/config/:/config/ olivetin
default: test-install test-run
cypress:
npm install
./cypressRun.sh "general"
./cypressRun.sh "hiddenNav"
test-install:
npm install --no-fund
.PHONY: cypress container
test-run:
npx mocha -t 10000
find-flakey-tests:
echo "Running test-run infinately"
sh -c "while make test-run; do :; done"
nginx:
podman-compose up -d nginx
clean:
podman-compose down
getsnapshot:
rm -rf /opt/OliveTin-snapshot/*
gh run download -D /opt/OliveTin-snapshot/
.PHONY: default

View File

@@ -1 +1,14 @@
# OliveTin-integration-tests
# OliveTin-integration-tests
## GitHub Actions (Ubuntu, Local Process)
- `mocha` is run with the default runner that starts and stops OliveTin as a local process (ie, localhost:1337).
## Running different configurations (Local Process, VM, Container)
- Get the snapshot you want to test `make getsnapshot`
- To test against VMs:
-- `export OLIVETIN_TEST_RUNNER=vm`
-- `vagrant up fedora38` (or whatever distro you like defined in `Vagrantfile`)
-- `. envVagrant.sh fedora38` to set the $IP and $PORT
- `mocha`

36
integration-tests/Vagrantfile vendored Normal file
View File

@@ -0,0 +1,36 @@
# This Vagrantfile is designed to be used with artifacts that have been built by goreleaser.
# (eg, snapshot builds on GitHub)
Vagrant.configure("2") do |config|
config.vm.provision "shell", inline: "mkdir /etc/OliveTin && chmod o+w /etc/OliveTin/ && mkdir -p /opt/OliveTin-configs/ && chmod 0777 /opt/OliveTin-configs", privileged: true
config.vm.provision "file", source: "configs/.", destination: "/opt/OliveTin-configs/"
config.vm.provider :libvirt do |libvirt|
libvirt.management_network_device = 'virbr0'
end
config.vm.define :stream9 do |i|
i.vm.box = "centos/stream9"
i.vm.provision "file", source: "/opt/OliveTin-snapshot/OliveTin_linux_amd64.rpm", destination: "$HOME/"
i.vm.provision "shell", inline: "rpm -U OliveTin* && systemctl enable --now OliveTin && systemctl disable --now firewalld"
end
config.vm.define :fedora38 do |i|
i.vm.box = "generic/fedora38"
i.vm.provision "file", source: "/opt/OliveTin-snapshot/OliveTin_linux_amd64.rpm", destination: "$HOME/"
i.vm.provision "shell", inline: "rpm -U OliveTin* && systemctl enable --now OliveTin && systemctl disable --now firewalld"
end
config.vm.define :debian12 do |i|
i.vm.box = "debian/bookworm64"
i.vm.provision "file", source: "/opt/OliveTin-snapshot/OliveTin_linux_amd64.deb", destination: "$HOME/"
i.vm.provision "shell", inline: "dpkg --force-confold -i OliveTin* && systemctl enable --now OliveTin"
end
config.vm.define :ubuntu2310 do |i|
i.vm.box = "ubuntu/mantic64"
i.vm.provision "file", source: "/opt/OliveTin-snapshot/OliveTin_linux_amd64.deb", destination: "$HOME/"
i.vm.provision "shell", inline: "dpkg --force-confold -i OliveTin* && systemctl enable --now OliveTin && systemctl disable --now firewalld"
end
end

View File

@@ -0,0 +1,17 @@
---
version: "3.8"
services:
nginx:
container_name: nginx
image: docker.io/nginx
volumes:
- ./proxies/nginx/:/etc/nginx/
ports:
- "8443:8443"
restart: unless-stopped
networks:
- otproxy
networks:
otproxy:
name: otproxy

View File

@@ -1,53 +0,0 @@
# There is a built-in micro proxy that will host the webui and REST API all on
# one port (this is called the "Single HTTP Frontend") and means you just need
# one open port in the container/firewalls/etc.
#
# Listen on all addresses available, port 1337
listenAddressSingleHTTPFrontend: 0.0.0.0:1337
# Choose from INFO (default), WARN and DEBUG
logLevel: "DEBUG"
# Actions (buttons) to show up on the WebUI:
actions:
# This will send 1 ping (-c 1)
# Docs: https://docs.olivetin.app/action-ping.html
- title: Ping Google.com
shell: ping google.com -c 1
icon: ping
# Restart lightdm on host "overseer"
# Docs: https://docs.olivetin.app/action-ping.html
- title: restart lightdm
icon: poop
shell: ssh root@overseer 'service lightdm restart'
- title: sleep 2 seconds
shell: sleep 2
icon: "&#x1F971"
- title: sleep 5 seconds (timeout)
shell: sleep 5
icon: "&#x1F62A"
# OliveTin can run long-running jobs like Ansible playbooks.
#
# For such jobs, you will need to install ansible-playbook on the host where
# you are running OliveTin, or in the container.
#
# You probably want a much longer timeout as well (so that ansible completes).
- title: "Run Ansible Playbook"
icon: "&#x1F1E6"
shell: ansible-playbook -i /etc/hosts /root/myRepo/myPlaybook.yaml
timeout: 120
# OliveTin can control containers - docker is just a command line app.
#
# However, if you are running in a container you will need to do some setup,
# see the docs below.
#
# Docs: https://docs.olivetin.app/action-container-control.html
- title: Restart Plex
icon: smile
shell: docker restart plex

View File

@@ -1,14 +0,0 @@
# There is a built-in micro proxy that will host the webui and REST API all on
# one port (this is called the "Single HTTP Frontend") and means you just need
# one open port in the container/firewalls/etc.
#
# Listen on all addresses available, port 1337
listenAddressSingleHTTPFrontend: 0.0.0.0:1337
hideNavigation: true
# Actions (buttons) to show up on the WebUI:
actions:
- title: Ping example.com
shell: ping example.com -c 1
icon: ping

View File

@@ -1,53 +0,0 @@
# There is a built-in micro proxy that will host the webui and REST API all on
# one port (this is called the "Single HTTP Frontend") and means you just need
# one open port in the container/firewalls/etc.
#
# Listen on all addresses available, port 1337
listenAddressSingleHTTPFrontend: 0.0.0.0:1337
# Choose from INFO (default), WARN and DEBUG
logLevel: "DEBUG"
# Actions (buttons) to show up on the WebUI:
actions:
# This will send 1 ping (-c 1)
# Docs: https://docs.olivetin.app/action-ping.html
- title: Ping Google.com
shell: ping google.com -c 1
icon: ping
# Restart lightdm on host "overseer"
# Docs: https://docs.olivetin.app/action-ping.html
- title: restart lightdm
icon: poop
shell: ssh root@overseer 'service lightdm restart'
- title: sleep 2 seconds
shell: sleep 2
icon: "&#x1F971"
- title: sleep 5 seconds (timeout)
shell: sleep 5
icon: "&#x1F62A"
# OliveTin can run long-running jobs like Ansible playbooks.
#
# For such jobs, you will need to install ansible-playbook on the host where
# you are running OliveTin, or in the container.
#
# You probably want a much longer timeout as well (so that ansible completes).
- title: "Run Ansible Playbook"
icon: "&#x1F1E6"
shell: ansible-playbook -i /etc/hosts /root/myRepo/myPlaybook.yaml
timeout: 120
# OliveTin can control containers - docker is just a command line app.
#
# However, if you are running in a container you will need to do some setup,
# see the docs below.
#
# Docs: https://docs.olivetin.app/action-container-control.html
- title: Restart Plex
icon: smile
shell: docker restart plex

View File

@@ -0,0 +1,18 @@
#
# Integration Test Config: Entities
#
listenAddressSingleHTTPFrontend: 0.0.0.0:1337
logLevel: "DEBUG"
checkForUpdates: false
actions:
- title: Ping {{ server.hostname }}
shell: ping {{ server.hostname }}
icon: ping
entity: server
entities:
- file: entities/servers.yaml
name: server

View File

@@ -0,0 +1,3 @@
- hostname: server1
- hostname: server2
- hostname: server3

View File

@@ -0,0 +1,41 @@
#
# Integration Test Config: General
#
listenAddressSingleHTTPFrontend: 0.0.0.0:1337
logLevel: "DEBUG"
checkForUpdates: false
actions:
- title: Ping Google.com
shell: ping google.com -c 1
icon: ping
- title: restart lightdm
icon: poop
shell: ssh root@overseer 'service lightdm restart'
- title: sleep 2 seconds
shell: sleep 2
icon: "&#x1F971"
- title: sleep 5 seconds (timeout)
shell: sleep 5
icon: "&#x1F62A"
- title: dir-popup
shell: dir
popupOnStart: execution-dialog-stdout-only
- title: cd-passive
shell: cd
- title: "Run Ansible Playbook"
icon: "&#x1F1E6"
shell: ansible-playbook -i /etc/hosts /root/myRepo/myPlaybook.yaml
timeout: 120
- title: Restart Plex
icon: smile
shell: docker restart plex

View File

@@ -0,0 +1,12 @@
#
# Integration Test Config: General
#
showFooter: false
checkForUpdates: false
# Actions (buttons) to show up on the WebUI:
actions:
- title: Ping example.com
shell: ping example.com -c 1
icon: ping

View File

@@ -0,0 +1,14 @@
#
# Integration Test Config: General
#
listenAddressSingleHTTPFrontend: 0.0.0.0:1337
showNavigation: false
checkForUpdates: false
# Actions (buttons) to show up on the WebUI:
actions:
- title: Ping example.com
shell: ping example.com -c 1
icon: ping

View File

@@ -0,0 +1,25 @@
---
listenAddressSingleHTTPFrontend: 0.0.0.0:1337
logLevel: "DEBUG"
checkForUpdates: false
actions:
- title: Ping Google.com
shell: ping google.com -c 1
icon: ping
- title: Test multiple dropdowns
shell: echo {{ salutation }} {{ person }}
icon: ping
arguments:
- name: salutation
choices:
- value: Hello
- value: Goodbye
- name: person
choices:
- value: Alice
- value: Bob
- value: Dave

View File

@@ -0,0 +1,28 @@
#
# Integration Test Config: General
#
listenAddressSingleHTTPFrontend: 0.0.0.0:1337
logLevel: "DEBUG"
checkForUpdates: false
actions:
- title: action1
shell: date
icon: clock
- title: action2
shell: date
icon: clock
- title: action3
shell: date
icon: clock
dashboards:
- title: My Dashboard
contents:
- title: action1
- title: action2
- title: action3

View File

@@ -0,0 +1,15 @@
#
# Integration Test Config: General
#
listenAddressSingleHTTPFrontend: 0.0.0.0:1337
logLevel: "DEBUG"
checkForUpdates: false
pageTitle: "My Custom App"
actions:
- title: sleep 2 seconds
shell: sleep 2
icon: "&#x1F971"

View File

@@ -0,0 +1,16 @@
#
# Integration Test Config: General
#
listenAddressSingleHTTPFrontend: 0.0.0.0:1337
logLevel: "DEBUG"
checkForUpdates: false
prometheus:
enabled: true
defaultGoMetrics: false
actions:
- title: Hello OliveTin
shell: echo "Hello OliveTin"

View File

@@ -0,0 +1,14 @@
# Integration Test Config: Sleep
#
listenAddressSingleHTTPFrontend: 0.0.0.0:1337
logLevel: "DEBUG"
checkForUpdates: false
actions:
- title: Sleep
shell: sleep 10
popupOnStart: execution-dialog
timeout: 9

View File

@@ -0,0 +1,3 @@
logLevel: DEBUG
authHttpHeaderUsername: "X-User"

View File

@@ -1,5 +0,0 @@
{
"baseUrl": "http://localhost:1337",
"screenshotsFolder": "results/screenshots/",
"videosFolder": "results/videos/"
}

View File

@@ -1,21 +0,0 @@
describe('Homepage rendering', () => {
beforeEach(() => {
cy.visit("/")
});
it("Footer contains promo", () => {
cy.get('footer').contains("OliveTin")
})
it('Default buttons are rendered', () => {
cy.get("#rootGroup button").should('have.length', 6)
})
it('Switcher navigation is visible', () => {
cy.get('#switcher').then($el => {
expect(Cypress.dom.isHidden($el)).to.be.false
})
})
});

View File

@@ -1,18 +0,0 @@
describe('Hidden Nav', () => {
beforeEach(() => {
cy.visit("/")
cy.wait(500)
});
it("Footer contains promo", () => {
cy.get('footer').contains("OliveTin")
})
it('Switcher navigation is hidden', () => {
cy.get('#switcher').then($el => {
expect(Cypress.dom.isHidden($el)).to.be.true
})
})
});

View File

@@ -1,22 +0,0 @@
/// <reference types="cypress" />
// ***********************************************************
// This example plugins/index.js can be used to load plugins
//
// You can change the location of this file or turn off loading
// the plugins file with the 'pluginsFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/plugins-guide
// ***********************************************************
// This function is called when a project is opened or re-opened (e.g. due to
// the project's config changing)
/**
* @type {Cypress.PluginConfig}
*/
// eslint-disable-next-line no-unused-vars
module.exports = (on, config) => {
// `on` is used to hook into various events Cypress emits
// `config` is the resolved Cypress config
}

View File

@@ -1,26 +0,0 @@
// ***********************************************
// This example commands.js shows you how to
// create various custom commands and overwrite
// existing commands.
//
// For more comprehensive examples of custom
// commands please read more here:
// https://on.cypress.io/custom-commands
// ***********************************************
//
//
// -- This is a parent command --
// Cypress.Commands.add('login', (email, password) => { ... })
//
//
// -- This is a child command --
// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })
//
//
// -- This is a dual command --
// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })
//
//
// -- This will overwrite an existing command --
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })

View File

@@ -1,20 +0,0 @@
// ***********************************************************
// This example support/index.js is processed and
// loaded automatically before your test files.
//
// This is a great place to put global configuration and
// behavior that modifies Cypress.
//
// You can change the location of this file or turn off
// automatically serving support files with the
// 'supportFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/configuration
// ***********************************************************
// Import commands.js using ES2015 syntax:
import './commands'
// Alternatively you can use CommonJS syntax:
// require('./commands')

View File

@@ -1,11 +0,0 @@
#!/bin/bash
set -o xtrace
echo "Running config $1"
cp -f ../configs/config.$1.yaml ./config/config.yaml
docker start olivetin
NO_COLOR=1 ./node_modules/.bin/cypress run --headless -s cypress/integration/$1/* || true
docker kill olivetin

View File

@@ -0,0 +1,8 @@
#!/bin/bash
# Run this like `. envVagrant.sh f38` before `mocha`
# args:
# $1: The Vagrant VM to test against. If blank and only one VM is provisioned, it will use that.
export IP=$(vagrant ssh-config $1 | grep HostName | awk '{print $2}')
export PORT=1337

View File

@@ -0,0 +1,57 @@
import { By } from 'selenium-webdriver'
import fs from 'fs'
import { expect } from 'chai'
import { Condition } from 'selenium-webdriver'
export async function getActionButtons (webdriver) {
return await webdriver.findElement(By.id('contentActions')).findElements(By.tagName('button'))
}
export function takeScreenshot (webdriver) {
return webdriver.takeScreenshot().then((img) => {
fs.writeFileSync('out.png', img, 'base64')
})
}
export async function getRootAndWait() {
await webdriver.get(runner.baseUrl())
await webdriver.wait(new Condition('wait for initial-marshal-complete', async function() {
const body = await webdriver.findElement(By.tagName('body'))
const attr = await body.getAttribute('initial-marshal-complete')
if (attr == 'true') {
return true
} else {
return false
}
}))
}
export async function requireExecutionDialogStatus (webdriver, expected) {
// It seems that webdriver will not give us text if domStatus is hidden (which it will be until complete)
await webdriver.executeScript('window.executionDialog.domExecutionDetails.hidden = false')
await webdriver.wait(new Condition('wait for action to be running', async function () {
const actual = await webdriver.executeScript('return window.executionDialog.domStatus.getText()')
if (actual === expected) {
return true
} else {
console.log('Waiting for domStatus text to be: ', expected, ', it is currently: ', actual)
console.log(await webdriver.executeScript('return window.executionDialog.res'))
return false
}
}))
}
export async function findExecutionDialog (webdriver) {
return webdriver.findElement(By.id('execution-results-popup'))
}
export async function getActionButton (webdriver, title) {
const buttons = await webdriver.findElements(By.css('[title="' + title + '"]'))
expect(buttons).to.have.length(1)
return buttons[0]
}

View File

@@ -0,0 +1,18 @@
import { Options } from 'selenium-webdriver/chrome.js'
import { Builder, Browser } from 'selenium-webdriver'
import getRunner from './runner.mjs'
export async function mochaGlobalSetup () {
const options = new Options()
options.addArguments('--headless')
global.webdriver = await new Builder().forBrowser(Browser.CHROME).setChromeOptions(options).build()
global.runner = getRunner()
console.log('Runner constructor: ' + global.runner.constructor.name)
}
export async function mochaGlobalTeardown () {
await global.webdriver.quit()
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,15 +1,22 @@
{
"name": "olivetin-integration-tests",
"version": "1.0.0",
"repository": "https://github.com/OliveTin/OliveTin-integration-tests",
"description": "The cypress WebUI tests",
"repository": "https://github.com/OliveTin/OliveTin",
"description": "The integration-tests for OliveTin's webui.",
"main": "index.js",
"type": "module",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"license": "AGPL-3.0-only",
"devDependencies": {
"chai": "^5.1.0",
"eslint": "^8.51.0",
"mocha": "^10.4.0",
"selenium-webdriver": "^4.19.0"
},
"dependencies": {
"cypress": "^8.3.0"
"wait-on": "^7.2.0"
}
}

View File

@@ -0,0 +1,3 @@
http://olivetin.example.com {
reverse_proxy * http://localhost:1337
}

View File

@@ -0,0 +1,15 @@
frontend cleartext_frontend
bind 0.0.0.0:80
option httplog
use_backend be_olivetin_webs if { hdr(Host) -i olivetin.example.com && path_beg /websocket }
use_backend be_olivetin_http if { hdr(Host) -i olivetin.example.com }
backend be_olivetin_http
server olivetinServer 127.0.0.1:1337 check
backend be_olivetin_webs
timeout tunnel 1h
option http-server-close
server olivetinServer 127.0.0.1:1337

View File

@@ -0,0 +1,9 @@
<VirtualHost *:80>
ServerName olivetin.example.com
ProxyPass / http://localhost:1337/
ProxyPassReverse / http://localhost:1337/
RewriteEngine On
RewriteCond %{REQUEST_URI} ^/websocket
RewriteRule /(.) ws://localhost:1337/websocket [P,L]
</VirtualHost>

View File

@@ -0,0 +1,22 @@
server {
listen 8443 ssl;
ssl_certificate "/etc/nginx/conf.d/server.crt";
ssl_certificate_key "/etc/nginx/conf.d/server.key";
access_log /var/log/nginx/ot.access.log main;
error_log /var/log/nginx/ot.error.log notice;
server_name olivetin.example.com;
location / {
proxy_pass http://host.containers.internal:1337/;
proxy_redirect http://host.containers.internal:1337/ http://host.containers.internal/OliveTin/;
}
location /websocket {
proxy_set_header Upgrade "websocket";
proxy_set_header Connection "upgrade";
proxy_pass http://host.containers.internal:1337/websocket;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,82 @@
# For more information on configuration, see:
# * Official English Documentation: http://nginx.org/en/docs/
# * Official Russian Documentation: http://nginx.org/ru/docs/
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log notice;
pid /run/nginx.pid;
# Load dynamic modules. See /usr/share/doc/nginx/README.dynamic.
include /usr/share/nginx/modules/*.conf;
events {
worker_connections 1024;
}
http {
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
tcp_nopush on;
keepalive_timeout 65;
types_hash_max_size 4096;
include /etc/nginx/mime.types;
default_type application/octet-stream;
# Load modular configuration files from the /etc/nginx/conf.d directory.
# See http://nginx.org/en/docs/ngx_core_module.html#include
# for more information.
include /etc/nginx/conf.d/*.conf;
server {
listen 80;
listen [::]:80;
server_name _;
root /usr/share/nginx/html;
# Load configuration files for the default server block.
include /etc/nginx/default.d/*.conf;
error_page 404 /404.html;
location = /404.html {
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
}
}
# Settings for a TLS enabled server.
#
# server {
# listen 8443 ssl http2;
# listen [::]:8443 ssl http2;
# server_name _;
# root /usr/share/nginx/html;
#
# ssl_certificate "/etc/pki/nginx/server.crt";
# ssl_certificate_key "/etc/pki/nginx/private/server.key";
# ssl_session_cache shared:SSL:1m;
# ssl_session_timeout 10m;
# ssl_ciphers PROFILE=SYSTEM;
# ssl_prefer_server_ciphers on;
#
# # Load configuration files for the default server block.
# include /etc/nginx/default.d/*.conf;
#
# error_page 404 /404.html;
# location = /404.html {
# }
#
# error_page 500 502 503 504 /50x.html;
# location = /50x.html {
# }
# }
}

View File

@@ -0,0 +1,137 @@
import * as process from 'node:process'
import waitOn from 'wait-on'
import { spawn } from 'node:child_process'
export default function getRunner () {
const type = process.env.OLIVETIN_TEST_RUNNER
console.log('OLIVETIN_TEST_RUNNER env value is: ', type)
switch (type) {
case 'local':
return new OliveTinTestRunnerStartLocalProcess()
case 'vm':
return new OliveTinTestRunnerVm()
case 'container':
return new OliveTinTestRunnerEnv()
default:
return new OliveTinTestRunnerStartLocalProcess()
}
}
class OliveTinTestRunner {
BASE_URL = 'http://nohost:1337/';
baseUrl() {
return this.BASE_URL
}
metricsUrl() {
return new URL('metrics', this.baseUrl());
}
}
class OliveTinTestRunnerStartLocalProcess extends OliveTinTestRunner {
async start (cfg) {
let stdout = ""
let stderr = ""
this.ot = spawn('./../OliveTin', ['-configdir', 'configs/' + cfg + '/'])
let logStdout = false
if (process.env.CI === 'true') {
logStdout = true;
} else {
logStdout = process.env.OLIVETIN_TEST_RUNNER_LOG_STDOUT === '1'
}
this.ot.stdout.on('data', (data) => {
stdout += data
if (logStdout) {
console.log(`stdout: ${data}`)
}
})
this.ot.stderr.on('data', (data) => {
stderr += data
if (logStdout) {
console.log(`stderr: ${data}`)
}
})
this.ot.on('close', (code) => {
if (code != null) {
console.log(`OliveTin local process exited with code ${code}`)
console.log(stdout)
console.log(stderr)
console.log(this.ot.exitCode)
}
})
if (this.ot.exitCode == null) {
this.BASE_URL = 'http://localhost:1337/'
await waitOn({
resources: [this.BASE_URL]
})
console.log(" OliveTin local process started and webUI accessible")
} else {
console.log(" OliveTin local process start FAILED!")
console.log(stdout)
console.log(stderr)
console.log(this.ot.exitCode)
}
}
async stop () {
if ((await this.ot.exitCode) != null) {
console.log(" OliveTin local process tried stop(), but it already exited with code", this.ot.exitCode)
} else {
await this.ot.kill()
console.log(" OliveTin local process killed")
}
await new Promise((res) => setTimeout(res, 100))
}
}
class OliveTinTestRunnerEnv extends OliveTinTestRunner {
constructor () {
super()
const IP = process.env.IP
const PORT = process.env.PORT
this.BASE_URL = 'http://' + IP + ':' + PORT + '/'
console.log('Runner ENV endpoint: ' + this.BASE_URL)
}
async start () {
await waitOn({
resources: [this.BASE_URL]
})
}
async stop () {
}
}
class OliveTinTestRunnerVm extends OliveTinTestRunnerEnv {
constructor() {
super()
}
async start (cfg) {
console.log("vagrant changing config")
spawn('vagrant', ['ssh', '-c', '"ln -sf /etc/OliveTin/ /opt/OliveTin-configs/' + cfg + '/config.yaml"'])
spawn('vagrant', ['ssh', '-c', '"systemctl restart OliveTin"'])
return null
}
}

View File

@@ -0,0 +1,30 @@
import { describe, it, before, after } from 'mocha'
import { expect } from 'chai'
import { By, until } from 'selenium-webdriver'
import { getRootAndWait, takeScreenshot } from '../lib/elements.js'
describe('config: entities', function () {
before(async function () {
await runner.start('entities')
})
after(async () => {
await runner.stop()
})
it('Entity buttons are rendered', async function() {
await getRootAndWait()
const buttons = await webdriver.findElement(By.id('root-group')).findElements(By.tagName('button'))
expect(buttons).to.not.be.null
expect(buttons).to.have.length(3)
expect(await buttons[0].getAttribute('title')).to.be.equal('Ping server1')
expect(await buttons[1].getAttribute('title')).to.be.equal('Ping server2')
expect(await buttons[2].getAttribute('title')).to.be.equal('Ping server3')
const dialogErr = await webdriver.findElement(By.id('big-error'))
expect(dialogErr).to.not.be.null
expect(await dialogErr.isDisplayed()).to.be.false
})
})

View File

@@ -0,0 +1,95 @@
import { describe, it, before, after } from 'mocha'
import { expect } from 'chai'
import { By, until, Condition } from 'selenium-webdriver'
//import * as waitOn from 'wait-on'
import { getRootAndWait, getActionButtons } from '../lib/elements.js'
describe('config: general', function () {
before(async function () {
await runner.start('general')
})
after(async () => {
await runner.stop()
})
it('Page title', async function () {
await webdriver.get(runner.baseUrl())
const title = await webdriver.getTitle()
expect(title).to.be.equal("OliveTin")
})
it('Page title2', async function () {
/*
await webdriver.get(runner.baseUrl())
const title = await webdriver.getTitle()
expect(title).to.be.equal("OliveTin")
*/
})
it('Footer contains promo', async function () {
const ftr = await webdriver.findElement(By.tagName('footer')).getText()
expect(ftr).to.contain('Documentation')
})
it('Default buttons are rendered', async function() {
await getRootAndWait()
const buttons = await getActionButtons(webdriver)
expect(buttons).to.have.length(8)
})
it('Start dir action (popup)', async function () {
await getRootAndWait()
const buttons = await webdriver.findElements(By.css('[title="dir-popup"]'))
expect(buttons).to.have.length(1)
const buttonCMD = buttons[0]
expect(buttonCMD).to.not.be.null
buttonCMD.click()
const dialog = await webdriver.findElement(By.id('execution-results-popup'))
expect(await dialog.isDisplayed()).to.be.true
const title = await webdriver.findElement(By.id('execution-dialog-title'))
expect(await webdriver.wait(until.elementTextIs(title, 'dir-popup'), 2000))
const dialogErr = await webdriver.findElement(By.id('big-error'))
expect(dialogErr).to.not.be.null
expect(await dialogErr.isDisplayed()).to.be.false
})
it('Start cd action (passive)', async function () {
await getRootAndWait()
const buttons = await webdriver.findElements(By.css('[title="cd-passive"]'))
expect(buttons).to.have.length(1)
const buttonCMD = buttons[0]
expect(buttonCMD).to.not.be.null
buttonCMD.click()
const dialog = await webdriver.findElement(By.id('execution-results-popup'))
expect(await dialog.isDisplayed()).to.be.false
const title = await webdriver.findElement(By.id('execution-dialog-title'))
expect(await title.getAttribute('innerText')).to.be.equal('?')
const dialogErr = await webdriver.findElement(By.id('big-error'))
expect(dialogErr).to.not.be.null
expect(await dialogErr.isDisplayed()).to.be.false
})
})

View File

@@ -0,0 +1,22 @@
import { describe, it, before, after } from 'mocha'
import { expect } from 'chai'
import { By } from 'selenium-webdriver'
describe('config: hiddenFooter', function () {
before(async function () {
await runner.start('hiddenFooter')
})
after(async () => {
await runner.stop()
})
it('Check that footer is hidden', async () => {
await webdriver.get(runner.baseUrl())
const footer = await webdriver.findElement(By.tagName('footer'))
expect(await footer.isDisplayed()).to.be.false
})
})

View File

@@ -0,0 +1,20 @@
import { expect } from 'chai'
import { By } from 'selenium-webdriver'
describe('config: hiddenNav', function () {
before(async function () {
await runner.start('hiddenNav')
})
after(async () => {
await runner.stop()
})
it('nav is hidden', async () => {
await webdriver.get(runner.baseUrl())
const toggler = await webdriver.findElement(By.tagName('header'))
expect(await toggler.isDisplayed()).to.be.false
})
})

View File

@@ -0,0 +1,44 @@
import { describe, it, before, after } from 'mocha'
import { expect } from 'chai'
import { By, until } from 'selenium-webdriver'
import { getActionButtons, getRootAndWait } from '../lib/elements.js'
describe('config: multipleDropdowns', function () {
before(async function () {
await runner.start('multipleDropdowns')
})
after(async () => {
await runner.stop()
})
it('Multiple dropdowns are possible', async function() {
await getRootAndWait()
const buttons = await getActionButtons(webdriver)
let button = null
for (const b of buttons) {
const title = await b.getAttribute('title')
if (title === 'Test multiple dropdowns') {
button = b
}
}
expect(buttons).to.have.length(2)
expect(button).to.not.be.null
await button.click()
const dialog = await webdriver.findElement(By.id('argument-popup'))
await webdriver.wait(until.elementIsVisible(dialog), 3500)
const selects = await dialog.findElements(By.tagName('select'))
expect(selects).to.have.length(2)
expect(await selects[0].findElements(By.tagName('option'))).to.have.length(2)
expect(await selects[1].findElements(By.tagName('option'))).to.have.length(3)
})
})

View File

@@ -0,0 +1,33 @@
import { describe, it, before, after } from 'mocha'
import { expect } from 'chai'
import { By } from 'selenium-webdriver'
let metrics = [
{'name': 'olivetin_actions_requested_count', 'type': 'counter', 'desc': 'The actions requested count'},
{'name': 'olivetin_config_action_count', 'type': 'gauge', 'desc': 'The number of actions in the config file'},
{'name': 'olivetin_config_reloaded_count', 'type': 'counter', 'desc': 'The number of times the config has been reloaded'},
{'name': 'olivetin_sv_count', 'type': 'gauge', 'desc': 'The number entries in the sv map'},
]
describe('config: prometheus', function () {
before(async function () {
await runner.start('prometheus')
})
after(async () => {
await runner.stop()
})
it('Metrics are available with correct types', async () => {
webdriver.get(runner.metricsUrl())
const prometheusOutput = await webdriver.findElement(By.tagName('pre')).getText()
expect(prometheusOutput).to.not.be.null
metrics.forEach(({name, type, desc}) => {
const metaLines = `# HELP ${name} ${desc}\n`
+ `# TYPE ${name} ${type}\n`
expect(prometheusOutput).to.match(new RegExp(metaLines))
})
})
})

View File

@@ -0,0 +1,48 @@
import * as process from 'node:process'
import { describe, it, before, after } from 'mocha'
import { expect } from 'chai'
import { By, Condition } from 'selenium-webdriver'
import {
takeScreenshot,
findExecutionDialog,
requireExecutionDialogStatus,
getRootAndWait,
getActionButton
} from '../lib/elements.js'
describe('config: sleep', function () {
before(async function () {
await runner.start('sleep')
})
after(async () => {
await runner.stop()
})
it('Sleep action kill', async function() {
await getRootAndWait()
const btnSleep = await getActionButton(webdriver, "Sleep")
const dialog = await findExecutionDialog(webdriver)
expect(await dialog.isDisplayed()).to.be.false
await btnSleep.click()
expect(await dialog.isDisplayed()).to.be.true
await requireExecutionDialogStatus(webdriver, "unknown")
const killButton = await webdriver.findElement(By.id('execution-dialog-kill-action'))
expect(killButton).to.not.be.undefined
await killButton.click()
console.log("env CI:", process.env.CI)
if (process.env.CI !== 'true') {
await requireExecutionDialogStatus(webdriver, "Non-Zero Exit")
}
})
})

View File

@@ -0,0 +1,32 @@
import { expect } from 'chai'
describe('config: trustedHeader', function () {
before(async function () {
await runner.start('trustedHeader')
})
after(async () => {
await runner.stop()
})
it('req with X-User', async () => {
const req = await fetch(runner.baseUrl() + '/api/WhoAmI', {
headers: {
"X-User": "fred",
}
})
if (!req.ok) {
console.log(req)
}
expect(req.ok, 'WhoAmI Request is ' + req.status).to.be.true
const json = await req.json()
expect(json).to.not.be.null
expect(json).to.have.own.property('authenticatedUser')
expect(json['authenticatedUser']).to.be.equal('fred')
})
})

View File

@@ -2,81 +2,235 @@ package acl
import (
"context"
config "github.com/jamesread/OliveTin/internal/config"
config "github.com/OliveTin/OliveTin/internal/config"
log "github.com/sirupsen/logrus"
"golang.org/x/exp/slices"
"google.golang.org/grpc/metadata"
)
type User struct {
Username string
type PermissionBits int
const (
View PermissionBits = 1 << iota
Exec
Logs
)
func (p PermissionBits) Has(permission PermissionBits) bool {
return p&permission != 0
}
func IsAllowedExec(cfg *config.Config, user *User, action *config.Action) bool {
canExec := cfg.DefaultPermissions.Exec
// User respresents a person.
type AuthenticatedUser struct {
Username string
Usergroup string
log.WithFields(log.Fields{
"User": user.Username,
"Action": action.Title,
"CanExec": canExec,
}).Debug("isAllowedExec Permission Default")
Provider string
SID string
for _, permissionEntry := range action.Permissions {
if isUserInGroup(user, permissionEntry.Usergroup) {
log.WithFields(log.Fields{
"User": user.Username,
"Action": action.Title,
"CanExec": permissionEntry.Exec,
}).Debug("isAllowedExec Permission Entry")
acls []string
}
canExec = permissionEntry.Exec
func logAclNotMatched(cfg *config.Config, aclFunction string, user *AuthenticatedUser, action *config.Action, acl *config.AccessControlList) {
if cfg.LogDebugOptions.AclNotMatched {
log.WithFields(log.Fields{
"User": user.Username,
"Action": action.Title,
}).Debugf("%v - No ACLs Matched", aclFunction)
}
}
func logAclMatched(cfg *config.Config, aclFunction string, user *AuthenticatedUser, action *config.Action, acl *config.AccessControlList) {
if cfg.LogDebugOptions.AclMatched {
log.WithFields(log.Fields{
"User": user.Username,
"Action": action.Title,
"ACL": acl.Name,
}).Debugf("%v - Matched ACL", aclFunction)
}
}
func logAclNoneMatched(cfg *config.Config, aclFunction string, user *AuthenticatedUser, action *config.Action, defaultPermission bool) {
if cfg.LogDebugOptions.AclNoneMatched {
log.WithFields(log.Fields{
"User": user.Username,
"Action": action.Title,
"Default": defaultPermission,
}).Debugf("%v - No ACLs Matched, returning default permission", aclFunction)
}
}
func permissionsConfigToBits(permissions config.PermissionsList) PermissionBits {
var ret PermissionBits
if permissions.View {
ret |= View
}
if permissions.Exec {
ret |= Exec
}
if permissions.Logs {
ret |= Logs
}
return ret
}
func aclCheck(requiredPermission PermissionBits, defaultValue bool, cfg *config.Config, aclFunction string, user *AuthenticatedUser, action *config.Action) bool {
relevantAcls := getRelevantAcls(cfg, action.Acls, user)
if cfg.LogDebugOptions.AclCheckStarted {
log.WithFields(log.Fields{
"actionTitle": action.Title,
"username": user.Username,
"usergroup": user.Usergroup,
"relevantAcls": len(relevantAcls),
"requiredPermission": requiredPermission,
}).Debugf("ACL check - %v", aclFunction)
}
for _, acl := range relevantAcls {
permissionBits := permissionsConfigToBits(acl.Permissions)
if permissionBits.Has(requiredPermission) {
logAclMatched(cfg, aclFunction, user, action, acl)
return true
} else {
logAclNotMatched(cfg, aclFunction, user, action, acl)
}
}
log.WithFields(log.Fields{
"User": user.Username,
"Action": action.Title,
"CanExec": canExec,
}).Debug("isAllowedExec Final Result")
logAclNoneMatched(cfg, aclFunction, user, action, cfg.DefaultPermissions.Logs)
return canExec
return defaultValue
}
func IsAllowedView(cfg *config.Config, user *User, action *config.Action) bool {
canView := cfg.DefaultPermissions.View
// IsAllowedLogs checks if a AuthenticatedUser is allowed to view an action's logs
func IsAllowedLogs(cfg *config.Config, user *AuthenticatedUser, action *config.Action) bool {
return aclCheck(Logs, cfg.DefaultPermissions.Logs, cfg, "isAllowedLogs", user, action)
}
// IsAllowedExec checks if a AuthenticatedUser is allowed to execute an Action
func IsAllowedExec(cfg *config.Config, user *AuthenticatedUser, action *config.Action) bool {
return aclCheck(Exec, cfg.DefaultPermissions.Exec, cfg, "isAllowedExec", user, action)
}
// IsAllowedView checks if a User is allowed to view an Action
func IsAllowedView(cfg *config.Config, user *AuthenticatedUser, action *config.Action) bool {
if action.Hidden {
return false
}
return aclCheck(View, cfg.DefaultPermissions.View, cfg, "isAllowedView", user, action)
}
func getMetadataKeyOrEmpty(md metadata.MD, key string) string {
mdValues := md.Get(key)
if len(mdValues) > 0 {
return mdValues[0]
}
return ""
}
// UserFromContext tries to find a user from a grpc context
func UserFromContext(ctx context.Context, cfg *config.Config) *AuthenticatedUser {
var ret *AuthenticatedUser
md, ok := metadata.FromIncomingContext(ctx)
if ok {
ret = &AuthenticatedUser{}
ret.Username = getMetadataKeyOrEmpty(md, "username")
ret.Usergroup = getMetadataKeyOrEmpty(md, "usergroup")
ret.Provider = getMetadataKeyOrEmpty(md, "provider")
buildUserAcls(cfg, ret)
}
if !ok || ret.Username == "" {
ret = UserGuest(cfg)
}
log.WithFields(log.Fields{
"User": user.Username,
"Action": action.Title,
"CanView": canView,
}).Debug("isAllowedView Permission Default")
"username": ret.Username,
"usergroup": ret.Usergroup,
"provider": ret.Provider,
}).Debugf("UserFromContext")
for idx, permissionEntry := range action.Permissions {
if isUserInGroup(user, permissionEntry.Usergroup) {
log.WithFields(log.Fields{
"User": user.Username,
"Action": action.Title,
"CanView": permissionEntry.View,
"Index": idx,
}).Debug("isAllowedView Permission Entry")
return ret
}
canView = permissionEntry.View
func UserGuest(cfg *config.Config) *AuthenticatedUser {
ret := &AuthenticatedUser{}
ret.Username = "guest"
ret.Usergroup = "guest"
ret.Provider = "system"
buildUserAcls(cfg, ret)
return ret
}
func UserFromSystem(cfg *config.Config, username string) *AuthenticatedUser {
ret := &AuthenticatedUser{
Username: username,
Usergroup: "system",
Provider: "system",
}
buildUserAcls(cfg, ret)
return ret
}
func buildUserAcls(cfg *config.Config, user *AuthenticatedUser) {
for _, acl := range cfg.AccessControlLists {
if slices.Contains(acl.MatchUsernames, user.Username) {
user.acls = append(user.acls, acl.Name)
continue
}
if slices.Contains(acl.MatchUsergroups, user.Usergroup) {
user.acls = append(user.acls, acl.Name)
continue
}
}
}
func isACLRelevantToAction(cfg *config.Config, actionAcls []string, acl *config.AccessControlList, user *AuthenticatedUser) bool {
if !slices.Contains(user.acls, acl.Name) {
// If the user does not have this ACL, then it is not relevant
return false
}
if acl.AddToEveryAction {
return true
}
if slices.Contains(actionAcls, acl.Name) {
return true
}
return false
}
func getRelevantAcls(cfg *config.Config, actionAcls []string, user *AuthenticatedUser) []*config.AccessControlList {
var ret []*config.AccessControlList
for _, acl := range cfg.AccessControlLists {
if isACLRelevantToAction(cfg, actionAcls, acl, user) {
ret = append(ret, acl)
}
}
log.WithFields(log.Fields{
"User": user.Username,
"Action": action.Title,
"CanView": canView,
}).Debug("isAllowedView Final Result")
return canView
}
func isUserInGroup(user *User, usergroup string) bool {
return true
}
func UserFromContext(ctx context.Context) *User {
return &User{
Username: "Guest",
}
return ret
}

View File

@@ -1,89 +1,240 @@
package config
import ()
import (
"fmt"
)
// Action represents the core functionality of OliveTin - commands that show up
// as buttons in the UI.
type Action struct {
ID string
Title string
Icon string
Shell string
CSS map[string]string `mapstructure:"omitempty"`
Timeout int
Permissions []PermissionsEntry
Arguments []ActionArgument
ID string
Title string
Icon string
Shell string
ShellAfterCompleted string
Timeout int
Acls []string
Entity string
Hidden bool
ExecOnStartup bool
ExecOnCron []string
ExecOnFileCreatedInDir []string
ExecOnFileChangedInDir []string
ExecOnCalendarFile string
Trigger string
MaxConcurrent int
MaxRate []RateSpec
Arguments []ActionArgument
PopupOnStart string
SaveLogs SaveLogsConfig
}
// ActionArgument objects appear on Actions.
type ActionArgument struct {
Name string
Title string
Type string
Default string
Choices []ActionArgumentChoice
Name string
Title string
Description string
Type string
Default string
Choices []ActionArgumentChoice
Entity string
RejectNull bool
Suggestions map[string]string
}
// ActionArgumentChoice represents a predefined choice for an argument.
type ActionArgumentChoice struct {
Value string
Title string
}
// RateSpec allows you to set a max frequency for an action.
type RateSpec struct {
Limit int
Duration string
}
// Entity represents a "thing" that can have multiple actions associated with it.
// for example, a media player with a start and stop action.
type Entity struct {
Title string
Icon string
Actions []Action `mapstructure:"actions"`
CSS map[string]string
type EntityFile struct {
File string
Name string
Icon string
}
type PermissionsEntry struct {
Usergroup string
View bool
Exec bool
}
type DefaultPermissions struct {
// PermissionsList defines what users can do with an action.
type PermissionsList struct {
View bool
Exec bool
Logs bool
}
type UserGroup struct {
Name string
Members []string
// AccessControlList defines what permissions apply to a user or user group.
type AccessControlList struct {
Name string
AddToEveryAction bool
MatchUsergroups []string
MatchUsernames []string
Permissions PermissionsList
}
type PrometheusConfig struct {
Enabled bool
DefaultGoMetrics bool
}
// Config is the global config used through the whole app.
type Config struct {
UseSingleHTTPFrontend bool
ThemeName string
HideNavigation bool
ThemeCacheDisabled bool
ListenAddressSingleHTTPFrontend string
ListenAddressWebUI string
ListenAddressRestActions string
ListenAddressGrpcActions string
ListenAddressPrometheus string
ExternalRestAddress string
LogLevel string
Actions []Action `mapstructure:"actions"`
Entities []Entity `mapstructure:"entities"`
LogDebugOptions LogDebugOptions
Actions []*Action `mapstructure:"actions"`
Entities []*EntityFile `mapstructure:"entities"`
Dashboards []*DashboardComponent `mapstructure:"dashboards"`
CheckForUpdates bool
PageTitle string
ShowFooter bool
ShowNavigation bool
ShowNewVersions bool
Usergroups []UserGroup
DefaultPermissions DefaultPermissions
EnableCustomJs bool
AuthJwtCookieName string
AuthJwtAud string
AuthJwtDomain string
AuthJwtCertsURL string
AuthJwtHmacSecret string // mutually exclusive with pub key config fields
AuthJwtClaimUsername string
AuthJwtClaimUserGroup string
AuthJwtPubKeyPath string // will read pub key from file on disk
AuthHttpHeaderUsername string
AuthHttpHeaderUserGroup string
AuthLocalUsers AuthLocalUsersConfig
AuthLoginUrl string
AuthAllowGuest bool
AuthOAuth2RedirectURL string
AuthOAuth2Providers map[string]*OAuth2Provider
DefaultPermissions PermissionsList
AccessControlLists []*AccessControlList
WebUIDir string
CronSupportForSeconds bool
SectionNavigationStyle string
DefaultPopupOnStart string
InsecureAllowDumpVars bool
InsecureAllowDumpSos bool
InsecureAllowDumpActionMap bool
InsecureAllowDumpJwtClaims bool
Prometheus PrometheusConfig
SaveLogs SaveLogsConfig
DefaultIconForActions string
DefaultIconForDirectories string
DefaultIconForBack string
AdditionalNavigationLinks []*NavigationLink
usedConfigDir string
}
type AuthLocalUsersConfig struct {
Enabled bool
Users []*LocalUser
}
type LocalUser struct {
Username string
Password string
Groups []string
}
type OAuth2Provider struct {
Name string
Title string
ClientID string
ClientSecret string
Icon string
Scopes []string
AuthUrl string
TokenUrl string
WhoamiUrl string
UsernameField string
}
type NavigationLink struct {
Title string
Url string
Target string
}
type SaveLogsConfig struct {
ResultsDirectory string
OutputDirectory string
}
type LogDebugOptions struct {
SingleFrontendRequests bool
SingleFrontendRequestHeaders bool
AclCheckStarted bool
AclMatched bool
AclNotMatched bool
AclNoneMatched bool
}
type DashboardComponent struct {
Title string
Type string
Entity string
Icon string
CssClass string
Contents []DashboardComponent
}
func DefaultConfig() *Config {
return DefaultConfigWithBasePort(1337)
}
// DefaultConfig gets a new Config structure with sensible default values.
func DefaultConfig() *Config {
func DefaultConfigWithBasePort(basePort int) *Config {
config := Config{}
config.UseSingleHTTPFrontend = true
config.HideNavigation = false
config.ListenAddressSingleHTTPFrontend = "0.0.0.0:1337"
config.ListenAddressRestActions = "localhost:1338"
config.ListenAddressGrpcActions = "localhost:1339"
config.ListenAddressWebUI = "localhost:1340"
config.LogLevel = "INFO"
config.CheckForUpdates = true
config.PageTitle = "OliveTin"
config.ShowFooter = true
config.ShowNavigation = true
config.ShowNewVersions = true
config.EnableCustomJs = false
config.ExternalRestAddress = "."
config.LogLevel = "INFO"
config.CheckForUpdates = false
config.DefaultPermissions.Exec = true
config.DefaultPermissions.View = true
config.DefaultPermissions.Logs = true
config.AuthJwtClaimUsername = "name"
config.AuthJwtClaimUserGroup = "group"
config.AuthAllowGuest = true
config.WebUIDir = "./webui"
config.CronSupportForSeconds = false
config.SectionNavigationStyle = "sidebar"
config.DefaultPopupOnStart = "nothing"
config.InsecureAllowDumpVars = false
config.InsecureAllowDumpSos = false
config.InsecureAllowDumpActionMap = false
config.InsecureAllowDumpJwtClaims = false
config.Prometheus.Enabled = false
config.Prometheus.DefaultGoMetrics = false
config.DefaultIconForActions = "&#x1F600;"
config.DefaultIconForDirectories = "&#128193"
config.DefaultIconForBack = "&laquo;"
config.ThemeCacheDisabled = false
config.ListenAddressSingleHTTPFrontend = fmt.Sprintf("0.0.0.0:%d", basePort)
config.ListenAddressRestActions = fmt.Sprintf("localhost:%d", basePort+1)
config.ListenAddressGrpcActions = fmt.Sprintf("localhost:%d", basePort+2)
config.ListenAddressWebUI = fmt.Sprintf("localhost:%d", basePort+3)
config.ListenAddressPrometheus = fmt.Sprintf("localhost:%d", basePort+4)
return &config
}

View File

@@ -1,11 +1,52 @@
package config
// FindAction will return a action if there is a match on Title
func (cfg *Config) FindAction(actionTitle string) *Action {
for _, action := range cfg.Actions {
if action.Title == actionTitle {
return &action
return action
}
}
return nil
}
// FindArg will return an arg if there is a match on Name
func (action *Action) FindArg(name string) *ActionArgument {
if name == "stdout" || name == "exitCode" {
return &ActionArgument{
Name: name,
Type: "very_dangerous_raw_string",
}
}
return action.findArg(name)
}
func (action *Action) findArg(name string) *ActionArgument {
for _, arg := range action.Arguments {
if arg.Name == name {
return &arg
}
}
return nil
}
func (cfg *Config) FindAcl(aclTitle string) *AccessControlList {
for _, acl := range cfg.AccessControlLists {
if acl.Name == aclTitle {
return acl
}
}
return nil
}
func (cfg *Config) SetDir(dir string) {
cfg.usedConfigDir = dir
}
func (cfg *Config) GetDir() string {
return cfg.usedConfigDir
}

View File

@@ -0,0 +1,53 @@
package config
import (
"github.com/stretchr/testify/assert"
"testing"
)
func TestFindAction(t *testing.T) {
c := DefaultConfig()
a1 := &Action{}
a1.Title = "a1"
c.Actions = append(c.Actions, a1)
a2 := &Action{
Title: "a2",
Arguments: []ActionArgument{
{
Name: "Blat",
},
},
}
c.Actions = append(c.Actions, a2)
assert.NotNil(t, c.FindAction("a1"), "Find action a1")
assert.NotNil(t, c.FindAction("a2"), "Find action a2")
assert.NotNil(t, c.FindAction("a2").FindArg("Blat"), "Find action argument")
assert.Nil(t, c.FindAction("a2").FindArg("Blatey Cake"), "Find non-existent action argument")
assert.Nil(t, c.FindAction("waffles"), "Find non-existent action")
}
func TestFindAcl(t *testing.T) {
c := DefaultConfig()
acl1 := &AccessControlList{
Name: "Testing ACL",
}
c.AccessControlLists = append(c.AccessControlLists, acl1)
assert.NotNil(t, c.FindAcl("Testing ACL"), "Find a ACL that should exist")
assert.Nil(t, c.FindAcl("Chocolate Cake"), "Find a ACL that does not exist")
}
func TestSetDir(t *testing.T) {
c := DefaultConfig()
c.SetDir("test")
assert.Equal(t, "test", c.GetDir(), "SetDir")
}

View File

@@ -0,0 +1,48 @@
package config
import (
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
"os"
"path/filepath"
log "github.com/sirupsen/logrus"
"github.com/spf13/viper"
)
var (
metricConfigActionCount = promauto.NewGauge(prometheus.GaugeOpts{
Name: "olivetin_config_action_count",
Help: "The number of actions in the config file",
})
metricConfigReloadedCount = promauto.NewCounter(prometheus.CounterOpts{
Name: "olivetin_config_reloaded_count",
Help: "The number of times the config has been reloaded",
})
listeners []func()
)
func AddListener(l func()) {
listeners = append(listeners, l)
}
func Reload(cfg *Config) {
if err := viper.UnmarshalExact(&cfg); err != nil {
log.Errorf("Config unmarshal error %+v", err)
os.Exit(1)
}
metricConfigReloadedCount.Inc()
metricConfigActionCount.Set(float64(len(cfg.Actions)))
cfg.SetDir(filepath.Dir(viper.ConfigFileUsed()))
cfg.Sanitize()
for _, l := range listeners {
l()
}
}

View File

@@ -1,12 +1,28 @@
package config
var emojis = map[string]string{
"poop": "&#x1f4a9;",
"smile": "&#x1F600;",
"ping": "&#x1f4e1;",
"poop": "&#x1f4a9;",
"smile": "&#x1F600;",
"ping": "&#x1f4e1;",
"backup": "&#128190;",
"reboot": "&#128260;",
"restart": "&#128260;",
"box": "&#128230;",
"ashtonished": "&#128562;",
"clock": "&#128338;",
"disk": "&#128189;",
"logs": "&#128269;",
"light": "&#128161;",
"robot": "&#129302;",
"ssh": "&#128272;",
"theme": "&#127912;",
}
func lookupHTMLIcon(keyToLookup string) string {
func lookupHTMLIcon(keyToLookup string, defaultIcon string) string {
if keyToLookup == "" {
return defaultIcon
}
if emoji, found := emojis[keyToLookup]; found {
return emoji
}

View File

@@ -6,7 +6,9 @@ import (
)
func TestGetEmojiByShortName(t *testing.T) {
assert.Equal(t, "&#x1F600;", lookupHTMLIcon("smile"), "Find an eomji by short name")
assert.Equal(t, "&#x1F600;", lookupHTMLIcon("smile", "empty"), "Find an eomji by short name")
assert.Equal(t, "notfound", lookupHTMLIcon("notfound"), "Find an eomji by undefined short name")
assert.Equal(t, "empty", lookupHTMLIcon("", "empty"), "Find an eomji when the value is empty")
assert.Equal(t, "notfound", lookupHTMLIcon("notfound", "empty"), "Find an eomji by undefined short name")
}

View File

@@ -1,50 +1,91 @@
package config
import (
"github.com/google/uuid"
log "github.com/sirupsen/logrus"
"strings"
)
func Sanitize(cfg *Config) {
sanitizeLogLevel(cfg)
// Sanitize will look for common configuration issues, and fix them. For example,
// populating undefined fields - name -> title, etc.
func (cfg *Config) Sanitize() {
cfg.sanitizeLogLevel()
//log.Infof("cfg %p", cfg)
// log.Infof("cfg %p", cfg)
for idx, _ := range cfg.Actions {
sanitizeAction(&cfg.Actions[idx])
for idx := range cfg.Actions {
cfg.Actions[idx].sanitize(cfg)
}
}
func sanitizeLogLevel(cfg *Config) {
func (cfg *Config) sanitizeLogLevel() {
if logLevel, err := log.ParseLevel(cfg.LogLevel); err == nil {
log.Info("Setting log level to ", logLevel)
log.SetLevel(logLevel)
}
}
func sanitizeAction(action *Action) {
func (action *Action) sanitize(cfg *Config) {
if action.Timeout < 3 {
action.Timeout = 3
}
action.Icon = lookupHTMLIcon(action.Icon)
action.ID = getActionID(action)
action.Icon = lookupHTMLIcon(action.Icon, cfg.DefaultIconForActions)
action.PopupOnStart = sanitizePopupOnStart(action.PopupOnStart, cfg)
for idx, _ := range action.Arguments {
sanitizeActionArgument(&action.Arguments[idx])
if action.MaxConcurrent < 1 {
action.MaxConcurrent = 1
}
for idx := range action.Arguments {
action.Arguments[idx].sanitize()
}
}
func sanitizeActionArgument(arg *ActionArgument) {
func getActionID(action *Action) string {
if action.ID == "" {
return uuid.NewString()
}
if strings.Contains(action.ID, "{{") {
log.Fatalf("Action IDs cannot contain variables")
}
return action.ID
}
func sanitizePopupOnStart(raw string, cfg *Config) string {
switch raw {
case "execution-dialog":
return raw
case "execution-dialog-stdout-only":
return raw
case "execution-button":
return raw
default:
return cfg.DefaultPopupOnStart
}
}
func (arg *ActionArgument) sanitize() {
if arg.Title == "" {
arg.Title = arg.Name
}
sanitizeActionArgumentNoType(arg)
for idx, choice := range arg.Choices {
if choice.Title == "" {
arg.Choices[idx].Title = choice.Value
}
}
arg.sanitizeNoType()
// TODO Validate the default against the type checker, but this creates a
// import loop
}
func sanitizeActionArgumentNoType(arg *ActionArgument) {
func (arg *ActionArgument) sanitizeNoType() {
if len(arg.Choices) == 0 && arg.Type == "" {
log.WithFields(log.Fields{
"arg": arg.Name,

View File

@@ -0,0 +1,38 @@
package config
import (
"github.com/stretchr/testify/assert"
"testing"
)
func TestSanitizeConfig(t *testing.T) {
c := DefaultConfig()
a := &Action{
Title: "Mr Waffles",
Arguments: []ActionArgument{
{
Name: "Carrots",
Choices: []ActionArgumentChoice{
{
Value: "Waffle",
},
},
},
{
Name: "foobar",
},
},
}
c.Actions = append(c.Actions, a)
c.Sanitize()
a2 := c.FindAction("Mr Waffles")
assert.NotNil(t, a2, "Found action after adding it")
assert.Equal(t, 3, a2.Timeout, "Default timeout is set")
assert.Equal(t, "&#x1F600;", a2.Icon, "Default icon is a smiley")
assert.Equal(t, "Carrots", a2.Arguments[0].Title, "Arg title is set to name")
assert.Equal(t, "Waffle", a2.Arguments[0].Choices[0].Title, "Choice title is set to name")
}

View File

@@ -13,7 +13,7 @@ import (
func AllowCors(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if origin := r.Header.Get("Origin"); origin != "" {
log.Debugf("Adding CORS header origin: %v", origin)
log.Debugf("Adding CORS header origin: %q", origin)
w.Header().Set("Access-Control-Allow-Origin", origin)
}

View File

@@ -0,0 +1,162 @@
package entityfiles
import (
"bytes"
"encoding/json"
"fmt"
config "github.com/OliveTin/OliveTin/internal/config"
"github.com/OliveTin/OliveTin/internal/filehelper"
sv "github.com/OliveTin/OliveTin/internal/stringvariables"
log "github.com/sirupsen/logrus"
"gopkg.in/yaml.v3"
"os"
"path/filepath"
"strings"
)
var (
EntityChangedSender chan bool
listeners []func()
)
func AddListener(l func()) {
listeners = append(listeners, l)
}
func SetupEntityFileWatchers(cfg *config.Config) {
configDir := cfg.GetDir()
configDirVar := filepath.Join(configDir, "var") // for development purposes
if _, err := os.Stat(configDirVar); err == nil {
configDir = configDirVar
}
for entityIndex, _ := range cfg.Entities { // #337 - iterate by key, not by value
ef := cfg.Entities[entityIndex]
p := ef.File
if !filepath.IsAbs(p) {
p = filepath.Join(configDir, p)
log.WithFields(log.Fields{
"entityFile": p,
}).Debugf("Adding config dir to entity file path")
}
go filehelper.WatchFileWrite(p, func(filename string) {
loadEntityFile(p, ef.Name)
})
loadEntityFile(p, ef.Name)
}
}
func loadEntityFile(filename string, entityname string) {
if strings.HasSuffix(filename, ".json") {
loadEntityFileJson(filename, entityname)
} else {
loadEntityFileYaml(filename, entityname)
}
}
func loadEntityFileJson(filename string, entityname string) {
log.WithFields(log.Fields{
"file": filename,
"name": entityname,
}).Infof("Loading entity file with JSON format")
jfile, err := os.ReadFile(filename)
if err != nil {
log.Errorf("ReadIn: %v", err)
return
}
data := make([]map[string]interface{}, 0)
decoder := json.NewDecoder(bytes.NewReader(jfile))
for decoder.More() {
d := make(map[string]interface{})
err := decoder.Decode(&d)
if err != nil {
log.Errorf("%v", err)
return
}
data = append(data, d)
}
updateSvFromFile(entityname, data)
}
func loadEntityFileYaml(filename string, entityname string) {
log.WithFields(log.Fields{
"file": filename,
"name": entityname,
}).Infof("Loading entity file with YAML format")
yfile, err := os.ReadFile(filename)
if err != nil {
log.Errorf("ReadIn: %v", err)
return
}
data := make([]map[string]interface{}, 1)
err = yaml.Unmarshal(yfile, &data)
if err != nil {
log.Errorf("Unmarshal: %v", err)
}
updateSvFromFile(entityname, data)
}
func updateSvFromFile(entityname string, data []map[string]interface{}) {
log.Debugf("updateSvFromFile: %+v", data)
count := len(data)
sv.RemoveKeysThatStartWith("entities." + entityname)
sv.SetEntityCount(entityname, count)
for i, mapp := range data {
prefix := "entities." + entityname + "." + fmt.Sprintf("%v", i)
serializeValueToSv(prefix, mapp)
}
for _, l := range listeners {
l()
}
}
func serializeValueToSv(prefix string, value interface{}) {
if m, ok := value.(map[string]interface{}); ok { // if value is a map we need to flatten it
serializeMapToSv(prefix, m)
} else if s, ok := value.([]interface{}); ok { // if value is a slice we need to flatten it
serializeSliceToSv(prefix, s)
} else {
sv.Set(prefix, fmt.Sprintf("%v", value))
}
}
func serializeMapToSv(prefix string, m map[string]interface{}) {
for k, v := range m {
serializeValueToSv(prefix+"."+k, v)
}
}
func serializeSliceToSv(prefix string, s []interface{}) {
sv.Set(prefix+".count", fmt.Sprintf("%v", len(s)))
for i, v := range s {
serializeValueToSv(prefix+"."+fmt.Sprintf("%v", i), v)
}
}

View File

@@ -0,0 +1,214 @@
package executor
import (
config "github.com/OliveTin/OliveTin/internal/config"
sv "github.com/OliveTin/OliveTin/internal/stringvariables"
log "github.com/sirupsen/logrus"
"errors"
"net/mail"
"net/url"
"regexp"
"strings"
"time"
)
var (
typecheckRegex = map[string]string{
"very_dangerous_raw_string": "",
"int": "^[\\d]+$",
"unicode_identifier": "^[\\w\\/\\\\.\\_ \\d]+$",
"ascii": "^[a-zA-Z0-9]+$",
"ascii_identifier": "^[a-zA-Z0-9\\-\\.\\_]+$",
"ascii_sentence": "^[a-zA-Z0-9 \\,\\.]+$",
}
)
func parseCommandForReplacements(rawShellCommand string, values map[string]string) (string, map[string]string, error) {
r := regexp.MustCompile("{{ *?([a-zA-Z0-9_]+?) *?}}")
foundArgumentNames := r.FindAllStringSubmatch(rawShellCommand, -1)
usedArguments := make(map[string]string)
for _, match := range foundArgumentNames {
argName := match[1]
argValue, argProvided := values[argName]
if !argProvided {
return "", nil, errors.New("Required arg not provided: " + argName)
}
usedArguments[argName] = argValue
rawShellCommand = strings.ReplaceAll(rawShellCommand, match[0], argValue)
}
return rawShellCommand, usedArguments, nil
}
func parseActionArguments(rawShellCommand string, values map[string]string, action *config.Action, actionTitle string, entityPrefix string) (string, error) {
log.WithFields(log.Fields{
"actionTitle": actionTitle,
"cmd": rawShellCommand,
}).Infof("Action parse args - Before")
rawShellCommand, usedArgs, err := parseCommandForReplacements(rawShellCommand, values)
if err != nil {
return "", err
}
for argName, argValue := range usedArgs {
err := typecheckActionArgument(argName, argValue, action)
if err != nil {
return "", err
}
log.WithFields(log.Fields{
"name": argName,
"value": argValue,
}).Debugf("Arg assigned")
}
rawShellCommand = sv.ReplaceEntityVars(entityPrefix, rawShellCommand)
log.WithFields(log.Fields{
"actionTitle": actionTitle,
"cmd": rawShellCommand,
}).Infof("Action parse args - After")
return rawShellCommand, nil
}
func typecheckActionArgument(name string, value string, action *config.Action) error {
arg := action.FindArg(name)
if arg == nil {
return errors.New("Action arg not defined: " + name)
}
if value == "" {
return typecheckNull(arg)
}
if len(arg.Choices) > 0 {
return typecheckChoice(value, arg)
}
return TypeSafetyCheck(name, value, arg.Type)
}
func typecheckNull(arg *config.ActionArgument) error {
if arg.RejectNull {
return errors.New("Null values are not allowed")
}
return nil
}
func typecheckChoice(value string, arg *config.ActionArgument) error {
if arg.Entity != "" {
return typecheckChoiceEntity(value, arg)
}
for _, choice := range arg.Choices {
if value == choice.Value {
return nil
}
}
return errors.New("argument value is not one of the predefined choices")
}
func typecheckChoiceEntity(value string, arg *config.ActionArgument) error {
templateChoice := arg.Choices[0].Value
for _, ent := range sv.GetEntities(arg.Entity) {
choice := sv.ReplaceEntityVars(ent, templateChoice)
if value == choice {
return nil
}
}
return errors.New("argument value cannot be found in entities")
}
// TypeSafetyCheck checks argument values match a specific type. The types are
// defined in typecheckRegex, and, you guessed it, uses regex to check for allowed
// characters.
//
//gocyclo:ignore
func TypeSafetyCheck(name string, value string, argumentType string) error {
switch argumentType {
case "password":
return nil
case "email":
return typeSafetyCheckEmail(name, value)
case "url":
return typeSafetyCheckUrl(name, value)
case "datetime":
return typeSafetyCheckDatetime(name, value)
}
return typeSafetyCheckRegex(name, value, argumentType)
}
func typeSafetyCheckEmail(name string, value string) error {
_, err := mail.ParseAddress(value)
log.Errorf("Email check: %v, %v", err, value)
if err != nil {
return err
}
return nil
}
func typeSafetyCheckDatetime(name string, value string) error {
_, err := time.Parse("2006-01-02T15:04:05", value)
if err != nil {
return err
}
return nil
}
func typeSafetyCheckRegex(name string, value string, argumentType string) error {
pattern := ""
if strings.HasPrefix(argumentType, "regex:") {
pattern = strings.Replace(argumentType, "regex:", "", 1)
} else {
found := false
pattern, found = typecheckRegex[argumentType]
if !found {
return errors.New("argument type not implemented " + argumentType)
}
}
matches, _ := regexp.MatchString(pattern, value)
if !matches {
log.WithFields(log.Fields{
"name": name,
"value": value,
"type": argumentType,
"pattern": pattern,
}).Warn("Arg type check safety failure")
return errors.New("invalid argument, doesn't match " + argumentType)
}
return nil
}
func typeSafetyCheckUrl(name string, value string) error {
_, err := url.ParseRequestURI(value)
return err
}

View File

@@ -0,0 +1,97 @@
package executor
import (
config "github.com/OliveTin/OliveTin/internal/config"
"github.com/stretchr/testify/assert"
"testing"
)
func TestSanitizeUnsafe(t *testing.T) {
assert.Nil(t, TypeSafetyCheck("", "_zomg_ c:/ haxxor ' bobby tables && rm -rf ", "very_dangerous_raw_string"))
}
func TestSanitizeUnimplemented(t *testing.T) {
err := TypeSafetyCheck("", "I am a happy little argument", "greeting_type")
assert.NotNil(t, err, "Test an argument type that does not exist")
}
func TestArgumentValueNullable(t *testing.T) {
a1 := config.Action{
Title: "Release the hounds",
Shell: "echo 'Releasing {{ count }} hounds'",
Arguments: []config.ActionArgument{
{
Name: "count",
Type: "int",
},
},
}
values := map[string]string{
"count": "",
}
out, err := parseActionArguments(a1.Shell, values, &a1, a1.Title, "")
assert.Equal(t, "echo 'Releasing hounds'", out)
assert.Nil(t, err)
a1.Arguments[0].RejectNull = true
_, err = parseActionArguments(a1.Shell, values, &a1, a1.Title, "")
assert.NotNil(t, err)
}
func TestArgumentNameNumbers(t *testing.T) {
a1 := config.Action{
Title: "Do some tickles",
Shell: "echo 'Tickling {{ person1name }}'",
Arguments: []config.ActionArgument{
{
Name: "person1name",
Type: "ascii",
},
},
}
values := map[string]string{
"person1name": "Fred",
}
out, err := parseActionArguments(a1.Shell, values, &a1, a1.Title, "")
assert.Equal(t, "echo 'Tickling Fred'", out)
assert.Nil(t, err)
}
func TestArgumentNotProvided(t *testing.T) {
a1 := config.Action{
Title: "Do some tickles",
Shell: "echo 'Tickling {{ personName }}'",
Arguments: []config.ActionArgument{
{
Name: "person",
Type: "ascii",
},
},
}
values := map[string]string{}
out, err := parseActionArguments(a1.Shell, values, &a1, a1.Title, "")
assert.Equal(t, "", out)
assert.Equal(t, err.Error(), "Required arg not provided: personName")
}
func TestTypeSafetyCheckUrl(t *testing.T) {
assert.Nil(t, TypeSafetyCheck("test1", "http://google.com", "url"), "Test URL: google.com")
assert.Nil(t, TypeSafetyCheck("test2", "http://technowax.net:80?foo=bar", "url"), "Test URL: technowax.net with query arguments")
assert.Nil(t, TypeSafetyCheck("test3", "http://localhost:80?foo=bar", "url"), "Test URL: localhost with query arguments")
assert.NotNil(t, TypeSafetyCheck("test4", "http://lo host:80", "url"), "Test a badly formed URL")
assert.NotNil(t, TypeSafetyCheck("test5", "12345", "url"), "Test a badly formed URL")
assert.NotNil(t, TypeSafetyCheck("test6", "_!23;", "url"), "Test a badly formed URL")
}

View File

@@ -1,171 +1,354 @@
package executor
import (
pb "github.com/jamesread/OliveTin/gen/grpc"
acl "github.com/jamesread/OliveTin/internal/acl"
config "github.com/jamesread/OliveTin/internal/config"
acl "github.com/OliveTin/OliveTin/internal/acl"
config "github.com/OliveTin/OliveTin/internal/config"
sv "github.com/OliveTin/OliveTin/internal/stringvariables"
"github.com/google/uuid"
log "github.com/sirupsen/logrus"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
"gopkg.in/yaml.v3"
"bytes"
"context"
"errors"
"os/exec"
"regexp"
"fmt"
"os"
"path"
"strings"
"sync"
"time"
)
var (
typecheckRegex = map[string]string{
"very_dangerous_raw_string": "",
"int": "^[\\d]+$",
"ascii": "^[a-zA-Z0-9]+$",
"ascii_identifier": "^[a-zA-Z0-9\\-\\.\\_]+$",
"ascii_sentence": "^[a-zA-Z0-9 \\,\\.]+$",
}
metricActionsRequested = promauto.NewCounter(prometheus.CounterOpts{
Name: "olivetin_actions_requested_count",
Help: "The actions requested count",
})
)
type ActionBinding struct {
Action *config.Action
EntityPrefix string
ConfigOrder int
}
// Executor represents a helper class for executing commands. It's main method
// is ExecRequest
type Executor struct {
logs map[string]*InternalLogEntry
LogsByActionId map[string][]*InternalLogEntry
logmutex sync.RWMutex
MapActionIdToBinding map[string]*ActionBinding
MapActionIdToBindingLock sync.RWMutex
Cfg *config.Config
listeners []listener
chainOfCommand []executorStepFunc
}
// ExecutionRequest is a request to execute an action. It's passed to an
// Executor. They're created from the grpcapi.
type ExecutionRequest struct {
ActionTitle string
Action *config.Action
Arguments map[string]string
TrackingID string
Tags []string
Cfg *config.Config
AuthenticatedUser *acl.AuthenticatedUser
EntityPrefix string
logEntry *InternalLogEntry
finalParsedCommand string
executor *Executor
}
// InternalLogEntry objects are created by an Executor, and represent the final
// state of execution (even if the command is not executed). It's designed to be
// easily serializable.
type InternalLogEntry struct {
Datetime string
Stdout string
Stderr string
TimedOut bool
ExitCode int32
DatetimeStarted time.Time
DatetimeFinished time.Time
Output string
TimedOut bool
Blocked bool
ExitCode int32
Tags []string
ExecutionStarted bool
ExecutionFinished bool
ExecutionTrackingID string
Process *os.Process
Username string
/*
The following two properties are obviously on Action normally, but it's useful
The following 3 properties are obviously on Action normally, but it's useful
that logs are lightweight (so we don't need to have an action associated to
logs, etc. Therefore, we duplicate those values here.
*/
ActionTitle string
ActionIcon string
ActionId string
}
type ExecutionRequest struct {
ActionName string
Arguments map[string]string
action *config.Action
Cfg *config.Config
User *acl.User
logEntry *InternalLogEntry
finalParsedCommand string
}
type executorStepFunc func(*ExecutionRequest) bool
type ExecutorStep interface {
Exec(*ExecutionRequest) bool
}
type Executor struct {
Logs []InternalLogEntry
chainOfCommand []ExecutorStep
}
func DefaultExecutor() *Executor {
// DefaultExecutor returns an Executor, with a sensible "chain of command" for
// executing actions.
func DefaultExecutor(cfg *config.Config) *Executor {
e := Executor{}
e.chainOfCommand = []ExecutorStep{
StepFindAction{},
StepAclCheck{},
StepParseArgs{},
StepLogStart{},
StepExec{},
StepLogFinish{},
e.Cfg = cfg
e.logs = make(map[string]*InternalLogEntry)
e.LogsByActionId = make(map[string][]*InternalLogEntry)
e.MapActionIdToBinding = make(map[string]*ActionBinding)
e.chainOfCommand = []executorStepFunc{
stepRequestAction,
stepConcurrencyCheck,
stepRateCheck,
stepACLCheck,
stepParseArgs,
stepLogStart,
stepExec,
stepExecAfter,
stepLogFinish,
stepSaveLog,
stepTrigger,
}
return &e
}
type StepFindAction struct{}
func (s StepFindAction) Exec(req *ExecutionRequest) bool {
actualAction := req.Cfg.FindAction(req.ActionName)
if actualAction == nil {
log.WithFields(log.Fields{
"actionName": req.ActionName,
}).Warnf("Action not found")
req.logEntry.Stderr = "Action not found"
req.logEntry.ExitCode = -1337
return false
}
req.action = actualAction
req.logEntry.ActionIcon = actualAction.Icon
return true
type listener interface {
OnExecutionStarted(actionTitle string)
OnExecutionFinished(logEntry *InternalLogEntry)
OnOutputChunk(o []byte, executionTrackingId string)
OnActionMapRebuilt()
}
type StepAclCheck struct{}
func (e *Executor) AddListener(m listener) {
e.listeners = append(e.listeners, m)
}
func (s StepAclCheck) Exec(req *ExecutionRequest) bool {
return acl.IsAllowedExec(req.Cfg, req.User, req.action)
func (e *Executor) GetLogsCopy() map[string]*InternalLogEntry {
e.logmutex.RLock()
copy := make(map[string]*InternalLogEntry)
for k, v := range e.logs {
copy[k] = v
}
e.logmutex.RUnlock()
return copy
}
func (e *Executor) GetLog(trackingID string) (*InternalLogEntry, bool) {
e.logmutex.RLock()
entry, found := e.logs[trackingID]
e.logmutex.RUnlock()
return entry, found
}
func (e *Executor) GetLogsByActionId(actionId string) []*InternalLogEntry {
e.logmutex.RLock()
logs, found := e.LogsByActionId[actionId]
e.logmutex.RUnlock()
if !found {
return make([]*InternalLogEntry, 0)
}
return logs
}
func (e *Executor) SetLog(trackingID string, entry *InternalLogEntry) {
e.logmutex.Lock()
e.logs[trackingID] = entry
e.logmutex.Unlock()
}
// ExecRequest processes an ExecutionRequest
func (e *Executor) ExecRequest(req *ExecutionRequest) *pb.StartActionResponse {
req.logEntry = &InternalLogEntry{
Datetime: time.Now().Format("2006-01-02 15:04:05"),
ActionTitle: req.ActionName,
func (e *Executor) ExecRequest(req *ExecutionRequest) (*sync.WaitGroup, string) {
if req.AuthenticatedUser == nil {
req.AuthenticatedUser = acl.UserGuest(req.Cfg)
}
req.executor = e
req.logEntry = &InternalLogEntry{
DatetimeStarted: time.Now(),
ExecutionTrackingID: req.TrackingID,
Output: "",
ExitCode: -1337, // If an Action is not actually executed, this is the default exit code.
ExecutionStarted: false,
ExecutionFinished: false,
ActionId: "",
ActionTitle: "notfound",
ActionIcon: "&#x1f4a9;",
Username: req.AuthenticatedUser.Username,
}
_, isDuplicate := e.GetLog(req.TrackingID)
if isDuplicate || req.TrackingID == "" {
req.TrackingID = uuid.NewString()
}
log.Tracef("executor.ExecRequest(): %v", req)
e.SetLog(req.TrackingID, req.logEntry)
wg := new(sync.WaitGroup)
wg.Add(1)
go func() {
e.execChain(req)
defer wg.Done()
}()
return wg, req.TrackingID
}
func (e *Executor) execChain(req *ExecutionRequest) {
for _, step := range e.chainOfCommand {
if !step.Exec(req) {
if !step(req) {
break
}
}
e.Logs = append(e.Logs, *req.logEntry)
req.logEntry.ExecutionFinished = true
return &pb.StartActionResponse{
LogEntry: &pb.LogEntry{
ActionTitle: req.logEntry.ActionTitle,
ActionIcon: req.logEntry.ActionIcon,
Datetime: req.logEntry.Datetime,
Stderr: req.logEntry.Stderr,
Stdout: req.logEntry.Stdout,
TimedOut: req.logEntry.TimedOut,
ExitCode: req.logEntry.ExitCode,
},
// This isn't a step, because we want to notify all listeners, irrespective
// of how many steps were actually executed.
notifyListeners(req)
}
func getConcurrentCount(req *ExecutionRequest) int {
concurrentCount := 0
req.executor.logmutex.RLock()
for _, log := range req.executor.GetLogsByActionId(req.Action.ID) {
if !log.ExecutionFinished {
concurrentCount += 1
}
}
req.executor.logmutex.RUnlock()
return concurrentCount
}
type StepLogStart struct{}
func stepConcurrencyCheck(req *ExecutionRequest) bool {
concurrentCount := getConcurrentCount(req)
func (e StepLogStart) Exec(req *ExecutionRequest) bool {
log.WithFields(log.Fields{
"title": req.action.Title,
"timeout": req.action.Timeout,
}).Infof("Action starting")
// Note that the current execution is counted int the logs, so when checking we +1
if concurrentCount >= (req.Action.MaxConcurrent + 1) {
msg := fmt.Sprintf("Blocked from executing. This would mean this action is running %d times concurrently, but this action has maxExecutions set to %d.", concurrentCount, req.Action.MaxConcurrent)
log.WithFields(log.Fields{
"actionTitle": req.logEntry.ActionTitle,
}).Warnf(msg)
req.logEntry.Output = msg
req.logEntry.Blocked = true
return false
}
return true
}
type StepLogFinish struct{}
func (e StepLogFinish) Exec(req *ExecutionRequest) bool {
log.WithFields(log.Fields{
"title": req.action.Title,
"stdout": req.logEntry.Stdout,
"stderr": req.logEntry.Stderr,
"timedOut": req.logEntry.TimedOut,
"exit": req.logEntry.ExitCode,
}).Infof("Action finished")
return true
}
type StepParseArgs struct{}
func (e StepParseArgs) Exec(req *ExecutionRequest) bool {
var err error
req.finalParsedCommand, err = parseActionArguments(req.action.Shell, req.Arguments, req.action)
func parseDuration(rate config.RateSpec) time.Duration {
duration, err := time.ParseDuration(rate.Duration)
if err != nil {
req.logEntry.ExitCode = -1337
req.logEntry.Stderr = ""
req.logEntry.Stdout = err.Error()
log.Warnf("Could not parse duration: %v", rate.Duration)
return -1 * time.Minute
}
return duration
}
func getExecutionsCount(rate config.RateSpec, req *ExecutionRequest) int {
executions := -1 // Because we will find ourself when checking execution logs
duration := parseDuration(rate)
then := time.Now().Add(-duration)
for _, logEntry := range req.executor.GetLogsByActionId(req.Action.ID) {
if logEntry.DatetimeStarted.After(then) && !logEntry.Blocked {
executions += 1
}
}
return executions
}
func stepRateCheck(req *ExecutionRequest) bool {
for _, rate := range req.Action.MaxRate {
executions := getExecutionsCount(rate, req)
if executions >= rate.Limit {
msg := fmt.Sprintf("Blocked from executing. This action has run %d out of %d allowed times in the last %s.", executions, rate.Limit, rate.Duration)
log.WithFields(log.Fields{
"actionTitle": req.logEntry.ActionTitle,
}).Infof(msg)
req.logEntry.Output = msg
req.logEntry.Blocked = true
return false
}
}
return true
}
func stepACLCheck(req *ExecutionRequest) bool {
canExec := acl.IsAllowedExec(req.Cfg, req.AuthenticatedUser, req.Action)
if !canExec {
req.logEntry.Output = "ACL check failed. Blocked from executing."
req.logEntry.Blocked = true
log.WithFields(log.Fields{
"actionTitle": req.logEntry.ActionTitle,
}).Warnf("ACL check failed. Blocked from executing.")
}
return canExec
}
func stepParseArgs(req *ExecutionRequest) bool {
var err error
if req.Arguments == nil {
req.Arguments = make(map[string]string)
}
req.Arguments["ot_executionTrackingId"] = req.TrackingID
req.Arguments["ot_username"] = req.AuthenticatedUser.Username
req.finalParsedCommand, err = parseActionArguments(req.Action.Shell, req.Arguments, req.Action, req.logEntry.ActionTitle, req.EntityPrefix)
if err != nil {
req.logEntry.Output = err.Error()
log.Warnf(err.Error())
@@ -175,120 +358,281 @@ func (e StepParseArgs) Exec(req *ExecutionRequest) bool {
return true
}
type StepExec struct{}
func stepRequestAction(req *ExecutionRequest) bool {
// The grpc API always tries to find the action by ID, but it may
if req.Action == nil {
log.WithFields(log.Fields{
"actionTitle": req.ActionTitle,
}).Infof("Action finding by title")
func (e StepExec) Exec(req *ExecutionRequest) bool {
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(req.action.Timeout)*time.Second)
defer cancel()
req.Action = req.Cfg.FindAction(req.ActionTitle)
cmd := exec.CommandContext(ctx, "sh", "-c", req.finalParsedCommand)
stdout, stderr := cmd.Output()
if req.Action == nil {
log.WithFields(log.Fields{
"actionTitle": req.ActionTitle,
}).Warnf("Action requested, but not found")
if stderr != nil {
req.logEntry.Stderr = stderr.Error()
req.logEntry.Output = "Action not found: " + req.ActionTitle
return false
}
}
if ctx.Err() == context.DeadlineExceeded {
req.logEntry.TimedOut = true
metricActionsRequested.Inc()
req.logEntry.ActionTitle = sv.ReplaceEntityVars(req.EntityPrefix, req.Action.Title)
req.logEntry.ActionIcon = req.Action.Icon
req.logEntry.ActionId = req.Action.ID
req.logEntry.Tags = req.Tags
req.executor.logmutex.Lock()
if _, containsKey := req.executor.LogsByActionId[req.Action.ID]; !containsKey {
req.executor.LogsByActionId[req.Action.ID] = make([]*InternalLogEntry, 0)
}
req.logEntry.ExitCode = int32(cmd.ProcessState.ExitCode())
req.logEntry.Stdout = string(stdout)
req.executor.LogsByActionId[req.Action.ID] = append(req.executor.LogsByActionId[req.Action.ID], req.logEntry)
req.executor.logmutex.Unlock()
log.WithFields(log.Fields{
"actionTitle": req.logEntry.ActionTitle,
"tags": req.Tags,
}).Infof("Action requested")
return true
}
func parseActionArguments(rawShellCommand string, values map[string]string, action *config.Action) (string, error) {
func stepLogStart(req *ExecutionRequest) bool {
log.WithFields(log.Fields{
"cmd": rawShellCommand,
}).Infof("Before Parse Args")
"actionTitle": req.logEntry.ActionTitle,
"timeout": req.Action.Timeout,
}).Infof("Action started")
r := regexp.MustCompile("{{ *?([a-z]+?) *?}}")
matches := r.FindAllStringSubmatch(rawShellCommand, -1)
return true
}
for _, match := range matches {
argValue, argProvided := values[match[1]]
func stepLogFinish(req *ExecutionRequest) bool {
req.logEntry.ExecutionFinished = true
if !argProvided {
log.Infof("%v", values)
return "", errors.New("Required arg not provided: " + match[1])
log.WithFields(log.Fields{
"actionTitle": req.logEntry.ActionTitle,
"outputLength": len(req.logEntry.Output),
"timedOut": req.logEntry.TimedOut,
"exit": req.logEntry.ExitCode,
}).Infof("Action finished")
return true
}
func notifyListeners(req *ExecutionRequest) {
for _, listener := range req.executor.listeners {
listener.OnExecutionFinished(req.logEntry)
}
}
func appendErrorToStderr(err error, logEntry *InternalLogEntry) {
if err != nil {
logEntry.Output = err.Error() + "\n\n" + logEntry.Output
}
}
type OutputStreamer struct {
Req *ExecutionRequest
output bytes.Buffer
}
func (ost *OutputStreamer) Write(o []byte) (n int, err error) {
for _, listener := range ost.Req.executor.listeners {
listener.OnOutputChunk(o, ost.Req.TrackingID)
}
return ost.output.Write(o)
}
func (ost *OutputStreamer) String() string {
return ost.output.String()
}
func buildEnv(args map[string]string) []string {
ret := append(os.Environ(), "OLIVETIN=1")
for k, v := range args {
varName := fmt.Sprintf("%v", strings.TrimSpace(strings.ToUpper(k)))
// Skip arguments that might not have a name (eg, confirmation), as this causes weird bugs on Windows.
if varName == "" {
continue
}
err := typecheckActionArgument(match[1], argValue, action)
ret = append(ret, fmt.Sprintf("%v=%v", varName, v))
}
return ret
}
func stepExec(req *ExecutionRequest) bool {
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(req.Action.Timeout)*time.Second)
defer cancel()
streamer := &OutputStreamer{Req: req}
cmd := wrapCommandInShell(ctx, req.finalParsedCommand)
cmd.Stdout = streamer
cmd.Stderr = streamer
cmd.Env = buildEnv(req.Arguments)
req.logEntry.ExecutionStarted = true
runerr := cmd.Start()
req.logEntry.Process = cmd.Process
waiterr := cmd.Wait()
req.logEntry.ExitCode = int32(cmd.ProcessState.ExitCode())
req.logEntry.Output = streamer.String()
appendErrorToStderr(runerr, req.logEntry)
appendErrorToStderr(waiterr, req.logEntry)
if ctx.Err() == context.DeadlineExceeded {
log.WithFields(log.Fields{
"actionTitle": req.logEntry.ActionTitle,
}).Warnf("Action timed out")
// The context timeout should kill the process, but let's make sure.
req.executor.Kill(req.logEntry)
req.logEntry.TimedOut = true
req.logEntry.Output += "OliveTin::timeout - this action timed out after " + fmt.Sprintf("%v", req.Action.Timeout) + " seconds. If you need more time for this action, set a longer timeout. See https://docs.olivetin.app/timeout.html for more help."
}
req.logEntry.DatetimeFinished = time.Now()
return true
}
func stepExecAfter(req *ExecutionRequest) bool {
if req.Action.ShellAfterCompleted == "" {
return true
}
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(req.Action.Timeout)*time.Second)
defer cancel()
var stdout bytes.Buffer
var stderr bytes.Buffer
args := map[string]string{
"output": req.logEntry.Output,
"exitCode": fmt.Sprintf("%v", req.logEntry.ExitCode),
"ot_executionTrackingId": req.TrackingID,
"ot_username": req.AuthenticatedUser.Username,
}
finalParsedCommand, _, err := parseCommandForReplacements(req.Action.ShellAfterCompleted, args)
if err != nil {
msg := "Could not prepare shellAfterCompleted command: " + err.Error() + "\n"
req.logEntry.Output += msg
log.Warnf(msg)
return true
}
cmd := wrapCommandInShell(ctx, finalParsedCommand)
cmd.Stdout = &stdout
cmd.Stderr = &stderr
cmd.Env = buildEnv(args)
runerr := cmd.Start()
waiterr := cmd.Wait()
req.logEntry.Output += "\n"
req.logEntry.Output += "OliveTin::shellAfterCompleted stdout\n"
req.logEntry.Output += stdout.String()
req.logEntry.Output += "OliveTin::shellAfterCompleted stderr\n"
req.logEntry.Output += stderr.String()
req.logEntry.Output += "OliveTin::shellAfterCompleted errors and summary\n"
appendErrorToStderr(runerr, req.logEntry)
appendErrorToStderr(waiterr, req.logEntry)
if ctx.Err() == context.DeadlineExceeded {
req.logEntry.Output += "Your shellAfterCompleted command timed out."
}
req.logEntry.Output += fmt.Sprintf("Your shellAfterCompleted exited with code %v\n", cmd.ProcessState.ExitCode())
req.logEntry.Output += "OliveTin::shellAfterCompleted output complete\n"
return true
}
func stepTrigger(req *ExecutionRequest) bool {
if req.Action.Trigger != "" {
trigger := &ExecutionRequest{
ActionTitle: req.Action.Trigger,
TrackingID: uuid.NewString(),
Tags: []string{"trigger"},
AuthenticatedUser: req.AuthenticatedUser,
Cfg: req.Cfg,
}
req.executor.ExecRequest(trigger)
}
return true
}
func stepSaveLog(req *ExecutionRequest) bool {
filename := fmt.Sprintf("%v.%v.%v", req.logEntry.ActionTitle, req.logEntry.DatetimeStarted.Unix(), req.logEntry.ExecutionTrackingID)
saveLogResults(req, filename)
saveLogOutput(req, filename)
return true
}
func firstNonEmpty(one, two string) string {
if one != "" {
return one
}
return two
}
func saveLogResults(req *ExecutionRequest, filename string) {
dir := firstNonEmpty(req.Action.SaveLogs.ResultsDirectory, req.Cfg.SaveLogs.ResultsDirectory)
if dir != "" {
data, err := yaml.Marshal(req.logEntry)
if err != nil {
return "", err
log.Warnf("%v", err)
}
log.WithFields(log.Fields{
"name": match[1],
"value": argValue,
}).Debugf("Arg assigned")
filepath := path.Join(dir, filename+".yaml")
err = os.WriteFile(filepath, data, 0644)
rawShellCommand = strings.Replace(rawShellCommand, match[0], argValue, -1)
}
log.WithFields(log.Fields{
"cmd": rawShellCommand,
}).Infof("After Parse Args")
return rawShellCommand, nil
}
func typecheckActionArgument(name string, value string, action *config.Action) error {
arg := findArg(name, action)
if arg == nil {
return errors.New("Action arg not defined: " + name)
}
if len(arg.Choices) > 0 {
return typecheckChoice(value, arg)
}
return TypeSafetyCheck(name, value, arg.Type)
}
func typecheckChoice(value string, arg *config.ActionArgument) error {
for _, choice := range arg.Choices {
if value == choice.Value {
return nil
if err != nil {
log.Warnf("%v", err)
}
}
return errors.New("Arg value is not one of the predefined choices")
}
func TypeSafetyCheck(name string, value string, typ string) error {
pattern, found := typecheckRegex[typ]
func saveLogOutput(req *ExecutionRequest, filename string) {
dir := firstNonEmpty(req.Action.SaveLogs.OutputDirectory, req.Cfg.SaveLogs.OutputDirectory)
log.Infof("%v %v", pattern, typ)
if dir != "" {
data := req.logEntry.Output
filepath := path.Join(dir, filename+".log")
err := os.WriteFile(filepath, []byte(data), 0644)
if !found {
return errors.New("Arg type not implemented " + typ)
}
matches, _ := regexp.MatchString(pattern, value)
if !matches {
log.WithFields(log.Fields{
"name": name,
"type": typ,
"value": value,
}).Warn("Arg type check safety failure")
return errors.New("Invalid argument, doesn't match " + typ)
}
return nil
}
func findArg(name string, action *config.Action) *config.ActionArgument {
for _, arg := range action.Arguments {
if arg.Name == name {
return &arg
if err != nil {
log.Warnf("%v", err)
}
}
return nil
}

View File

@@ -0,0 +1,89 @@
package executor
import (
"crypto/sha256"
"fmt"
config "github.com/OliveTin/OliveTin/internal/config"
sv "github.com/OliveTin/OliveTin/internal/stringvariables"
log "github.com/sirupsen/logrus"
"strconv"
)
func (e *Executor) FindActionBindingByID(id string) *config.Action {
e.MapActionIdToBindingLock.RLock()
pair, found := e.MapActionIdToBinding[id]
e.MapActionIdToBindingLock.RUnlock()
if found {
log.Infof("findActionBinding %v, %v", id, pair.Action.ID)
return pair.Action
}
return nil
}
func (e *Executor) RebuildActionMap() {
e.MapActionIdToBindingLock.Lock()
clear(e.MapActionIdToBinding)
for configOrder, action := range e.Cfg.Actions {
if action.Entity != "" {
registerActionsFromEntities(e, configOrder, action.Entity, action)
} else {
registerAction(e, configOrder, action)
}
}
e.MapActionIdToBindingLock.Unlock()
for _, l := range e.listeners {
l.OnActionMapRebuilt()
}
}
func registerAction(e *Executor, configOrder int, action *config.Action) {
actionId := hashActionToID(action, "")
e.MapActionIdToBinding[actionId] = &ActionBinding{
Action: action,
EntityPrefix: "noent",
ConfigOrder: configOrder,
}
}
func registerActionsFromEntities(e *Executor, configOrder int, entityTitle string, tpl *config.Action) {
entityCount, _ := strconv.Atoi(sv.Get("entities." + entityTitle + ".count"))
for i := 0; i < entityCount; i++ {
registerActionFromEntity(e, configOrder, tpl, entityTitle, i)
}
}
func registerActionFromEntity(e *Executor, configOrder int, tpl *config.Action, entityTitle string, entityIndex int) {
prefix := sv.GetEntityPrefix(entityTitle, entityIndex)
virtualActionId := hashActionToID(tpl, prefix)
e.MapActionIdToBinding[virtualActionId] = &ActionBinding{
Action: tpl,
EntityPrefix: prefix,
ConfigOrder: configOrder,
}
}
func hashActionToID(action *config.Action, entityPrefix string) string {
if action.ID != "" && entityPrefix == "" {
return action.ID
}
h := sha256.New()
if entityPrefix == "" {
h.Write([]byte(action.Title))
} else {
h.Write([]byte(action.ID + "." + entityPrefix))
}
return fmt.Sprintf("%x", h.Sum(nil))
}

View File

@@ -0,0 +1,111 @@
package executor
import (
"github.com/stretchr/testify/assert"
"testing"
acl "github.com/OliveTin/OliveTin/internal/acl"
config "github.com/OliveTin/OliveTin/internal/config"
)
func testingExecutor() (*Executor, *config.Config) {
cfg := config.DefaultConfig()
e := DefaultExecutor(cfg)
a1 := &config.Action{
Title: "Do some tickles",
Shell: "echo 'Tickling {{ person }}'",
Arguments: []config.ActionArgument{
{
Name: "person",
Type: "ascii",
},
},
}
cfg.Actions = append(cfg.Actions, a1)
cfg.Sanitize()
return e, cfg
}
func TestCreateExecutorAndExec(t *testing.T) {
e, cfg := testingExecutor()
req := ExecutionRequest{
ActionTitle: "Do some tickles",
AuthenticatedUser: &acl.AuthenticatedUser{Username: "Mr Tickle"},
Cfg: cfg,
Arguments: map[string]string{
"person": "yourself",
},
}
assert.NotNil(t, e, "Create an executor")
wg, _ := e.ExecRequest(&req)
wg.Wait()
assert.Equal(t, int32(0), req.logEntry.ExitCode, "Exit code is zero")
}
func TestExecNonExistant(t *testing.T) {
e, cfg := testingExecutor()
req := ExecutionRequest{
ActionTitle: "Waffles",
logEntry: &InternalLogEntry{},
Cfg: cfg,
}
wg, _ := e.ExecRequest(&req)
wg.Wait()
assert.Equal(t, int32(-1337), req.logEntry.ExitCode, "Log entry is set to an internal error code")
assert.Equal(t, "&#x1f4a9;", req.logEntry.ActionIcon, "Log entry icon is a poop (not found)")
}
func TestArgumentNameCamelCase(t *testing.T) {
a1 := &config.Action{
Title: "Do some tickles",
Shell: "echo 'Tickling {{ personName }}'",
Arguments: []config.ActionArgument{
{
Name: "personName",
Type: "ascii",
},
},
}
values := map[string]string{
"personName": "Fred",
}
out, err := parseActionArguments(a1.Shell, values, a1, a1.Title, "")
assert.Equal(t, "echo 'Tickling Fred'", out)
assert.Nil(t, err)
}
func TestArgumentNameSnakeCase(t *testing.T) {
a1 := &config.Action{
Title: "Do some tickles",
Shell: "echo 'Tickling {{ person_name }}'",
Arguments: []config.ActionArgument{
{
Name: "person_name",
Type: "ascii",
},
},
}
values := map[string]string{
"person_name": "Fred",
}
out, err := parseActionArguments(a1.Shell, values, a1, a1.Title, "")
assert.Equal(t, "echo 'Tickling Fred'", out)
assert.Nil(t, err)
}

View File

@@ -0,0 +1,25 @@
//go:build !windows
// +build !windows
package executor
import (
"context"
"os/exec"
"syscall"
)
func (e *Executor) Kill(execReq *InternalLogEntry) error {
// A negative PID means to kill the whole process group. This is *nix specific behavior.
return syscall.Kill(-execReq.Process.Pid, syscall.SIGKILL)
}
func wrapCommandInShell(ctx context.Context, finalParsedCommand string) *exec.Cmd {
cmd := exec.CommandContext(ctx, "sh", "-c", finalParsedCommand)
// This is to ensure that the process group is killed when the parent process is killed.
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
return cmd
}

Some files were not shown because too many files have changed in this diff Show More