Compare commits

...

96 Commits

Author SHA1 Message Date
dependabot[bot]
ae6c62905b
build(deps): bump softprops/action-gh-release from 2.3.3 to 2.4.1 (#280) 2025-11-01 14:40:30 -06:00
dependabot[bot]
e4f139a656
build(deps): bump github/codeql-action from 3.30.5 to 4.31.2 (#281) 2025-11-01 14:40:15 -06:00
dependabot[bot]
b75ab17fe2
build(deps): bump actions/upload-artifact from 4.6.2 to 5.0.0 (#282) 2025-11-01 14:40:02 -06:00
dependabot[bot]
149b796849
build(deps): bump github/codeql-action from 3.29.7 to 3.30.5 (#274)
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.29.7 to 3.30.5.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](51f77329af...3599b3baa1)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: 3.30.5
  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>
2025-10-01 11:34:57 -06:00
dependabot[bot]
503b87c240
build(deps): bump softprops/action-gh-release from 2.3.2 to 2.3.3 (#273)
Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2.3.2 to 2.3.3.
- [Release notes](https://github.com/softprops/action-gh-release/releases)
- [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md)
- [Commits](72f2c25fcb...6cbd405e2c)

---
updated-dependencies:
- dependency-name: softprops/action-gh-release
  dependency-version: 2.3.3
  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>
2025-10-01 11:31:00 -06:00
dependabot[bot]
60cc3ad30c
build(deps): bump actions/checkout from 4.2.2 to 5.0.0 (#275)
Bumps [actions/checkout](https://github.com/actions/checkout) from 4.2.2 to 5.0.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](11bd71901b...08c6903cd8)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 5.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-01 11:30:36 -06:00
dependabot[bot]
daefc0de79
build(deps): bump actions/setup-go from 5.5.0 to 6.0.0 (#276)
Bumps [actions/setup-go](https://github.com/actions/setup-go) from 5.5.0 to 6.0.0.
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](d35c59abb0...4469467582)

---
updated-dependencies:
- dependency-name: actions/setup-go
  dependency-version: 6.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-01 11:30:21 -06:00
dependabot[bot]
d9b1675a59
build(deps): bump docker/login-action from 3.4.0 to 3.6.0 (#277)
Bumps [docker/login-action](https://github.com/docker/login-action) from 3.4.0 to 3.6.0.
- [Release notes](https://github.com/docker/login-action/releases)
- [Commits](74a5d14239...5e57cd1181)

---
updated-dependencies:
- dependency-name: docker/login-action
  dependency-version: 3.6.0
  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>
2025-10-01 11:30:10 -06:00
dependabot[bot]
ee922588e0
build(deps): bump github.com/ulikunitz/xz from 0.5.12 to 0.5.14 (#266)
Bumps [github.com/ulikunitz/xz](https://github.com/ulikunitz/xz) from 0.5.12 to 0.5.14.
- [Commits](https://github.com/ulikunitz/xz/compare/v0.5.12...v0.5.14)

---
updated-dependencies:
- dependency-name: github.com/ulikunitz/xz
  dependency-version: 0.5.14
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-28 14:53:48 -06:00
Matthew Penner
7bb7696307
Revert "chore: reorder config loading to load config, *then* fill in missing defaults (#185)"
This reverts commit 708cdd0ba8a1db4014898b67a3e6b380b9d94ea6.
2025-08-27 14:47:14 -06:00
dependabot[bot]
695b2eaf86
build(deps): bump golang.org/x/crypto from 0.39.0 to 0.41.0 (#262) 2025-08-11 15:37:25 -06:00
dependabot[bot]
e76dc74ddb
build(deps): bump golang.org/x/sys from 0.33.0 to 0.34.0 (#260)
Bumps [golang.org/x/sys](https://github.com/golang/sys) from 0.33.0 to 0.34.0.
- [Commits](https://github.com/golang/sys/compare/v0.33.0...v0.34.0)

---
updated-dependencies:
- dependency-name: golang.org/x/sys
  dependency-version: 0.34.0
  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>
2025-08-04 13:05:11 -06:00
dependabot[bot]
0d9b0863f9
build(deps): bump github/codeql-action from 3.29.2 to 3.29.5 (#258)
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.29.2 to 3.29.5.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](181d5eefc2...51f77329af)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: 3.29.5
  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>
2025-08-01 12:52:38 -06:00
dependabot[bot]
326db212e9
build(deps): bump docker/metadata-action from 5.7.0 to 5.8.0 (#257)
Bumps [docker/metadata-action](https://github.com/docker/metadata-action) from 5.7.0 to 5.8.0.
- [Release notes](https://github.com/docker/metadata-action/releases)
- [Commits](902fa8ec7d...c1e51972af)

---
updated-dependencies:
- dependency-name: docker/metadata-action
  dependency-version: 5.8.0
  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>
2025-08-01 12:52:26 -06:00
dependabot[bot]
798cbe567d
build(deps): bump github.com/docker/docker (#256)
Bumps [github.com/docker/docker](https://github.com/docker/docker) from 28.2.2+incompatible to 28.3.3+incompatible.
- [Release notes](https://github.com/docker/docker/releases)
- [Commits](https://github.com/docker/docker/compare/v28.2.2...v28.3.3)

---
updated-dependencies:
- dependency-name: github.com/docker/docker
  dependency-version: 28.3.3+incompatible
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-29 16:06:25 -06:00
dependabot[bot]
8b65328390
build(deps): bump github/codeql-action from 3.28.18 to 3.29.2 (#252) 2025-07-01 10:07:33 -06:00
dependabot[bot]
746c7afbfb
build(deps): bump docker/setup-buildx-action from 3.10.0 to 3.11.1 (#251) 2025-07-01 10:07:19 -06:00
dependabot[bot]
c93a1da141
build(deps): bump softprops/action-gh-release from 2.2.2 to 2.3.2 (#250) 2025-07-01 10:07:08 -06:00
dependabot[bot]
06a3167754
build(deps): bump github.com/mholt/archives from 0.1.1 to 0.1.3 (#249)
Bumps [github.com/mholt/archives](https://github.com/mholt/archives) from 0.1.1 to 0.1.3.
- [Release notes](https://github.com/mholt/archives/releases)
- [Commits](https://github.com/mholt/archives/compare/v0.1.1...v0.1.3)

---
updated-dependencies:
- dependency-name: github.com/mholt/archives
  dependency-version: 0.1.3
  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>
2025-06-30 12:07:55 -06:00
dependabot[bot]
ccae513224
build(deps): bump github.com/gin-gonic/gin from 1.10.0 to 1.10.1 (#247)
Bumps [github.com/gin-gonic/gin](https://github.com/gin-gonic/gin) from 1.10.0 to 1.10.1.
- [Release notes](https://github.com/gin-gonic/gin/releases)
- [Changelog](https://github.com/gin-gonic/gin/blob/master/CHANGELOG.md)
- [Commits](https://github.com/gin-gonic/gin/compare/v1.10.0...v1.10.1)

---
updated-dependencies:
- dependency-name: github.com/gin-gonic/gin
  dependency-version: 1.10.1
  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>
2025-06-16 11:47:30 -06:00
dependabot[bot]
56bb2ce298
build(deps): bump golang.org/x/crypto from 0.37.0 to 0.39.0 (#246)
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.37.0 to 0.39.0.
- [Commits](https://github.com/golang/crypto/compare/v0.37.0...v0.39.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-version: 0.39.0
  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>
2025-06-09 10:17:57 -06:00
dependabot[bot]
d1f2c4e641
build(deps): bump github.com/docker/docker (#245) 2025-06-02 10:26:13 -06:00
dependabot[bot]
b6f07bc959
build(deps): bump docker/build-push-action from 6.16.0 to 6.18.0 (#244) 2025-06-02 10:24:23 -06:00
dependabot[bot]
b7d442ecc0
build(deps): bump github/codeql-action from 3.28.16 to 3.28.18 (#243) 2025-06-02 10:24:12 -06:00
dependabot[bot]
a94d4ec5e5
build(deps): bump actions/setup-go from 5.4.0 to 5.5.0 (#242) 2025-06-02 10:24:03 -06:00
dependabot[bot]
35d2e0f040
build(deps): bump golang.org/x/sys from 0.32.0 to 0.33.0 (#238)
Bumps [golang.org/x/sys](https://github.com/golang/sys) from 0.32.0 to 0.33.0.
- [Commits](https://github.com/golang/sys/compare/v0.32.0...v0.33.0)

---
updated-dependencies:
- dependency-name: golang.org/x/sys
  dependency-version: 0.33.0
  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>
2025-05-13 13:51:06 -06:00
dependabot[bot]
d2b4e983f5
build(deps): bump github/codeql-action from 3.28.13 to 3.28.16 (#237) 2025-05-01 18:42:06 +00:00
dependabot[bot]
25aaf127f6
build(deps): bump softprops/action-gh-release from 2.2.1 to 2.2.2 (#236) 2025-05-01 18:41:54 +00:00
dependabot[bot]
cb391bcbb8
build(deps): bump docker/build-push-action from 6.15.0 to 6.16.0 (#235) 2025-05-01 18:40:52 +00:00
dependabot[bot]
2a94eb0af5
build(deps): bump gorm.io/gorm from 1.25.12 to 1.26.0 (#234)
Bumps [gorm.io/gorm](https://github.com/go-gorm/gorm) from 1.25.12 to 1.26.0.
- [Release notes](https://github.com/go-gorm/gorm/releases)
- [Commits](https://github.com/go-gorm/gorm/compare/v1.25.12...v1.26.0)

---
updated-dependencies:
- dependency-name: gorm.io/gorm
  dependency-version: 1.26.0
  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>
2025-04-28 09:24:27 -06:00
dependabot[bot]
ae1192f760
build(deps): bump golang.org/x/net from 0.37.0 to 0.38.0 (#229)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.37.0 to 0.38.0.
- [Commits](https://github.com/golang/net/compare/v0.37.0...v0.38.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-16 17:45:26 -06:00
dependabot[bot]
3855eecb60
build(deps): bump golang.org/x/crypto from 0.36.0 to 0.37.0 (#223)
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.36.0 to 0.37.0.
- [Commits](https://github.com/golang/crypto/compare/v0.36.0...v0.37.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-version: 0.37.0
  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>
2025-04-07 10:30:52 -06:00
dependabot[bot]
3ea9e6991a
build(deps): bump golang.org/x/sync from 0.12.0 to 0.13.0 (#224) 2025-04-07 10:16:08 -06:00
dependabot[bot]
9533f4f04a
build(deps): bump golang.org/x/sys from 0.31.0 to 0.32.0 (#225) 2025-04-07 10:03:50 -06:00
dependabot[bot]
0bc3df9306
build(deps): bump github.com/mholt/archives from 0.1.0 to 0.1.1 (#226) 2025-04-07 10:03:37 -06:00
Matthew Penner
a4e16748b0
flake: switch to go_1_24
Signed-off-by: Matthew Penner <me@matthewp.io>
2025-04-01 13:31:48 -06:00
dependabot[bot]
3d90ac5909
build(deps): bump actions/upload-artifact from 4.6.1 to 4.6.2 (#219)
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.6.1 to 4.6.2.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](4cec3d8aa0...ea165f8d65)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  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>
2025-04-01 11:50:49 -06:00
dependabot[bot]
74fd19c86f
build(deps): bump actions/setup-go from 5.3.0 to 5.4.0 (#220)
Bumps [actions/setup-go](https://github.com/actions/setup-go) from 5.3.0 to 5.4.0.
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](f111f3307d...0aaccfd150)

---
updated-dependencies:
- dependency-name: actions/setup-go
  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>
2025-04-01 11:50:36 -06:00
dependabot[bot]
cecdc8c612
build(deps): bump github/codeql-action from 3.28.11 to 3.28.13 (#221)
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.28.11 to 3.28.13.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](6bb031afdd...1b549b9259)

---
updated-dependencies:
- dependency-name: github/codeql-action
  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>
2025-04-01 11:49:28 -06:00
dependabot[bot]
489af48eb9
build(deps): bump docker/login-action from 3.3.0 to 3.4.0 (#222)
Bumps [docker/login-action](https://github.com/docker/login-action) from 3.3.0 to 3.4.0.
- [Release notes](https://github.com/docker/login-action/releases)
- [Commits](9780b0c442...74a5d14239)

---
updated-dependencies:
- dependency-name: docker/login-action
  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>
2025-04-01 11:49:06 -06:00
dependabot[bot]
104fe1aace
build(deps): bump github.com/docker/docker (#217)
Bumps [github.com/docker/docker](https://github.com/docker/docker) from 28.0.2+incompatible to 28.0.4+incompatible.
- [Release notes](https://github.com/docker/docker/releases)
- [Commits](https://github.com/docker/docker/compare/v28.0.2...v28.0.4)

---
updated-dependencies:
- dependency-name: github.com/docker/docker
  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>
2025-03-31 11:43:36 -06:00
dependabot[bot]
81e8c9a8c4
build(deps): bump github.com/pkg/sftp from 1.13.8 to 1.13.9 (#218)
Bumps [github.com/pkg/sftp](https://github.com/pkg/sftp) from 1.13.8 to 1.13.9.
- [Release notes](https://github.com/pkg/sftp/releases)
- [Commits](https://github.com/pkg/sftp/compare/v1.13.8...v1.13.9)

---
updated-dependencies:
- dependency-name: github.com/pkg/sftp
  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>
2025-03-31 11:43:17 -06:00
dependabot[bot]
9ff918bf0b
build(deps): bump github.com/docker/docker (#216)
Bumps [github.com/docker/docker](https://github.com/docker/docker) from 28.0.1+incompatible to 28.0.2+incompatible.
- [Release notes](https://github.com/docker/docker/releases)
- [Commits](https://github.com/docker/docker/compare/v28.0.1...v28.0.2)

---
updated-dependencies:
- dependency-name: github.com/docker/docker
  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>
2025-03-24 13:10:55 -06:00
dependabot[bot]
d5097e5f59
build(deps): bump github.com/pkg/sftp from 1.13.7 to 1.13.8 (#215)
Bumps [github.com/pkg/sftp](https://github.com/pkg/sftp) from 1.13.7 to 1.13.8.
- [Release notes](https://github.com/pkg/sftp/releases)
- [Commits](https://github.com/pkg/sftp/compare/v1.13.7...v1.13.8)

---
updated-dependencies:
- dependency-name: github.com/pkg/sftp
  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>
2025-03-17 12:23:14 -06:00
Matthew Penner
e311206d6b
config: handle old AuthenticationToken value
Signed-off-by: Matthew Penner <me@matthewp.io>
2025-03-17 12:16:29 -06:00
Matthew Penner
f18726c874
chore: avoid exiting if config file is not writable
Signed-off-by: Matthew Penner <me@matthewp.io>
2025-03-10 12:54:25 -06:00
Matthew Penner
49b04fe9bb
fix: use of old AuthenticationToken value
Signed-off-by: Matthew Penner <me@matthewp.io>
2025-03-10 12:40:52 -06:00
Matthew Penner
9535aae52e
fix: Duplicated key token in config panic
Signed-off-by: Matthew Penner <me@matthewp.io>
2025-03-10 12:35:46 -06:00
Matthew Penner
4b4e8f8fa0
system: fix test relying on reflection to determine if mutex is locked
Signed-off-by: Matthew Penner <me@matthewp.io>
2025-03-09 20:00:45 -06:00
Matthew Penner
91e016249c
ci: fix codeql workflow matrix
Signed-off-by: Matthew Penner <me@matthewp.io>
2025-03-09 19:51:20 -06:00
Matthew Penner
59905a6b69
go: update dependencies
Signed-off-by: Matthew Penner <me@matthewp.io>
2025-03-09 19:50:03 -06:00
Matthew Penner
d49607de0e
feat: add support for loading token from env and file (WINGS_TOKEN and WINGS_TOKEN_ID)
Signed-off-by: Matthew Penner <me@matthewp.io>
2025-03-09 19:48:43 -06:00
Matthew Penner
91ed7f25e1
chore: add .editorconfig
Signed-off-by: Matthew Penner <me@matthewp.io>
2025-03-09 19:26:32 -06:00
Matthew Penner
a4ff433d95
chore: remove outdated wings-api.paw
Signed-off-by: Matthew Penner <me@matthewp.io>
2025-03-09 19:26:13 -06:00
Matthew Penner
7f63162d1d
nix: update flake.lock
Flake lock file updates:

• Updated input 'flake-parts':
    'github:hercules-ci/flake-parts/205b12d8b7cd4802fbcb8e8ef6a0f1408781a4f9?narHash=sha256-4pDvzqnegAfRkPwO3wmwBhVi/Sye1mzps0zHWYnP88c%3D' (2024-12-04)
  → 'github:hercules-ci/flake-parts/f4330d22f1c5d2ba72d3d22df5597d123fdb60a9?narHash=sha256-%2Bu2UunDA4Cl5Fci3m7S643HzKmIDAe%2BfiXrLqYsR2fs%3D' (2025-03-07)
• Updated input 'flake-parts/nixpkgs-lib':
    '5487e69da4.tar.gz?narHash=sha256-1qRH7uAUsyQI7R1Uwl4T%2BXvdNv778H0Nb5njNrqvylY%3D' (2024-12-01)
  → 'github:nix-community/nixpkgs.lib/147dee35aab2193b174e4c0868bd80ead5ce755c?narHash=sha256-oiwv/ZK/2FhGxrCkQkB83i7GnWXPPLzoqFHpDD3uYpk%3D' (2025-03-02)
• Updated input 'nixpkgs':
    'github:NixOS/nixpkgs/d0797a04b81caeae77bcff10a9dde78bc17f5661?narHash=sha256-kEsTJTUQfQFIJOcLYFt/RvNxIK653ZkTBIs4DG%2BcBns%3D' (2024-12-05)
  → 'github:NixOS/nixpkgs/36fd87baa9083f34f7f5027900b62ee6d09b1f2f?narHash=sha256-Wh7esNh7G24qYleLvgOSY/7HlDUzWaL/n4qzlBePpiw%3D' (2025-03-07)
• Updated input 'treefmt-nix':
    'github:numtide/treefmt-nix/50862ba6a8a0255b87377b9d2d4565e96f29b410?narHash=sha256-qKL3vjO%2BIXFQ0nTinFDqNq/sbbnnS5bMI1y0xX215fU%3D' (2024-12-05)
  → 'github:numtide/treefmt-nix/3d0579f5cc93436052d94b73925b48973a104204?narHash=sha256-mL1szCeIsjh6Khn3nH2cYtwO5YXG6gBiTw1A30iGeDU%3D' (2025-02-17)
2025-03-09 19:25:45 -06:00
Matthew Penner
d0e7332881
ci: add dependabot for github-actions and gomod
Signed-off-by: Matthew Penner <me@matthewp.io>
2025-03-09 19:25:18 -06:00
Matthew Penner
5e9a1c7139
go: update to 1.23.7, enable 1.24 in matrix
Signed-off-by: Matthew Penner <me@matthewp.io>
2025-03-09 19:24:55 -06:00
Matthew Penner
407b783aa5
ufs: improve error handling
Signed-off-by: Matthew Penner <me@matthewp.io>
2025-03-09 19:19:29 -06:00
Matthew Penner
25966e7838
system: prevent deadlock while sending to a SinkPool
Signed-off-by: Matthew Penner <me@matthewp.io>
2025-03-09 19:19:22 -06:00
Arnaud Lier
77153ffbb1
ci: fix broken commit hash (#212) 2025-02-14 13:17:39 -07:00
Matthew Penner
c6c235dbc0
cron: batch deletions of activity logs to workaround sqlite limitation
Signed-off-by: Matthew Penner <me@matthewp.io>
2025-01-12 15:28:46 -07:00
Matthew Penner
a55277da47
ufs: ensure fsDirfd always gets closed (fix file descriptor leak)
Signed-off-by: Matthew Penner <me@matthewp.io>
2025-01-12 14:45:16 -07:00
dependabot[bot]
76a9f6dc5a
build(deps): bump golang.org/x/net from 0.32.0 to 0.33.0 (#211)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.32.0 to 0.33.0.
- [Commits](https://github.com/golang/net/compare/v0.32.0...v0.33.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>
2025-01-12 21:25:34 +00:00
dependabot[bot]
0e96ef3edf
build(deps): bump golang.org/x/crypto from 0.30.0 to 0.31.0 (#210)
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.30.0 to 0.31.0.
- [Commits](https://github.com/golang/crypto/compare/v0.30.0...v0.31.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-12 21:25:19 +00:00
Matthew Penner
1dee350268
server(filesystem): fix archive filesystem
Signed-off-by: Matthew Penner <me@matthewp.io>
2025-01-12 14:22:18 -07:00
Daniel Barton
ec14db6142
fix: backups unable to be restored 2024-12-21 18:48:14 -07:00
Matthew Penner
c83875ffc0
go: remove toolchain directive
Signed-off-by: Matthew Penner <me@matthewp.io>
2024-12-09 11:07:26 -07:00
Matthew Penner
b4dc19fca1
Revert "fix: remove unnecessary seek call and wrap errors with additional information"
This reverts commit a2be26574c895f0fc9c62675cc03c4064f84a3d9.

I thought this was unnecessary but only if we use the reader returned by
`archives.Identify`, since we use the original file we still need this.
2024-12-09 10:58:26 -07:00
Matthew Penner
a2be26574c
fix: remove unnecessary seek call and wrap errors with additional information
Signed-off-by: Matthew Penner <me@matthewp.io>
2024-12-07 19:06:48 -07:00
Matthew Penner
8045318e44
go: update dependencies
Signed-off-by: Matthew Penner <me@matthewp.io>
2024-12-07 14:54:17 -07:00
Matthew Penner
024fe548ed
chore: switch from mholt/archiver/v4 to mholt/archives
Fixes https://github.com/pterodactyl/panel/issues/5253

Signed-off-by: Matthew Penner <me@matthewp.io>
2024-12-07 14:46:59 -07:00
Matthew Penner
82ae64b4c6
go: 1.22.6 -> 1.22.10, 1.23.0 -> 1.23.4
Signed-off-by: Matthew Penner <me@matthewp.io>
2024-12-07 14:45:47 -07:00
Matthew Penner
f221cde754
nix: update flake.lock
Flake lock file updates:

• Updated input 'flake-parts':
    'github:hercules-ci/flake-parts/9227223f6d922fee3c7b190b2cc238a99527bbb7?narHash=sha256-pQMhCCHyQGRzdfAkdJ4cIWiw%2BJNuWsTX7f0ZYSyz0VY%3D' (2024-07-03)
  → 'github:hercules-ci/flake-parts/205b12d8b7cd4802fbcb8e8ef6a0f1408781a4f9?narHash=sha256-4pDvzqnegAfRkPwO3wmwBhVi/Sye1mzps0zHWYnP88c%3D' (2024-12-04)
• Updated input 'flake-parts/nixpkgs-lib':
    '5daf051448.tar.gz?narHash=sha256-Fm2rDDs86sHy0/1jxTOKB1118Q0O3Uc7EC0iXvXKpbI%3D' (2024-07-01)
  → '5487e69da4.tar.gz?narHash=sha256-1qRH7uAUsyQI7R1Uwl4T%2BXvdNv778H0Nb5njNrqvylY%3D' (2024-12-01)
• Updated input 'nixpkgs':
    'github:NixOS/nixpkgs/68c9ed8bbed9dfce253cc91560bf9043297ef2fe?narHash=sha256-Tybxt65eyOARf285hMHIJ2uul8SULjFZbT9ZaEeUnP8%3D' (2024-07-21)
  → 'github:NixOS/nixpkgs/d0797a04b81caeae77bcff10a9dde78bc17f5661?narHash=sha256-kEsTJTUQfQFIJOcLYFt/RvNxIK653ZkTBIs4DG%2BcBns%3D' (2024-12-05)
• Updated input 'treefmt-nix':
    'github:numtide/treefmt-nix/8db8970be1fb8be9c845af7ebec53b699fe7e009?narHash=sha256-6Pqa0bi5nV74IZcENKYRToRNM5obo1EQ%2B3ihtunJ014%3D' (2024-07-23)
  → 'github:numtide/treefmt-nix/50862ba6a8a0255b87377b9d2d4565e96f29b410?narHash=sha256-qKL3vjO%2BIXFQ0nTinFDqNq/sbbnnS5bMI1y0xX215fU%3D' (2024-12-05)
2024-12-07 14:36:09 -07:00
Matthew Penner
63c4c1ce57
Update README.md
Signed-off-by: Matthew Penner <me@matthewp.io>
2024-10-21 19:12:09 -06:00
Matthew Penner
b4a9a1c5de
ci: update workflow permissions
Signed-off-by: Matthew Penner <me@matthewp.io>
2024-08-28 20:02:10 -06:00
dependabot[bot]
7daaaaac18
build(deps): bump github.com/docker/docker (#202)
Bumps [github.com/docker/docker](https://github.com/docker/docker) from 25.0.4+incompatible to 25.0.6+incompatible.
- [Release notes](https://github.com/docker/docker/releases)
- [Commits](https://github.com/docker/docker/compare/v25.0.4...v25.0.6)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-28 19:51:23 -06:00
Daniel Barton
708cdd0ba8
chore: reorder config loading to load config, *then* fill in missing defaults (#185) 2024-08-28 19:48:57 -06:00
Matthew Penner
adb2b26ae0
go: update dependencies (#198)
Signed-off-by: Matthew Penner <me@matthewp.io>
2024-08-28 19:47:33 -06:00
Daniel Barton
6c9d3670c8
fix: symlinks not flagged correctly in file stat (#199) 2024-08-28 19:47:12 -06:00
dannyhpy
18306badaf
chore: expose default SFTP port in Dockerfile (#193)
The purpose of `EXPOSE` is to automatically map ports when running a container with Docker, removing the need for the user to specify what ports should be mapped to the host.
2024-08-28 19:46:32 -06:00
dannyhpy
34bd2b54e5
ci(docker): use GitHub token and repository name (#196) 2024-08-28 19:45:11 -06:00
Matthew Penner
d023c97334
ci: update to go1.22.6, run tests against go1.23.0
Signed-off-by: Matthew Penner <me@matthewp.io>
2024-08-28 19:33:23 -06:00
Matthew Penner
8744e64f1d
fix: update ufs, fix issues with network filesystems and random file not found during directory walking
Signed-off-by: Matthew Penner <me@matthewp.io>
2024-08-28 19:31:50 -06:00
Matthew Penner
3b88bbc7aa
chore: update sponsors
Signed-off-by: Matthew Penner <me@matthewp.io>
2024-08-28 19:28:09 -06:00
Matthew Penner
d739948989
feat: add ability to mount generated passwd files to containers (#197)
This PR will add an option to mount:
- `/etc/group`
- `/etc/passwd`

Signed-off-by: Matthew Penner <me@matthewp.io>
2024-07-24 15:12:42 -06:00
Matthew Penner
ac260bd5ee
ci: update to go1.22.5, drop go1.21
Signed-off-by: Matthew Penner <me@matthewp.io>
2024-07-24 13:14:25 -06:00
Matthew Penner
2f4a0d7262
nix: update flake.lock
Flake lock file updates:

• Updated input 'flake-parts':
    'github:hercules-ci/flake-parts/b253292d9c0a5ead9bc98c4e9a26c6312e27d69f?narHash=sha256-a0NYyp%2Bh9hlb7ddVz4LUn1vT/PLwqfrWYcHMvFB1xYg%3D' (2024-02-01)
  → 'github:hercules-ci/flake-parts/9227223f6d922fee3c7b190b2cc238a99527bbb7?narHash=sha256-pQMhCCHyQGRzdfAkdJ4cIWiw%2BJNuWsTX7f0ZYSyz0VY%3D' (2024-07-03)
• Updated input 'flake-parts/nixpkgs-lib':
    'github:NixOS/nixpkgs/97b17f32362e475016f942bbdfda4a4a72a8a652?dir=lib&narHash=sha256-UcsnCG6wx%2B%2B23yeER4Hg18CXWbgNpqNXcHIo5/1Y%2Bhc%3D' (2024-01-29)
  → '5daf051448.tar.gz?narHash=sha256-Fm2rDDs86sHy0/1jxTOKB1118Q0O3Uc7EC0iXvXKpbI%3D' (2024-07-01)
• Updated input 'nixpkgs':
    'github:NixOS/nixpkgs/a4d4fe8c5002202493e87ec8dbc91335ff55552c?narHash=sha256-ZL2TrjVsiFNKOYwYQozpbvQSwvtV/3Me7Zwhmdsfyu4%3D' (2024-02-15)
  → 'github:NixOS/nixpkgs/68c9ed8bbed9dfce253cc91560bf9043297ef2fe?narHash=sha256-Tybxt65eyOARf285hMHIJ2uul8SULjFZbT9ZaEeUnP8%3D' (2024-07-21)
• Updated input 'treefmt-nix':
    'github:numtide/treefmt-nix/ac599dab59a66304eb511af07b3883114f061b9d?narHash=sha256-qQF0fEkHlnxHcrKIMRzOETnRBksUK048MXkX0SOmxvA%3D' (2024-02-07)
  → 'github:numtide/treefmt-nix/8db8970be1fb8be9c845af7ebec53b699fe7e009?narHash=sha256-6Pqa0bi5nV74IZcENKYRToRNM5obo1EQ%2B3ihtunJ014%3D' (2024-07-23)
2024-07-24 13:10:35 -06:00
EpicPlayerA10
1d8b383682
fix: only count hard-links once when calculating filesystem usage (#181) 2024-06-29 12:56:51 -06:00
Arnaud Lier
934bf2493d
fix: properly use base2 (1024, *bibyte) when calculating memory limits (#190) 2024-06-29 12:34:20 -06:00
Daniel Barton
29e4425e21
fix: overhaul docker container termination signals (#192)
Fixes https://github.com/pterodactyl/panel/issues/4783

Requires https://github.com/pterodactyl/panel/pull/5132 to work
2024-06-29 12:31:36 -06:00
Daniel Barton
5a15612754
chore: show the actual location in "config not found" error (#179) 2024-06-29 12:26:59 -06:00
Daniel Barton
ad1ae862a9
fix: user-defined labels not being passed to environment (#191) 2024-06-29 12:25:47 -06:00
Matthew Penner
3114a3b82e
Update README.md
Signed-off-by: Matthew Penner <me@matthewp.io>
2024-06-22 19:21:54 -06:00
Matthew Penner
500f217514
Update CHANGELOG.md 2024-05-13 16:01:33 -06:00
Daniel Barton
9ffbcdcdb1
cmd: handle relative paths to the config file (#180) 2024-05-13 15:53:33 -06:00
Daniel Barton
9b341db2db
server(filesystem): fix sort position of directories (#188) 2024-05-13 15:52:10 -06:00
69 changed files with 1779 additions and 768 deletions

21
.editorconfig Normal file
View File

@ -0,0 +1,21 @@
root = true
[*]
indent_style = tab
indent_size = 4
tab_width = 4
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.go]
max_line_length = 100
[*.md]
trim_trailing_whitespace = false
[*.{md,nix,yaml}]
indent_style = space
indent_size = 2
tab_width = 2

9
.envrc
View File

@ -1 +1,8 @@
use flake #!/usr/bin/env sh
# Load the flake's `devShells.${currentSystem}.default`.
if ! use flake .; then
echo 'The development shell was unable to be built.' >&2
echo 'The development environment was not loaded.' >&2
echo 'Please make the necessary changes in flake.nix to fix any issues and hit enter to try again.' >&2
fi

10
.github/dependabot.yaml vendored Normal file
View File

@ -0,0 +1,10 @@
version: 2
updates:
- package-ecosystem: github-actions
directory: /
schedule:
interval: monthly
- package-ecosystem: gomod
directory: /
schedule:
interval: weekly

View File

@ -13,30 +13,26 @@ on:
jobs: jobs:
analyze: analyze:
name: Analyze name: Analyze
runs-on: ubuntu-22.04 runs-on: ubuntu-24.04
strategy:
fail-fast: false
matrix:
include:
- language: go
build-mode: autobuild
permissions: permissions:
actions: read actions: read
contents: read contents: read
security-events: write security-events: write
strategy:
fail-fast: false
matrix:
language:
- go
steps: steps:
- name: Code Checkout - name: Code checkout
uses: actions/checkout@v4 uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Initialize CodeQL - name: Initialize CodeQL
uses: github/codeql-action/init@v2 uses: github/codeql-action/init@0499de31b99561a6d14a36a5f662c2a54f91beee # v3.29.5
with: with:
languages: ${{ matrix.language }} languages: ${{ matrix.language }}
build-mode: ${{ matrix.build-mode }}
- name: Autobuild
uses: github/codeql-action/autobuild@v2
- name: Perform CodeQL Analysis - name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2 uses: github/codeql-action/analyze@0499de31b99561a6d14a36a5f662c2a54f91beee # v3.29.5

View File

@ -11,18 +11,21 @@ on:
jobs: jobs:
build-and-push: build-and-push:
name: Build and Push name: Build and Push
runs-on: ubuntu-22.04 runs-on: ubuntu-24.04
# Always run against a tag, even if the commit into the tag has [docker skip] within the commit message. # Always run against a tag, even if the commit into the tag has [docker skip] within the commit message.
if: "!contains(github.ref, 'develop') || (!contains(github.event.head_commit.message, 'skip docker') && !contains(github.event.head_commit.message, 'docker skip'))" if: "!contains(github.ref, 'develop') || (!contains(github.event.head_commit.message, 'skip docker') && !contains(github.event.head_commit.message, 'docker skip'))"
permissions:
contents: read
packages: write
steps: steps:
- name: Code checkout - name: Code checkout
uses: actions/checkout@v4 uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Docker metadata - name: Docker metadata
id: docker_meta id: docker_meta
uses: docker/metadata-action@v5 uses: docker/metadata-action@c1e51972afc2121e065aed6d45c65596fe445f3f # v5.8.0
with: with:
images: ghcr.io/pterodactyl/wings images: ghcr.io/${{ github.repository }}
flavor: | flavor: |
latest=false latest=false
tags: | tags: |
@ -31,17 +34,17 @@ jobs:
type=ref,event=branch type=ref,event=branch
- name: Setup QEMU - name: Setup QEMU
uses: docker/setup-qemu-action@v3 uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0
- name: Setup Docker buildx - name: Setup Docker buildx
uses: docker/setup-buildx-action@v3 uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
- name: Login to GitHub Container Registry - name: Login to GitHub Container Registry
uses: docker/login-action@v3 uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
with: with:
registry: ghcr.io registry: ghcr.io
username: ${{ github.repository_owner }} username: ${{ github.actor }}
password: ${{ secrets.REGISTRY_TOKEN }} password: ${{ secrets.GITHUB_TOKEN }}
- name: Get Build Information - name: Get Build Information
id: build_info id: build_info
@ -50,7 +53,7 @@ jobs:
echo "short_sha=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT echo "short_sha=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
- name: Build and Push (tag) - name: Build and Push (tag)
uses: docker/build-push-action@v5 uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
if: "github.event_name == 'release' && github.event.action == 'published'" if: "github.event_name == 'release' && github.event.action == 'published'"
with: with:
context: . context: .
@ -63,7 +66,7 @@ jobs:
tags: ${{ steps.docker_meta.outputs.tags }} tags: ${{ steps.docker_meta.outputs.tags }}
- name: Build and Push (develop) - name: Build and Push (develop)
uses: docker/build-push-action@v5 uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
if: "github.event_name == 'push' && contains(github.ref, 'develop')" if: "github.event_name == 'push' && contains(github.ref, 'develop')"
with: with:
context: . context: .

View File

@ -15,20 +15,21 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
os: [ubuntu-22.04] os: [ubuntu-24.04]
go: ["1.21.9", "1.22.2"] go: ["1.23.7", "1.24.1"]
goos: [linux] goos: [linux]
goarch: [amd64, arm64] goarch: [amd64, arm64]
permissions:
contents: read
steps: steps:
- name: Code checkout
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Setup Go - name: Setup Go
uses: actions/setup-go@v5 uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0
with: with:
go-version: ${{ matrix.go }} go-version: ${{ matrix.go }}
- name: Code checkout
uses: actions/checkout@v4
- name: go mod download - name: go mod download
env: env:
CGO_ENABLED: 0 CGO_ENABLED: 0
@ -42,8 +43,8 @@ jobs:
CGO_ENABLED: 0 CGO_ENABLED: 0
SRC_PATH: github.com/pterodactyl/wings SRC_PATH: github.com/pterodactyl/wings
run: | run: |
go build -v -trimpath -ldflags="-s -w -X ${SRC_PATH}/system.Version=dev-${GIT_COMMIT:0:7}" -o dist/wings ${SRC_PATH} go build -v -trimpath -ldflags="-s -w -X ${SRC_PATH}/system.Version=dev-${GITHUB_SHA:0:7}" -o dist/wings ${SRC_PATH}
go build -v -trimpath -ldflags="-X ${SRC_PATH}/system.Version=dev-${GIT_COMMIT:0:7}" -o dist/wings_debug ${SRC_PATH} go build -v -trimpath -ldflags="-X ${SRC_PATH}/system.Version=dev-${GITHUB_SHA:0:7}" -o dist/wings_debug ${SRC_PATH}
chmod 755 dist/* chmod 755 dist/*
- name: go test - name: go test
@ -61,15 +62,15 @@ jobs:
go test -race $(go list ./...) go test -race $(go list ./...)
- name: Upload Release Artifact - name: Upload Release Artifact
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
if: ${{ (github.ref == 'refs/heads/develop' || github.event_name == 'pull_request') && matrix.go == '1.21.8' }} if: ${{ (github.ref == 'refs/heads/develop' || github.event_name == 'pull_request') && matrix.go == '1.23.7' }}
with: with:
name: wings_linux_${{ matrix.goarch }} name: wings_linux_${{ matrix.goarch }}
path: dist/wings path: dist/wings
- name: Upload Debug Artifact - name: Upload Debug Artifact
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
if: ${{ (github.ref == 'refs/heads/develop' || github.event_name == 'pull_request') && matrix.go == '1.21.8' }} if: ${{ (github.ref == 'refs/heads/develop' || github.event_name == 'pull_request') && matrix.go == '1.23.7' }}
with: with:
name: wings_linux_${{ matrix.goarch }}_debug name: wings_linux_${{ matrix.goarch }}_debug
path: dist/wings_debug path: dist/wings_debug

View File

@ -8,16 +8,18 @@ on:
jobs: jobs:
release: release:
name: Release name: Release
runs-on: ubuntu-22.04 runs-on: ubuntu-24.04
permissions:
contents: write # write is required to create releases and push.
steps: steps:
- name: Code Checkout - name: Code checkout
uses: actions/checkout@v4 uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Setup Go - name: Setup Go
uses: actions/setup-go@v5 uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0
with: with:
go-version: "1.21.9" go-version: "1.23.7"
- name: Build release binaries - name: Build release binaries
env: env:
@ -57,41 +59,13 @@ jobs:
git push git push
- name: Create release - name: Create release
id: create_release uses: softprops/action-gh-release@6da8fa9354ddfdc4aeace5fc48d7f679b5214090 # v2.4.1
uses: softprops/action-gh-release@v1
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with: with:
draft: true draft: true
prerelease: ${{ contains(github.ref, 'rc') || contains(github.ref, 'beta') || contains(github.ref, 'alpha') }} prerelease: ${{ contains(github.ref, 'rc') || contains(github.ref, 'beta') || contains(github.ref, 'alpha') }}
body_path: ./RELEASE_CHANGELOG body_path: ./RELEASE_CHANGELOG
files: |
- name: Upload amd64 binary dist/*
uses: actions/upload-release-asset@v1 checksums.txt
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: dist/wings_linux_amd64
asset_name: wings_linux_amd64
asset_content_type: application/octet-stream
- name: Upload arm64 binary
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: dist/wings_linux_arm64
asset_name: wings_linux_arm64
asset_content_type: application/octet-stream
- name: Upload checksum
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./checksums.txt
asset_name: checksums.txt
asset_content_type: text/plain

View File

@ -1,5 +1,15 @@
# Changelog # Changelog
## v1.11.14
### Added
* Support relative file paths for the Wings config ([#180](https://github.com/pterodactyl/wings/pull/180))
### Fixed
* Folders not being sorted before files properly ([#5078](https://github.com/pterodactyl/panel/issues/5078)
## v1.11.13 ## v1.11.13
### Fixed ### Fixed

View File

@ -1,8 +1,8 @@
# Stage 1 (Build) # Stage 1 (Build)
FROM golang:1.21.9-alpine AS builder FROM golang:1.23.7-alpine AS builder
ARG VERSION ARG VERSION
RUN apk add --update --no-cache git make RUN apk add --update --no-cache git make mailcap
WORKDIR /app/ WORKDIR /app/
COPY go.mod go.sum /app/ COPY go.mod go.sum /app/
RUN go mod download RUN go mod download
@ -18,8 +18,11 @@ RUN echo "ID=\"distroless\"" > /etc/os-release
# Stage 2 (Final) # Stage 2 (Final)
FROM gcr.io/distroless/static:latest FROM gcr.io/distroless/static:latest
COPY --from=builder /etc/os-release /etc/os-release COPY --from=builder /etc/os-release /etc/os-release
COPY --from=builder /etc/mime.types /etc/mime.types
COPY --from=builder /app/wings /usr/bin/ COPY --from=builder /app/wings /usr/bin/
CMD [ "/usr/bin/wings", "--config", "/etc/pterodactyl/config.yml" ]
EXPOSE 8080 ENTRYPOINT ["/usr/bin/wings"]
CMD ["--config", "/etc/pterodactyl/config.yml"]
EXPOSE 8080 2022

View File

@ -18,14 +18,14 @@ dependencies, and allowing users to authenticate with the same credentials they
I would like to extend my sincere thanks to the following sponsors for helping fund Pterodactyl's development. I would like to extend my sincere thanks to the following sponsors for helping fund Pterodactyl's development.
[Interested in becoming a sponsor?](https://github.com/sponsors/matthewpi) [Interested in becoming a sponsor?](https://github.com/sponsors/matthewpi)
| Company | About | | Company | About |
|--------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| |-----------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| [**Aussie Server Hosts**](https://aussieserverhosts.com/) | No frills Australian Owned and operated High Performance Server hosting for some of the most demanding games serving Australia and New Zealand. | | [**Aussie Server Hosts**](https://aussieserverhosts.com/) | No frills Australian Owned and operated High Performance Server hosting for some of the most demanding games serving Australia and New Zealand. |
| [**BisectHosting**](https://www.bisecthosting.com/) | BisectHosting provides Minecraft, Valheim and other server hosting services with the highest reliability and lightning fast support since 2012. | | [**BisectHosting**](https://www.bisecthosting.com/) | BisectHosting provides Minecraft, Valheim and other server hosting services with the highest reliability and lightning fast support since 2012. |
| [**MineStrator**](https://minestrator.com/) | Looking for the most highend French hosting company for your minecraft server? More than 24,000 members on our discord trust us. Give us a try! | | [**MineStrator**](https://minestrator.com/) | Looking for the most highend French hosting company for your minecraft server? More than 24,000 members on our discord trust us. Give us a try! |
| [**HostEZ**](https://hostez.io) | US & EU Rust & Minecraft Hosting. DDoS Protected bare metal, VPS and colocation with low latency, high uptime and maximum availability. EZ! | | [**HostEZ**](https://hostez.io) | US & EU Rust & Minecraft Hosting. DDoS Protected bare metal, VPS and colocation with low latency, high uptime and maximum availability. EZ! |
| [**Blueprint**](https://blueprint.zip/?pterodactyl=true) | Create and install Pterodactyl addons and themes with the growing Blueprint framework - the package-manager for Pterodactyl. Use multiple modifications at once without worrying about conflicts and make use of the large extension ecosystem. | | [**Blueprint**](https://blueprint.zip/?utm_source=pterodactyl&utm_medium=sponsor) | Create and install Pterodactyl addons and themes with the growing Blueprint framework - the package-manager for Pterodactyl. Use multiple modifications at once without worrying about conflicts and make use of the large extension ecosystem. |
| [**indifferent broccoli**](https://indifferentbroccoli.com/) | indifferent broccoli is a game server hosting and rental company. With us, you get top-notch computer power for your gaming sessions. We destroy lag, latency, and complexity--letting you focus on the fun stuff. | | [**indifferent broccoli**](https://indifferentbroccoli.com/) | indifferent broccoli is a game server hosting and rental company. With us, you get top-notch computer power for your gaming sessions. We destroy lag, latency, and complexity--letting you focus on the fun stuff. |
## Documentation ## Documentation

View File

@ -2,6 +2,7 @@ package cmd
import ( import (
"crypto/tls" "crypto/tls"
"encoding/json"
"fmt" "fmt"
"io" "io"
"net/http" "net/http"
@ -13,7 +14,6 @@ import (
"github.com/AlecAivazis/survey/v2" "github.com/AlecAivazis/survey/v2"
"github.com/AlecAivazis/survey/v2/terminal" "github.com/AlecAivazis/survey/v2/terminal"
"github.com/goccy/go-json"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/pterodactyl/wings/config" "github.com/pterodactyl/wings/config"

View File

@ -2,6 +2,7 @@ package cmd
import ( import (
"context" "context"
"encoding/json"
"errors" "errors"
"fmt" "fmt"
"io" "io"
@ -17,9 +18,9 @@ import (
"github.com/AlecAivazis/survey/v2/terminal" "github.com/AlecAivazis/survey/v2/terminal"
"github.com/apex/log" "github.com/apex/log"
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
dockersystem "github.com/docker/docker/api/types/system"
"github.com/docker/docker/pkg/parsers/kernel" "github.com/docker/docker/pkg/parsers/kernel"
"github.com/docker/docker/pkg/parsers/operatingsystem" "github.com/docker/docker/pkg/parsers/operatingsystem"
"github.com/goccy/go-json"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/pterodactyl/wings/config" "github.com/pterodactyl/wings/config"
@ -206,18 +207,18 @@ func diagnosticsCmdRun(*cobra.Command, []string) {
} }
} }
func getDockerInfo() (types.Version, types.Info, error) { func getDockerInfo() (types.Version, dockersystem.Info, error) {
client, err := environment.Docker() client, err := environment.Docker()
if err != nil { if err != nil {
return types.Version{}, types.Info{}, err return types.Version{}, dockersystem.Info{}, err
} }
dockerVersion, err := client.ServerVersion(context.Background()) dockerVersion, err := client.ServerVersion(context.Background())
if err != nil { if err != nil {
return types.Version{}, types.Info{}, err return types.Version{}, dockersystem.Info{}, err
} }
dockerInfo, err := client.Info(context.Background()) dockerInfo, err := client.Info(context.Background())
if err != nil { if err != nil {
return types.Version{}, types.Info{}, err return types.Version{}, dockersystem.Info{}, err
} }
return dockerVersion, dockerInfo, nil return dockerVersion, dockerInfo, nil
} }

View File

@ -13,7 +13,7 @@ import (
"path/filepath" "path/filepath"
"runtime" "runtime"
"strconv" "strconv"
"strings" "syscall"
"time" "time"
"github.com/NYTimes/logrotate" "github.com/NYTimes/logrotate"
@ -104,6 +104,7 @@ func rootCmdRun(cmd *cobra.Command, _ []string) {
if err := config.ConfigureTimezone(); err != nil { if err := config.ConfigureTimezone(); err != nil {
log.WithField("error", err).Fatal("failed to detect system timezone or use supplied configuration value") log.WithField("error", err).Fatal("failed to detect system timezone or use supplied configuration value")
return
} }
log.WithField("timezone", config.Get().System.Timezone).Info("configured wings with system timezone") log.WithField("timezone", config.Get().System.Timezone).Info("configured wings with system timezone")
if err := config.ConfigureDirectories(); err != nil { if err := config.ConfigureDirectories(); err != nil {
@ -112,6 +113,11 @@ func rootCmdRun(cmd *cobra.Command, _ []string) {
} }
if err := config.EnsurePterodactylUser(); err != nil { if err := config.EnsurePterodactylUser(); err != nil {
log.WithField("error", err).Fatal("failed to create pterodactyl system user") log.WithField("error", err).Fatal("failed to create pterodactyl system user")
return
}
if err := config.ConfigurePasswd(); err != nil {
log.WithField("error", err).Fatal("failed to configure container passwd file")
return
} }
log.WithFields(log.Fields{ log.WithFields(log.Fields{
"username": config.Get().System.Username, "username": config.Get().System.Username,
@ -123,9 +129,10 @@ func rootCmdRun(cmd *cobra.Command, _ []string) {
return return
} }
t := config.Get().Token
pclient := remote.New( pclient := remote.New(
config.Get().PanelLocation, config.Get().PanelLocation,
remote.WithCredentials(config.Get().AuthenticationTokenId, config.Get().AuthenticationToken), remote.WithCredentials(t.ID, t.Token),
remote.WithHttpClient(&http.Client{ remote.WithHttpClient(&http.Client{
Timeout: time.Second * time.Duration(config.Get().RemoteQuery.Timeout), Timeout: time.Second * time.Duration(config.Get().RemoteQuery.Timeout),
}), }),
@ -133,19 +140,26 @@ func rootCmdRun(cmd *cobra.Command, _ []string) {
if err := database.Initialize(); err != nil { if err := database.Initialize(); err != nil {
log.WithField("error", err).Fatal("failed to initialize database") log.WithField("error", err).Fatal("failed to initialize database")
return
} }
manager, err := server.NewManager(cmd.Context(), pclient) manager, err := server.NewManager(cmd.Context(), pclient)
if err != nil { if err != nil {
log.WithField("error", err).Fatal("failed to load server configurations") log.WithField("error", err).Fatal("failed to load server configurations")
return
} }
if err := environment.ConfigureDocker(cmd.Context()); err != nil { if err := environment.ConfigureDocker(cmd.Context()); err != nil {
log.WithField("error", err).Fatal("failed to configure docker environment") log.WithField("error", err).Fatal("failed to configure docker environment")
return
} }
if err := config.WriteToDisk(config.Get()); err != nil { if err := config.WriteToDisk(config.Get()); err != nil {
log.WithField("error", err).Fatal("failed to write configuration to disk") if !errors.Is(err, syscall.EROFS) {
log.WithField("error", err).Error("failed to write configuration to disk")
} else {
log.WithField("error", err).Debug("failed to write configuration to disk")
}
} }
// Just for some nice log output. // Just for some nice log output.
@ -379,13 +393,14 @@ func rootCmdRun(cmd *cobra.Command, _ []string) {
// Reads the configuration from the disk and then sets up the global singleton // Reads the configuration from the disk and then sets up the global singleton
// with all the configuration values. // with all the configuration values.
func initConfig() { func initConfig() {
if !strings.HasPrefix(configPath, "/") { if !filepath.IsAbs(configPath) {
d, err := os.Getwd() d, err := filepath.Abs(configPath)
if err != nil { if err != nil {
log2.Fatalf("cmd/root: could not determine directory: %s", err) log2.Fatalf("cmd/root: failed to get path to config file: %s", err)
} }
configPath = path.Clean(path.Join(d, configPath)) configPath = d
} }
err := config.FromFile(configPath) err := config.FromFile(configPath)
if err != nil { if err != nil {
if errors.Is(err, os.ErrNotExist) { if errors.Is(err, os.ErrNotExist) {
@ -440,18 +455,18 @@ in all copies or substantial portions of the Software.%s`), system.Version, time
} }
func exitWithConfigurationNotice() { func exitWithConfigurationNotice() {
fmt.Print(colorstring.Color(` fmt.Printf(colorstring.Color(`
[_red_][white][bold]Error: Configuration File Not Found[reset] [_red_][white][bold]Error: Configuration File Not Found[reset]
Wings was not able to locate your configuration file, and therefore is not Wings was not able to locate your configuration file, and therefore is not
able to complete its boot process. Please ensure you have copied your instance able to complete its boot process. Please ensure you have copied your instance
configuration file into the default location below. configuration file into the default location below.
Default Location: /etc/pterodactyl/config.yml Default Location: %s
[yellow]This is not a bug with this software. Please do not make a bug report [yellow]This is not a bug with this software. Please do not make a bug report
for this issue, it will be closed.[reset] for this issue, it will be closed.[reset]
`)) `), config.DefaultLocation)
os.Exit(1) os.Exit(1)
} }

View File

@ -1,6 +1,7 @@
package config package config
import ( import (
"bytes"
"context" "context"
"crypto/tls" "crypto/tls"
"fmt" "fmt"
@ -172,6 +173,25 @@ type SystemConfiguration struct {
Gid int `yaml:"gid"` Gid int `yaml:"gid"`
} `yaml:"user"` } `yaml:"user"`
// Passwd controls the mounting of a generated passwd files into containers started by Wings.
Passwd struct {
// Enable controls whether generated passwd files should be mounted into containers.
//
// By default this option is disabled and Wings will not mount any additional passwd
// files into containers.
Enable bool `yaml:"enabled" default:"false"`
// Directory is the directory on disk where the generated files will be stored.
// This directory may be temporary as it will be re-created whenever Wings is started.
//
// This path **WILL** be both written to by Wings and mounted into containers created by
// Wings. If you are running Wings itself in a container, this path will need to be mounted
// into the Wings container as the exact path on the host, which should match the value
// specified here. If you are using SELinux, you will need to make sure this file has the
// correct SELinux context in order for containers to use it.
Directory string `yaml:"directory" default:"/run/wings/etc"`
} `yaml:"passwd"`
// The amount of time in seconds that can elapse before a server's disk space calculation is // The amount of time in seconds that can elapse before a server's disk space calculation is
// considered stale and a re-check should occur. DANGER: setting this value too low can seriously // considered stale and a re-check should occur. DANGER: setting this value too low can seriously
// impact system performance and cause massive I/O bottlenecks and high CPU usage for the Wings // impact system performance and cause massive I/O bottlenecks and high CPU usage for the Wings
@ -275,7 +295,14 @@ type ConsoleThrottles struct {
Period uint64 `json:"line_reset_interval" yaml:"line_reset_interval" default:"100"` Period uint64 `json:"line_reset_interval" yaml:"line_reset_interval" default:"100"`
} }
type Token struct {
ID string
Token string
}
type Configuration struct { type Configuration struct {
Token Token `json:"-" yaml:"-"`
// The location from which this configuration instance was instantiated. // The location from which this configuration instance was instantiated.
path string path string
@ -348,11 +375,16 @@ func NewAtPath(path string) (*Configuration, error) {
// will be paused until it is complete. // will be paused until it is complete.
func Set(c *Configuration) { func Set(c *Configuration) {
mu.Lock() mu.Lock()
if _config == nil || _config.AuthenticationToken != c.AuthenticationToken { defer mu.Unlock()
_jwtAlgo = jwt.NewHS256([]byte(c.AuthenticationToken)) token := c.Token.Token
if token == "" {
c.Token.Token = c.AuthenticationToken
token = c.Token.Token
}
if _config == nil || _config.Token.Token != token {
_jwtAlgo = jwt.NewHS256([]byte(token))
} }
_config = c _config = c
mu.Unlock()
} }
// SetDebugViaFlag tracks if the application is running in debug mode because of // SetDebugViaFlag tracks if the application is running in debug mode because of
@ -360,9 +392,9 @@ func Set(c *Configuration) {
// change to the disk. // change to the disk.
func SetDebugViaFlag(d bool) { func SetDebugViaFlag(d bool) {
mu.Lock() mu.Lock()
defer mu.Unlock()
_config.Debug = d _config.Debug = d
_debugViaFlag = d _debugViaFlag = d
mu.Unlock()
} }
// Get returns the global configuration instance. This is a thread-safe operation // Get returns the global configuration instance. This is a thread-safe operation
@ -387,8 +419,8 @@ func Get() *Configuration {
// the global configuration. // the global configuration.
func Update(callback func(c *Configuration)) { func Update(callback func(c *Configuration)) {
mu.Lock() mu.Lock()
defer mu.Unlock()
callback(_config) callback(_config)
mu.Unlock()
} }
// GetJwtAlgorithm returns the in-memory JWT algorithm. // GetJwtAlgorithm returns the in-memory JWT algorithm.
@ -497,6 +529,37 @@ func EnsurePterodactylUser() error {
return nil return nil
} }
// ConfigurePasswd generates required passwd files for use with containers started by Wings.
func ConfigurePasswd() error {
passwd := _config.System.Passwd
if !passwd.Enable {
return nil
}
v := []byte(fmt.Sprintf(
`root:x:0:
container:x:%d:
nogroup:x:65534:`,
_config.System.User.Gid,
))
if err := os.WriteFile(filepath.Join(passwd.Directory, "group"), v, 0o644); err != nil {
return fmt.Errorf("failed to write file to %s/group: %v", passwd.Directory, err)
}
v = []byte(fmt.Sprintf(
`root:x:0:0::/root:/bin/sh
container:x:%d:%d::/home/container:/bin/sh
nobody:x:65534:65534::/var/empty:/bin/sh
`,
_config.System.User.Uid,
_config.System.User.Gid,
))
if err := os.WriteFile(filepath.Join(passwd.Directory, "passwd"), v, 0o644); err != nil {
return fmt.Errorf("failed to write file to %s/passwd: %v", passwd.Directory, err)
}
return nil
}
// FromFile reads the configuration from the provided file and stores it in the // FromFile reads the configuration from the provided file and stores it in the
// global singleton for this instance. // global singleton for this instance.
func FromFile(path string) error { func FromFile(path string) error {
@ -513,6 +576,26 @@ func FromFile(path string) error {
return err return err
} }
c.Token = Token{
ID: os.Getenv("WINGS_TOKEN_ID"),
Token: os.Getenv("WINGS_TOKEN"),
}
if c.Token.ID == "" {
c.Token.ID = c.AuthenticationTokenId
}
if c.Token.Token == "" {
c.Token.Token = c.AuthenticationToken
}
c.Token.ID, err = Expand(c.Token.ID)
if err != nil {
return err
}
c.Token.Token, err = Expand(c.Token.Token)
if err != nil {
return err
}
// Store this configuration in the global state. // Store this configuration in the global state.
Set(c) Set(c)
return nil return nil
@ -561,6 +644,13 @@ func ConfigureDirectories() error {
return err return err
} }
if _config.System.Passwd.Enable {
log.WithField("path", _config.System.Passwd.Directory).Debug("ensuring passwd directory exists")
if err := os.MkdirAll(_config.System.Passwd.Directory, 0o755); err != nil {
return err
}
}
return nil return nil
} }
@ -708,3 +798,36 @@ func UseOpenat2() bool {
return true return true
} }
} }
// Expand expands an input string by calling [os.ExpandEnv] to expand all
// environment variables, then checks if the value is prefixed with `file://`
// to support reading the value from a file.
//
// NOTE: the order of expanding environment variables first then checking if
// the value references a file is important. This behaviour allows a user to
// pass a value like `file://${CREDENTIALS_DIRECTORY}/token` to allow us to
// work with credentials loaded by systemd's `LoadCredential` (or `LoadCredentialEncrypted`)
// options without the user needing to assume the path of `CREDENTIALS_DIRECTORY`
// or use a preStart script to read the files for us.
func Expand(v string) (string, error) {
// Expand environment variables within the string.
//
// NOTE: this may cause issues if the string contains `$` and doesn't intend
// on getting expanded, however we are using this for our tokens which are
// all alphanumeric characters only.
v = os.ExpandEnv(v)
// Handle files.
const filePrefix = "file://"
if strings.HasPrefix(v, filePrefix) {
p := v[len(filePrefix):]
b, err := os.ReadFile(p)
if err != nil {
return "", nil
}
v = string(bytes.TrimRight(bytes.TrimRight(b, "\r"), "\n"))
}
return v, nil
}

View File

@ -2,11 +2,11 @@ package config
import ( import (
"encoding/base64" "encoding/base64"
"encoding/json"
"sort" "sort"
"github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/registry" "github.com/docker/docker/api/types/registry"
"github.com/goccy/go-json"
) )
type dockerNetworkInterfaces struct { type dockerNetworkInterfaces struct {

View File

@ -7,7 +7,6 @@ import (
"emperror.dev/errors" "emperror.dev/errors"
"github.com/apex/log" "github.com/apex/log"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/network" "github.com/docker/docker/api/types/network"
"github.com/docker/docker/client" "github.com/docker/docker/client"
@ -39,7 +38,7 @@ func ConfigureDocker(ctx context.Context) error {
} }
nw := config.Get().Docker.Network nw := config.Get().Docker.Network
resource, err := cli.NetworkInspect(ctx, nw.Name, types.NetworkInspectOptions{}) resource, err := cli.NetworkInspect(ctx, nw.Name, network.InspectOptions{})
if err != nil { if err != nil {
if !client.IsErrNotFound(err) { if !client.IsErrNotFound(err) {
return err return err
@ -72,9 +71,10 @@ func ConfigureDocker(ctx context.Context) error {
// Creates a new network on the machine if one does not exist already. // Creates a new network on the machine if one does not exist already.
func createDockerNetwork(ctx context.Context, cli *client.Client) error { func createDockerNetwork(ctx context.Context, cli *client.Client) error {
nw := config.Get().Docker.Network nw := config.Get().Docker.Network
_, err := cli.NetworkCreate(ctx, nw.Name, types.NetworkCreate{ enableIPv6 := true
_, err := cli.NetworkCreate(ctx, nw.Name, network.CreateOptions{
Driver: nw.Driver, Driver: nw.Driver,
EnableIPv6: true, EnableIPv6: &enableIPv6,
Internal: nw.IsInternal, Internal: nw.IsInternal,
IPAM: &network.IPAM{ IPAM: &network.IPAM{
Config: []network.IPAMConfig{{ Config: []network.IPAMConfig{{

View File

@ -2,6 +2,7 @@ package docker
import ( import (
"context" "context"
"encoding/json"
"io" "io"
"net/http" "net/http"
"reflect" "reflect"
@ -13,7 +14,6 @@ import (
"github.com/docker/docker/api/types/versions" "github.com/docker/docker/api/types/versions"
"github.com/docker/docker/client" "github.com/docker/docker/client"
"github.com/docker/docker/errdefs" "github.com/docker/docker/errdefs"
"github.com/goccy/go-json"
"github.com/pterodactyl/wings/config" "github.com/pterodactyl/wings/config"
) )

View File

@ -12,9 +12,10 @@ import (
"emperror.dev/errors" "emperror.dev/errors"
"github.com/apex/log" "github.com/apex/log"
"github.com/buger/jsonparser" "github.com/buger/jsonparser"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/image"
"github.com/docker/docker/api/types/mount" "github.com/docker/docker/api/types/mount"
"github.com/docker/docker/api/types/network"
"github.com/docker/docker/client" "github.com/docker/docker/client"
"github.com/pterodactyl/wings/config" "github.com/pterodactyl/wings/config"
@ -49,7 +50,7 @@ func (e *Environment) Attach(ctx context.Context) error {
return nil return nil
} }
opts := types.ContainerAttachOptions{ opts := container.AttachOptions{
Stdin: true, Stdin: true,
Stdout: true, Stdout: true,
Stderr: true, Stderr: true,
@ -103,7 +104,7 @@ func (e *Environment) Attach(ctx context.Context) error {
// container. This allows memory, cpu, and IO limitations to be adjusted on the // container. This allows memory, cpu, and IO limitations to be adjusted on the
// fly for individual instances. // fly for individual instances.
func (e *Environment) InSituUpdate() error { func (e *Environment) InSituUpdate() error {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*10) ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel() defer cancel()
if _, err := e.ContainerInspect(ctx); err != nil { if _, err := e.ContainerInspect(ctx); err != nil {
@ -199,14 +200,15 @@ func (e *Environment) Create() error {
networkName := "ip-" + strings.ReplaceAll(strings.ReplaceAll(a.DefaultMapping.Ip, ".", "-"), ":", "-") networkName := "ip-" + strings.ReplaceAll(strings.ReplaceAll(a.DefaultMapping.Ip, ".", "-"), ":", "-")
networkMode = container.NetworkMode(networkName) networkMode = container.NetworkMode(networkName)
if _, err := e.client.NetworkInspect(ctx, networkName, types.NetworkInspectOptions{}); err != nil { if _, err := e.client.NetworkInspect(ctx, networkName, network.InspectOptions{}); err != nil {
if !client.IsErrNotFound(err) { if !client.IsErrNotFound(err) {
return err return err
} }
if _, err := e.client.NetworkCreate(ctx, networkName, types.NetworkCreate{ enableIPv6 := false
if _, err := e.client.NetworkCreate(ctx, networkName, network.CreateOptions{
Driver: "bridge", Driver: "bridge",
EnableIPv6: false, EnableIPv6: &enableIPv6,
Internal: false, Internal: false,
Attachable: false, Attachable: false,
Ingress: false, Ingress: false,
@ -270,7 +272,7 @@ func (e *Environment) Destroy() error {
// We set it to stopping than offline to prevent crash detection from being triggered. // We set it to stopping than offline to prevent crash detection from being triggered.
e.SetState(environment.ProcessStoppingState) e.SetState(environment.ProcessStoppingState)
err := e.client.ContainerRemove(context.Background(), e.Id, types.ContainerRemoveOptions{ err := e.client.ContainerRemove(context.Background(), e.Id, container.RemoveOptions{
RemoveVolumes: true, RemoveVolumes: true,
RemoveLinks: false, RemoveLinks: false,
Force: true, Force: true,
@ -316,7 +318,7 @@ func (e *Environment) SendCommand(c string) error {
// is running or not, it will simply try to read the last X bytes of the file // is running or not, it will simply try to read the last X bytes of the file
// and return them. // and return them.
func (e *Environment) Readlog(lines int) ([]string, error) { func (e *Environment) Readlog(lines int) ([]string, error) {
r, err := e.client.ContainerLogs(context.Background(), e.Id, types.ContainerLogsOptions{ r, err := e.client.ContainerLogs(context.Background(), e.Id, container.LogsOptions{
ShowStdout: true, ShowStdout: true,
ShowStderr: true, ShowStderr: true,
Tail: strconv.Itoa(lines), Tail: strconv.Itoa(lines),
@ -343,25 +345,25 @@ func (e *Environment) Readlog(lines int) ([]string, error) {
// late, and we don't need to block all the servers from booting just because // late, and we don't need to block all the servers from booting just because
// of that. I'd imagine in a lot of cases an outage shouldn't affect users too // of that. I'd imagine in a lot of cases an outage shouldn't affect users too
// badly. It'll at least keep existing servers working correctly if anything. // badly. It'll at least keep existing servers working correctly if anything.
func (e *Environment) ensureImageExists(image string) error { func (e *Environment) ensureImageExists(img string) error {
e.Events().Publish(environment.DockerImagePullStarted, "") e.Events().Publish(environment.DockerImagePullStarted, "")
defer e.Events().Publish(environment.DockerImagePullCompleted, "") defer e.Events().Publish(environment.DockerImagePullCompleted, "")
// Images prefixed with a ~ are local images that we do not need to try and pull. // Images prefixed with a ~ are local images that we do not need to try and pull.
if strings.HasPrefix(image, "~") { if strings.HasPrefix(img, "~") {
return nil return nil
} }
// Give it up to 15 minutes to pull the image. I think this should cover 99.8% of cases where an // Give it up to 15 minutes to pull the image. I think this should cover 99.8% of cases where an
// image pull might fail. I can't imagine it will ever take more than 15 minutes to fully pull // image pull might fail. I can't imagine it will ever take more than 15 minutes to fully pull
// an image. Let me know when I am inevitably wrong here... // an image. Let me know when I am inevitably wrong here...
ctx, cancel := context.WithTimeout(context.Background(), time.Minute*15) ctx, cancel := context.WithTimeout(context.Background(), 15*time.Minute)
defer cancel() defer cancel()
// Get a registry auth configuration from the config. // Get a registry auth configuration from the config.
var registryAuth *config.RegistryConfiguration var registryAuth *config.RegistryConfiguration
for registry, c := range config.Get().Docker.Registries { for registry, c := range config.Get().Docker.Registries {
if !strings.HasPrefix(image, registry) { if !strings.HasPrefix(img, registry) {
continue continue
} }
@ -371,7 +373,7 @@ func (e *Environment) ensureImageExists(image string) error {
} }
// Get the ImagePullOptions. // Get the ImagePullOptions.
imagePullOptions := types.ImagePullOptions{All: false} imagePullOptions := image.PullOptions{All: false}
if registryAuth != nil { if registryAuth != nil {
b64, err := registryAuth.Base64() b64, err := registryAuth.Base64()
if err != nil { if err != nil {
@ -382,23 +384,23 @@ func (e *Environment) ensureImageExists(image string) error {
imagePullOptions.RegistryAuth = b64 imagePullOptions.RegistryAuth = b64
} }
out, err := e.client.ImagePull(ctx, image, imagePullOptions) out, err := e.client.ImagePull(ctx, img, imagePullOptions)
if err != nil { if err != nil {
images, ierr := e.client.ImageList(ctx, types.ImageListOptions{}) images, ierr := e.client.ImageList(ctx, image.ListOptions{})
if ierr != nil { if ierr != nil {
// Well damn, something has gone really wrong here, just go ahead and abort there // Well damn, something has gone really wrong here, just go ahead and abort there
// isn't much anything we can do to try and self-recover from this. // isn't much anything we can do to try and self-recover from this.
return errors.Wrap(ierr, "environment/docker: failed to list images") return errors.Wrap(ierr, "environment/docker: failed to list images")
} }
for _, img := range images { for _, img2 := range images {
for _, t := range img.RepoTags { for _, t := range img2.RepoTags {
if t != image { if t != img {
continue continue
} }
log.WithFields(log.Fields{ log.WithFields(log.Fields{
"image": image, "image": img,
"container_id": e.Id, "container_id": e.Id,
"err": err.Error(), "err": err.Error(),
}).Warn("unable to pull requested image from remote source, however the image exists locally") }).Warn("unable to pull requested image from remote source, however the image exists locally")
@ -409,11 +411,11 @@ func (e *Environment) ensureImageExists(image string) error {
} }
} }
return errors.Wrapf(err, "environment/docker: failed to pull \"%s\" image for server", image) return errors.Wrapf(err, "environment/docker: failed to pull \"%s\" image for server", img)
} }
defer out.Close() defer out.Close()
log.WithField("image", image).Debug("pulling docker image... this could take a bit of time") log.WithField("image", img).Debug("pulling docker image... this could take a bit of time")
// I'm not sure what the best approach here is, but this will block execution until the image // I'm not sure what the best approach here is, but this will block execution until the image
// is done being pulled, which is what we need. // is done being pulled, which is what we need.
@ -431,22 +433,21 @@ func (e *Environment) ensureImageExists(image string) error {
return err return err
} }
log.WithField("image", image).Debug("completed docker image pull") log.WithField("image", img).Debug("completed docker image pull")
return nil return nil
} }
func (e *Environment) convertMounts() []mount.Mount { func (e *Environment) convertMounts() []mount.Mount {
var out []mount.Mount mounts := e.Configuration.Mounts()
out := make([]mount.Mount, len(mounts))
for _, m := range e.Configuration.Mounts() { for i, m := range mounts {
out = append(out, mount.Mount{ out[i] = mount.Mount{
Type: mount.TypeBind, Type: mount.TypeBind,
Source: m.Source, Source: m.Source,
Target: m.Target, Target: m.Target,
ReadOnly: m.ReadOnly, ReadOnly: m.ReadOnly,
}) }
} }
return out return out
} }

View File

@ -4,12 +4,10 @@ import (
"context" "context"
"os" "os"
"strings" "strings"
"syscall"
"time" "time"
"emperror.dev/errors" "emperror.dev/errors"
"github.com/apex/log" "github.com/apex/log"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/container"
"github.com/docker/docker/client" "github.com/docker/docker/client"
@ -27,7 +25,7 @@ import (
// is running does not result in the server becoming un-bootable. // is running does not result in the server becoming un-bootable.
func (e *Environment) OnBeforeStart(ctx context.Context) error { func (e *Environment) OnBeforeStart(ctx context.Context) error {
// Always destroy and re-create the server container to ensure that synced data from the Panel is used. // Always destroy and re-create the server container to ensure that synced data from the Panel is used.
if err := e.client.ContainerRemove(ctx, e.Id, types.ContainerRemoveOptions{RemoveVolumes: true}); err != nil { if err := e.client.ContainerRemove(ctx, e.Id, container.RemoveOptions{RemoveVolumes: true}); err != nil {
if !client.IsErrNotFound(err) { if !client.IsErrNotFound(err) {
return errors.WrapIf(err, "environment/docker: failed to remove container during pre-boot") return errors.WrapIf(err, "environment/docker: failed to remove container during pre-boot")
} }
@ -122,7 +120,7 @@ func (e *Environment) Start(ctx context.Context) error {
return errors.WrapIf(err, "environment/docker: failed to attach to container") return errors.WrapIf(err, "environment/docker: failed to attach to container")
} }
if err := e.client.ContainerStart(actx, e.Id, types.ContainerStartOptions{}); err != nil { if err := e.client.ContainerStart(actx, e.Id, container.StartOptions{}); err != nil {
return errors.WrapIf(err, "environment/docker: failed to start container") return errors.WrapIf(err, "environment/docker: failed to start container")
} }
@ -143,42 +141,49 @@ func (e *Environment) Stop(ctx context.Context) error {
s := e.meta.Stop s := e.meta.Stop
e.mu.RUnlock() e.mu.RUnlock()
// A native "stop" as the Type field value will just skip over all of this
// logic and end up only executing the container stop command (which may or
// may not work as expected).
if s.Type == "" || s.Type == remote.ProcessStopSignal {
if s.Type == "" {
log.WithField("container_id", e.Id).Warn("no stop configuration detected for environment, using termination procedure")
}
signal := os.Kill
// Handle a few common cases, otherwise just fall through and just pass along
// the os.Kill signal to the process.
switch strings.ToUpper(s.Value) {
case "SIGABRT":
signal = syscall.SIGABRT
case "SIGINT":
signal = syscall.SIGINT
case "SIGTERM":
signal = syscall.SIGTERM
}
return e.Terminate(ctx, signal)
}
// If the process is already offline don't switch it back to stopping. Just leave it how // If the process is already offline don't switch it back to stopping. Just leave it how
// it is and continue through to the stop handling for the process. // it is and continue through to the stop handling for the process.
if e.st.Load() != environment.ProcessOfflineState { if e.st.Load() != environment.ProcessOfflineState {
e.SetState(environment.ProcessStoppingState) e.SetState(environment.ProcessStoppingState)
} }
// Handle signal based actions
if s.Type == remote.ProcessStopSignal {
log.WithField("signal_value", s.Value).Debug("stopping server using signal")
// Handle some common signals - Default to SIGKILL
signal := "SIGKILL"
switch strings.ToUpper(s.Value) {
case "SIGABRT":
signal = "SIGABRT"
case "SIGINT", "C":
signal = "SIGINT"
case "SIGTERM":
signal = "SIGTERM"
case "SIGKILL":
signal = "SIGKILL"
default:
log.Info("Unrecognised signal requested, defaulting to SIGKILL")
}
return e.SignalContainer(ctx, signal)
}
// Handle command based stops
// Only attempt to send the stop command to the instance if we are actually attached to // Only attempt to send the stop command to the instance if we are actually attached to
// the instance. If we are not for some reason, just send the container stop event. // the instance. If we are not for some reason, just send the container stop event.
if e.IsAttached() && s.Type == remote.ProcessStopCommand { if e.IsAttached() && s.Type == remote.ProcessStopCommand {
return e.SendCommand(s.Value) return e.SendCommand(s.Value)
} }
// Allow the stop action to run for however long it takes, similar to executing a command if s.Type == "" {
// and using a different logic pathway to wait for the container to stop successfully. log.WithField("container_id", e.Id).Warn("no stop configuration detected for environment, using native docker stop")
}
// Fallback to a native docker stop. As we aren't passing a signal to ContainerStop docker will
// attempt to stop the container using the default stop signal, SIGTERM, unless
// another signal was specified in the Dockerfile
// //
// Using a negative timeout here will allow the container to stop gracefully, // Using a negative timeout here will allow the container to stop gracefully,
// rather than forcefully terminating it. Value is in seconds, but -1 is // rather than forcefully terminating it. Value is in seconds, but -1 is
@ -224,7 +229,7 @@ func (e *Environment) WaitForStop(ctx context.Context, duration time.Duration, t
doTermination := func(s string) error { doTermination := func(s string) error {
e.log().WithField("step", s).WithField("duration", duration).Warn("container stop did not complete in time, terminating process...") e.log().WithField("step", s).WithField("duration", duration).Warn("container stop did not complete in time, terminating process...")
return e.Terminate(ctx, os.Kill) return e.Terminate(ctx, "SIGKILL")
} }
// We pass through the timed context for this stop action so that if one of the // We pass through the timed context for this stop action so that if one of the
@ -268,8 +273,8 @@ func (e *Environment) WaitForStop(ctx context.Context, duration time.Duration, t
return nil return nil
} }
// Terminate forcefully terminates the container using the signal provided. // Sends the specified signal to the container in an attempt to stop it.
func (e *Environment) Terminate(ctx context.Context, signal os.Signal) error { func (e *Environment) SignalContainer(ctx context.Context, signal string) error {
c, err := e.ContainerInspect(ctx) c, err := e.ContainerInspect(ctx)
if err != nil { if err != nil {
// Treat missing containers as an okay error state, means it is obviously // Treat missing containers as an okay error state, means it is obviously
@ -294,10 +299,23 @@ func (e *Environment) Terminate(ctx context.Context, signal os.Signal) error {
// We set it to stopping than offline to prevent crash detection from being triggered. // We set it to stopping than offline to prevent crash detection from being triggered.
e.SetState(environment.ProcessStoppingState) e.SetState(environment.ProcessStoppingState)
sig := strings.TrimSuffix(strings.TrimPrefix(signal.String(), "signal "), "ed") if err := e.client.ContainerKill(ctx, e.Id, signal); err != nil && !client.IsErrNotFound(err) {
if err := e.client.ContainerKill(ctx, e.Id, sig); err != nil && !client.IsErrNotFound(err) {
return errors.WithStack(err) return errors.WithStack(err)
} }
return nil
}
// Terminate forcefully terminates the container using the signal provided.
// then sets its state to stopped.
func (e *Environment) Terminate(ctx context.Context, signal string) error {
// Send the signal to the container to kill it
if err := e.SignalContainer(ctx, signal); err != nil {
return errors.WithStack(err)
}
// We expect Terminate to instantly kill the container
// so go ahead and mark it as dead and clean up
e.SetState(environment.ProcessOfflineState) e.SetState(environment.ProcessOfflineState)
return nil return nil

View File

@ -2,13 +2,13 @@ package docker
import ( import (
"context" "context"
"encoding/json"
"io" "io"
"math" "math"
"time" "time"
"emperror.dev/errors" "emperror.dev/errors"
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types/container"
"github.com/goccy/go-json"
"github.com/pterodactyl/wings/environment" "github.com/pterodactyl/wings/environment"
) )
@ -57,7 +57,7 @@ func (e *Environment) pollResources(ctx context.Context) error {
case <-ctx.Done(): case <-ctx.Done():
return ctx.Err() return ctx.Err()
default: default:
var v types.StatsJSON var v container.StatsResponse
if err := dec.Decode(&v); err != nil { if err := dec.Decode(&v); err != nil {
if err != io.EOF && !errors.Is(err, context.Canceled) { if err != io.EOF && !errors.Is(err, context.Canceled) {
e.log().WithField("error", err).Warn("error while processing Docker stats output for container") e.log().WithField("error", err).Warn("error while processing Docker stats output for container")
@ -95,7 +95,7 @@ func (e *Environment) pollResources(ctx context.Context) error {
} }
} }
// The "docker stats" CLI call does not return the same value as the types.MemoryStats.Usage // The "docker stats" CLI call does not return the same value as the [container.MemoryStats].Usage
// value which can be rather confusing to people trying to compare panel usage to // value which can be rather confusing to people trying to compare panel usage to
// their stats output. // their stats output.
// //
@ -103,7 +103,7 @@ func (e *Environment) pollResources(ctx context.Context) error {
// bothering me about it. It should also reflect a slightly more correct memory value anyways. // bothering me about it. It should also reflect a slightly more correct memory value anyways.
// //
// @see https://github.com/docker/cli/blob/96e1d1d6/cli/command/container/stats_helpers.go#L227-L249 // @see https://github.com/docker/cli/blob/96e1d1d6/cli/command/container/stats_helpers.go#L227-L249
func calculateDockerMemory(stats types.MemoryStats) uint64 { func calculateDockerMemory(stats container.MemoryStats) uint64 {
if v, ok := stats.Stats["total_inactive_file"]; ok && v < stats.Usage { if v, ok := stats.Stats["total_inactive_file"]; ok && v < stats.Usage {
return stats.Usage - v return stats.Usage - v
} }
@ -119,7 +119,7 @@ func calculateDockerMemory(stats types.MemoryStats) uint64 {
// by the defined CPU limits on the container. // by the defined CPU limits on the container.
// //
// @see https://github.com/docker/cli/blob/aa097cf1aa19099da70930460250797c8920b709/cli/command/container/stats_helpers.go#L166 // @see https://github.com/docker/cli/blob/aa097cf1aa19099da70930460250797c8920b709/cli/command/container/stats_helpers.go#L166
func calculateDockerAbsoluteCpu(pStats types.CPUStats, stats types.CPUStats) float64 { func calculateDockerAbsoluteCpu(pStats container.CPUStats, stats container.CPUStats) float64 {
// Calculate the change in CPU usage between the current and previous reading. // Calculate the change in CPU usage between the current and previous reading.
cpuDelta := float64(stats.CPUUsage.TotalUsage) - float64(pStats.CPUUsage.TotalUsage) cpuDelta := float64(stats.CPUUsage.TotalUsage) - float64(pStats.CPUUsage.TotalUsage)

View File

@ -2,7 +2,6 @@ package environment
import ( import (
"context" "context"
"os"
"time" "time"
"github.com/pterodactyl/wings/events" "github.com/pterodactyl/wings/events"
@ -72,7 +71,7 @@ type ProcessEnvironment interface {
// Terminate stops a running server instance using the provided signal. This function // Terminate stops a running server instance using the provided signal. This function
// is a no-op if the server is already stopped. // is a no-op if the server is already stopped.
Terminate(ctx context.Context, signal os.Signal) error Terminate(ctx context.Context, signal string) error
// Destroys the environment removing any containers that were created (in Docker // Destroys the environment removing any containers that were created (in Docker
// environments at least). // environments at least).

View File

@ -34,7 +34,7 @@ type Mount struct {
// Limits is the build settings for a given server that impact docker container // Limits is the build settings for a given server that impact docker container
// creation and resource limits for a server instance. // creation and resource limits for a server instance.
type Limits struct { type Limits struct {
// The total amount of memory in megabytes that this server is allowed to // The total amount of memory in mebibytes that this server is allowed to
// use on the host system. // use on the host system.
MemoryLimit int64 `json:"memory_limit"` MemoryLimit int64 `json:"memory_limit"`
@ -79,7 +79,7 @@ func (l Limits) MemoryOverheadMultiplier() float64 {
} }
func (l Limits) BoundedMemoryLimit() int64 { func (l Limits) BoundedMemoryLimit() int64 {
return int64(math.Round(float64(l.MemoryLimit) * l.MemoryOverheadMultiplier() * 1_000_000)) return int64(math.Round(float64(l.MemoryLimit) * l.MemoryOverheadMultiplier() * 1024 * 1024))
} }
// ConvertedSwap returns the amount of swap available as a total in bytes. This // ConvertedSwap returns the amount of swap available as a total in bytes. This
@ -90,7 +90,7 @@ func (l Limits) ConvertedSwap() int64 {
return -1 return -1
} }
return (l.Swap * 1_000_000) + l.BoundedMemoryLimit() return (l.Swap * 1024 * 1024) + l.BoundedMemoryLimit()
} }
// ProcessLimit returns the process limit for a container. This is currently // ProcessLimit returns the process limit for a container. This is currently
@ -105,7 +105,7 @@ func (l Limits) AsContainerResources() container.Resources {
pids := l.ProcessLimit() pids := l.ProcessLimit()
resources := container.Resources{ resources := container.Resources{
Memory: l.BoundedMemoryLimit(), Memory: l.BoundedMemoryLimit(),
MemoryReservation: l.MemoryLimit * 1_000_000, MemoryReservation: l.MemoryLimit * 1024 * 1024,
MemorySwap: l.ConvertedSwap(), MemorySwap: l.ConvertedSwap(),
BlkioWeight: l.IoWeight, BlkioWeight: l.IoWeight,
OomKillDisable: &l.OOMDisabled, OomKillDisable: &l.OOMDisabled,

View File

@ -1,10 +1,10 @@
package events package events
import ( import (
"encoding/json"
"strings" "strings"
"emperror.dev/errors" "emperror.dev/errors"
"github.com/goccy/go-json"
"github.com/pterodactyl/wings/system" "github.com/pterodactyl/wings/system"
) )

35
flake.lock generated
View File

@ -5,11 +5,11 @@
"nixpkgs-lib": "nixpkgs-lib" "nixpkgs-lib": "nixpkgs-lib"
}, },
"locked": { "locked": {
"lastModified": 1706830856, "lastModified": 1741352980,
"narHash": "sha256-a0NYyp+h9hlb7ddVz4LUn1vT/PLwqfrWYcHMvFB1xYg=", "narHash": "sha256-+u2UunDA4Cl5Fci3m7S643HzKmIDAe+fiXrLqYsR2fs=",
"owner": "hercules-ci", "owner": "hercules-ci",
"repo": "flake-parts", "repo": "flake-parts",
"rev": "b253292d9c0a5ead9bc98c4e9a26c6312e27d69f", "rev": "f4330d22f1c5d2ba72d3d22df5597d123fdb60a9",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -20,11 +20,11 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1707956935, "lastModified": 1741379970,
"narHash": "sha256-ZL2TrjVsiFNKOYwYQozpbvQSwvtV/3Me7Zwhmdsfyu4=", "narHash": "sha256-Wh7esNh7G24qYleLvgOSY/7HlDUzWaL/n4qzlBePpiw=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "a4d4fe8c5002202493e87ec8dbc91335ff55552c", "rev": "36fd87baa9083f34f7f5027900b62ee6d09b1f2f",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -36,19 +36,16 @@
}, },
"nixpkgs-lib": { "nixpkgs-lib": {
"locked": { "locked": {
"dir": "lib", "lastModified": 1740877520,
"lastModified": 1706550542, "narHash": "sha256-oiwv/ZK/2FhGxrCkQkB83i7GnWXPPLzoqFHpDD3uYpk=",
"narHash": "sha256-UcsnCG6wx++23yeER4Hg18CXWbgNpqNXcHIo5/1Y+hc=", "owner": "nix-community",
"owner": "NixOS", "repo": "nixpkgs.lib",
"repo": "nixpkgs", "rev": "147dee35aab2193b174e4c0868bd80ead5ce755c",
"rev": "97b17f32362e475016f942bbdfda4a4a72a8a652",
"type": "github" "type": "github"
}, },
"original": { "original": {
"dir": "lib", "owner": "nix-community",
"owner": "NixOS", "repo": "nixpkgs.lib",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github" "type": "github"
} }
}, },
@ -66,11 +63,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1707300477, "lastModified": 1739829690,
"narHash": "sha256-qQF0fEkHlnxHcrKIMRzOETnRBksUK048MXkX0SOmxvA=", "narHash": "sha256-mL1szCeIsjh6Khn3nH2cYtwO5YXG6gBiTw1A30iGeDU=",
"owner": "numtide", "owner": "numtide",
"repo": "treefmt-nix", "repo": "treefmt-nix",
"rev": "ac599dab59a66304eb511af07b3883114f061b9d", "rev": "3d0579f5cc93436052d94b73925b48973a104204",
"type": "github" "type": "github"
}, },
"original": { "original": {

View File

@ -13,7 +13,7 @@
outputs = {...} @ inputs: outputs = {...} @ inputs:
inputs.flake-parts.lib.mkFlake {inherit inputs;} { inputs.flake-parts.lib.mkFlake {inherit inputs;} {
systems = ["aarch64-darwin" "aarch64-linux" "x86_64-darwin" "x86_64-linux"]; systems = inputs.nixpkgs.lib.systems.flakeExposed;
imports = [ imports = [
inputs.treefmt-nix.flakeModule inputs.treefmt-nix.flakeModule
@ -24,10 +24,10 @@
in { in {
devShells.default = pkgs.mkShell { devShells.default = pkgs.mkShell {
buildInputs = with pkgs; [ buildInputs = with pkgs; [
go_1_22 go_1_24
gofumpt gofumpt
golangci-lint golangci-lint
gotools gotools
]; ];
}; };

123
go.mod
View File

@ -1,6 +1,8 @@
module github.com/pterodactyl/wings module github.com/pterodactyl/wings
go 1.21 go 1.23.0
toolchain go1.24.1
require ( require (
emperror.dev/errors v0.8.1 emperror.dev/errors v0.8.1
@ -10,73 +12,75 @@ require (
github.com/acobaugh/osrelease v0.1.0 github.com/acobaugh/osrelease v0.1.0
github.com/apex/log v1.9.0 github.com/apex/log v1.9.0
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2
github.com/beevik/etree v1.3.0 github.com/beevik/etree v1.5.0
github.com/buger/jsonparser v1.1.1 github.com/buger/jsonparser v1.1.1
github.com/cenkalti/backoff/v4 v4.3.0 github.com/cenkalti/backoff/v4 v4.3.0
github.com/creasty/defaults v1.7.0 github.com/creasty/defaults v1.8.0
github.com/docker/docker v25.0.4+incompatible github.com/docker/docker v28.3.3+incompatible
github.com/docker/go-connections v0.5.0 github.com/docker/go-connections v0.5.0
github.com/fatih/color v1.16.0 github.com/fatih/color v1.18.0
github.com/franela/goblin v0.0.0-20211003143422-0a4f594942bf github.com/franela/goblin v0.0.0-20211003143422-0a4f594942bf
github.com/gabriel-vasile/mimetype v1.4.3 github.com/gabriel-vasile/mimetype v1.4.8
github.com/gammazero/workerpool v1.1.3 github.com/gammazero/workerpool v1.1.3
github.com/gbrlsnchs/jwt/v3 v3.0.1 github.com/gbrlsnchs/jwt/v3 v3.0.1
github.com/gin-gonic/gin v1.9.1 github.com/gin-gonic/gin v1.10.1
github.com/glebarez/sqlite v1.11.0 github.com/glebarez/sqlite v1.11.0
github.com/go-co-op/gocron v1.37.0 github.com/go-co-op/gocron v1.37.0
github.com/goccy/go-json v0.10.2
github.com/google/uuid v1.6.0 github.com/google/uuid v1.6.0
github.com/gorilla/websocket v1.5.1 github.com/gorilla/websocket v1.5.3
github.com/iancoleman/strcase v0.3.0 github.com/iancoleman/strcase v0.3.0
github.com/icza/dyno v0.0.0-20230330125955-09f820a8d9c0 github.com/icza/dyno v0.0.0-20230330125955-09f820a8d9c0
github.com/juju/ratelimit v1.0.2 github.com/juju/ratelimit v1.0.2
github.com/klauspost/compress v1.17.8 github.com/klauspost/compress v1.18.0
github.com/klauspost/pgzip v1.2.6 github.com/klauspost/pgzip v1.2.6
github.com/magiconair/properties v1.8.7 github.com/magiconair/properties v1.8.9
github.com/mattn/go-colorable v0.1.13 github.com/mattn/go-colorable v0.1.14
github.com/mholt/archiver/v4 v4.0.0-alpha.8 github.com/mholt/archives v0.1.3
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db
github.com/patrickmn/go-cache v2.1.0+incompatible github.com/patrickmn/go-cache v2.1.0+incompatible
github.com/pkg/sftp v1.13.6 github.com/pkg/sftp v1.13.9
github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06 github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06
github.com/spf13/cobra v1.8.0 github.com/spf13/cobra v1.9.1
github.com/stretchr/testify v1.9.0 github.com/stretchr/testify v1.10.0
golang.org/x/crypto v0.22.0 golang.org/x/crypto v0.41.0
golang.org/x/sync v0.7.0 golang.org/x/sync v0.16.0
golang.org/x/sys v0.19.0 golang.org/x/sys v0.35.0
gopkg.in/ini.v1 v1.67.0 gopkg.in/ini.v1 v1.67.0
gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v2 v2.4.0
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1
gorm.io/gorm v1.25.9 gorm.io/gorm v1.26.0
) )
require ( require (
github.com/Microsoft/go-winio v0.6.1 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/Microsoft/hcsshim v0.12.2 // indirect github.com/Microsoft/hcsshim v0.12.9 // indirect
github.com/andybalholm/brotli v1.1.0 // indirect github.com/STARRY-S/zip v0.2.2 // indirect
github.com/andybalholm/brotli v1.1.2-0.20250424173009-453214e765f3 // indirect
github.com/bodgit/plumbing v1.3.0 // indirect github.com/bodgit/plumbing v1.3.0 // indirect
github.com/bodgit/sevenzip v1.5.1 // indirect github.com/bodgit/sevenzip v1.6.0 // indirect
github.com/bodgit/windows v1.0.1 // indirect github.com/bodgit/windows v1.0.1 // indirect
github.com/bytedance/sonic v1.11.3 // indirect github.com/bytedance/sonic v1.13.1 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect github.com/bytedance/sonic/loader v0.2.4 // indirect
github.com/chenzhuoyu/iasm v0.9.1 // indirect github.com/cloudwego/base64x v0.1.5 // indirect
github.com/containerd/errdefs v0.3.0 // indirect
github.com/containerd/errdefs/pkg v0.3.0 // indirect
github.com/containerd/log v0.1.0 // indirect github.com/containerd/log v0.1.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect
github.com/distribution/reference v0.6.0 // indirect github.com/distribution/reference v0.6.0 // indirect
github.com/docker/go-units v0.5.0 // indirect github.com/docker/go-units v0.5.0 // indirect
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect github.com/dustin/go-humanize v1.0.1 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/gammazero/deque v0.2.1 // indirect github.com/gammazero/deque v1.0.0 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect github.com/gin-contrib/sse v1.0.0 // indirect
github.com/glebarez/go-sqlite v1.22.0 // indirect github.com/glebarez/go-sqlite v1.22.0 // indirect
github.com/go-logr/logr v1.4.1 // indirect github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.19.0 // indirect github.com/go-playground/validator/v10 v10.25.0 // indirect
github.com/goccy/go-json v0.10.5 // indirect
github.com/gogo/protobuf v1.3.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
@ -85,53 +89,56 @@ require (
github.com/jinzhu/now v1.1.5 // indirect github.com/jinzhu/now v1.1.5 // indirect
github.com/json-iterator/go v1.1.12 // indirect github.com/json-iterator/go v1.1.12 // indirect
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
github.com/klauspost/cpuid/v2 v2.2.7 // indirect github.com/klauspost/cpuid/v2 v2.2.10 // indirect
github.com/kr/fs v0.1.0 // indirect github.com/kr/fs v0.1.0 // indirect
github.com/leodido/go-urn v1.4.0 // indirect github.com/leodido/go-urn v1.4.0 // indirect
github.com/magefile/mage v1.15.0 // indirect github.com/magefile/mage v1.15.0 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
github.com/mikelolasagasti/xz v1.0.1 // indirect
github.com/minio/minlz v1.0.0 // indirect
github.com/moby/docker-image-spec v1.3.1 // indirect
github.com/moby/sys/atomicwriter v0.1.0 // indirect
github.com/moby/term v0.0.0-20220808134915-39b0c02b01ae // indirect github.com/moby/term v0.0.0-20220808134915-39b0c02b01ae // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/morikuni/aec v1.0.0 // indirect github.com/morikuni/aec v1.0.0 // indirect
github.com/ncruces/go-strftime v0.1.9 // indirect github.com/ncruces/go-strftime v0.1.9 // indirect
github.com/nwaples/rardecode/v2 v2.0.0-beta.2 // indirect github.com/nwaples/rardecode/v2 v2.1.1 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.0 // indirect github.com/opencontainers/image-spec v1.1.1 // indirect
github.com/pelletier/go-toml/v2 v2.2.0 // indirect github.com/pelletier/go-toml/v2 v2.2.3 // indirect
github.com/pierrec/lz4/v4 v4.1.21 // indirect github.com/pierrec/lz4/v4 v4.1.22 // indirect
github.com/pkg/errors v0.9.1 // indirect github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/robfig/cron/v3 v3.0.1 // indirect github.com/robfig/cron/v3 v3.0.1 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect github.com/sirupsen/logrus v1.9.3 // indirect
github.com/spf13/pflag v1.0.5 // indirect github.com/sorairolake/lzip-go v0.3.5 // indirect
github.com/therootcompany/xz v1.0.1 // indirect github.com/spf13/pflag v1.0.6 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.12 // indirect github.com/ugorji/go/codec v1.2.12 // indirect
github.com/ulikunitz/xz v0.5.12 // indirect github.com/ulikunitz/xz v0.5.14 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.50.0 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/otel v1.25.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect
go.opentelemetry.io/otel v1.35.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.24.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.24.0 // indirect
go.opentelemetry.io/otel/metric v1.25.0 // indirect go.opentelemetry.io/otel/metric v1.35.0 // indirect
go.opentelemetry.io/otel/sdk v1.24.0 // indirect go.opentelemetry.io/otel/trace v1.35.0 // indirect
go.opentelemetry.io/otel/trace v1.25.0 // indirect
go.uber.org/atomic v1.11.0 // indirect go.uber.org/atomic v1.11.0 // indirect
go.uber.org/multierr v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect
go4.org v0.0.0-20230225012048-214862532bf5 // indirect go4.org v0.0.0-20230225012048-214862532bf5 // indirect
golang.org/x/arch v0.7.0 // indirect golang.org/x/arch v0.15.0 // indirect
golang.org/x/mod v0.17.0 // indirect golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 // indirect
golang.org/x/net v0.24.0 // indirect golang.org/x/net v0.42.0 // indirect
golang.org/x/term v0.19.0 // indirect golang.org/x/term v0.34.0 // indirect
golang.org/x/text v0.14.0 // indirect golang.org/x/text v0.28.0 // indirect
golang.org/x/time v0.0.0-20220922220347-f3bd1da661af // indirect golang.org/x/time v0.0.0-20220922220347-f3bd1da661af // indirect
golang.org/x/tools v0.20.0 // indirect golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect google.golang.org/protobuf v1.36.5 // indirect
google.golang.org/protobuf v1.33.0 // indirect
gotest.tools/v3 v3.0.2 // indirect gotest.tools/v3 v3.0.2 // indirect
modernc.org/libc v1.49.3 // indirect modernc.org/libc v1.61.13 // indirect
modernc.org/mathutil v1.6.0 // indirect modernc.org/mathutil v1.7.1 // indirect
modernc.org/memory v1.8.0 // indirect modernc.org/memory v1.8.2 // indirect
modernc.org/sqlite v1.29.6 // indirect modernc.org/sqlite v1.36.0 // indirect
) )

348
go.sum
View File

@ -19,24 +19,27 @@ emperror.dev/errors v0.8.1 h1:UavXZ5cSX/4u9iyvH6aDcuGkVjeexUGJ7Ij7G4VfQT0=
emperror.dev/errors v0.8.1/go.mod h1:YcRvLPh626Ubn2xqtoprejnA5nFha+TJ+2vew48kWuE= emperror.dev/errors v0.8.1/go.mod h1:YcRvLPh626Ubn2xqtoprejnA5nFha+TJ+2vew48kWuE=
github.com/AlecAivazis/survey/v2 v2.3.7 h1:6I/u8FvytdGsgonrYsVn2t8t4QiRnh6QSTqkkhIiSjQ= github.com/AlecAivazis/survey/v2 v2.3.7 h1:6I/u8FvytdGsgonrYsVn2t8t4QiRnh6QSTqkkhIiSjQ=
github.com/AlecAivazis/survey/v2 v2.3.7/go.mod h1:xUTIdE4KCOIjsBAE1JYsUPoCqYdZ1reCfTwbto0Fduo= github.com/AlecAivazis/survey/v2 v2.3.7/go.mod h1:xUTIdE4KCOIjsBAE1JYsUPoCqYdZ1reCfTwbto0Fduo=
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
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/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/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/Jeffail/gabs/v2 v2.7.0 h1:Y2edYaTcE8ZpRsR2AtmPu5xQdFDIthFG0jYhu5PY8kg= github.com/Jeffail/gabs/v2 v2.7.0 h1:Y2edYaTcE8ZpRsR2AtmPu5xQdFDIthFG0jYhu5PY8kg=
github.com/Jeffail/gabs/v2 v2.7.0/go.mod h1:dp5ocw1FvBBQYssgHsG7I1WYsiLRtkUaB1FEtSwvNUw= github.com/Jeffail/gabs/v2 v2.7.0/go.mod h1:dp5ocw1FvBBQYssgHsG7I1WYsiLRtkUaB1FEtSwvNUw=
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/Microsoft/hcsshim v0.12.2 h1:AcXy+yfRvrx20g9v7qYaJv5Rh+8GaHOS6b8G6Wx/nKs= github.com/Microsoft/hcsshim v0.12.9 h1:2zJy5KA+l0loz1HzEGqyNnjd3fyZA31ZBCGKacp6lLg=
github.com/Microsoft/hcsshim v0.12.2/go.mod h1:RZV12pcHCXQ42XnlQ3pz6FZfmrC1C+R4gaOHhRNML1g= github.com/Microsoft/hcsshim v0.12.9/go.mod h1:fJ0gkFAna6ukt0bLdKB8djt4XIJhF/vEPuoIWYVvZ8Y=
github.com/NYTimes/logrotate v1.0.0 h1:6jFGbon6jOtpy3t3kwZZKS4Gdmf1C/Wv5J4ll4Xn5yk= github.com/NYTimes/logrotate v1.0.0 h1:6jFGbon6jOtpy3t3kwZZKS4Gdmf1C/Wv5J4ll4Xn5yk=
github.com/NYTimes/logrotate v1.0.0/go.mod h1:GxNz1cSw1c6t99PXoZlw+nm90H6cyQyrH66pjVv7x88= github.com/NYTimes/logrotate v1.0.0/go.mod h1:GxNz1cSw1c6t99PXoZlw+nm90H6cyQyrH66pjVv7x88=
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s= github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s=
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w= github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w=
github.com/STARRY-S/zip v0.2.2 h1:8QeCbIi1Z9U5MgoDARJR1ClbBo9RD46SmVy+dl0woCk=
github.com/STARRY-S/zip v0.2.2/go.mod h1:lqJ9JdeRipyOQJrYSOtpNAiaesFO6zVDsE8GIGFaoSk=
github.com/acobaugh/osrelease v0.1.0 h1:Yb59HQDGGNhCj4suHaFQQfBps5wyoKLSSX/J/+UifRE= github.com/acobaugh/osrelease v0.1.0 h1:Yb59HQDGGNhCj4suHaFQQfBps5wyoKLSSX/J/+UifRE=
github.com/acobaugh/osrelease v0.1.0/go.mod h1:4bFEs0MtgHNHBrmHCt67gNisnabCRAlzdVasCEGHTWY= github.com/acobaugh/osrelease v0.1.0/go.mod h1:4bFEs0MtgHNHBrmHCt67gNisnabCRAlzdVasCEGHTWY=
github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= github.com/andybalholm/brotli v1.1.2-0.20250424173009-453214e765f3 h1:8PmGpDEZl9yDpcdEr6Odf23feCxK3LNUNMxjXg41pZQ=
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= github.com/andybalholm/brotli v1.1.2-0.20250424173009-453214e765f3/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA=
github.com/apex/log v1.9.0 h1:FHtw/xuaM8AgmvDDTI9fiwoAL25Sq2cxojnZICUU8l0= github.com/apex/log v1.9.0 h1:FHtw/xuaM8AgmvDDTI9fiwoAL25Sq2cxojnZICUU8l0=
github.com/apex/log v1.9.0/go.mod h1:m82fZlWIuiWzWP04XCTXmnX0xRkYYbCdYn8jbJeLBEA= github.com/apex/log v1.9.0/go.mod h1:m82fZlWIuiWzWP04XCTXmnX0xRkYYbCdYn8jbJeLBEA=
github.com/apex/logs v1.0.0/go.mod h1:XzxuLZ5myVHDy9SAmYpamKKRNApGj54PfYLcFrXqDwo= github.com/apex/logs v1.0.0/go.mod h1:XzxuLZ5myVHDy9SAmYpamKKRNApGj54PfYLcFrXqDwo=
@ -46,81 +49,82 @@ github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3d
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
github.com/aws/aws-sdk-go v1.20.6/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.20.6/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U2H7sSsE2t6Kca0lfwTK8JdoNGS/yzM/4iH5I= github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U2H7sSsE2t6Kca0lfwTK8JdoNGS/yzM/4iH5I=
github.com/beevik/etree v1.3.0 h1:hQTc+pylzIKDb23yYprodCWWTt+ojFfUZyzU09a/hmU= github.com/beevik/etree v1.5.0 h1:iaQZFSDS+3kYZiGoc9uKeOkUY3nYMXOKLl6KIJxiJWs=
github.com/beevik/etree v1.3.0/go.mod h1:aiPf89g/1k3AShMVAzriilpcE4R/Vuor90y83zVZWFc= github.com/beevik/etree v1.5.0/go.mod h1:gPNJNaBGVZ9AwsidazFZyygnd+0pAU38N4D+WemwKNs=
github.com/bodgit/plumbing v1.3.0 h1:pf9Itz1JOQgn7vEOE7v7nlEfBykYqvUYioC61TwWCFU= github.com/bodgit/plumbing v1.3.0 h1:pf9Itz1JOQgn7vEOE7v7nlEfBykYqvUYioC61TwWCFU=
github.com/bodgit/plumbing v1.3.0/go.mod h1:JOTb4XiRu5xfnmdnDJo6GmSbSbtSyufrsyZFByMtKEs= github.com/bodgit/plumbing v1.3.0/go.mod h1:JOTb4XiRu5xfnmdnDJo6GmSbSbtSyufrsyZFByMtKEs=
github.com/bodgit/sevenzip v1.5.1 h1:rVj0baZsooZFy64DJN0zQogPzhPrT8BQ8TTRd1H4WHw= github.com/bodgit/sevenzip v1.6.0 h1:a4R0Wu6/P1o1pP/3VV++aEOcyeBxeO/xE2Y9NSTrr6A=
github.com/bodgit/sevenzip v1.5.1/go.mod h1:Q3YMySuVWq6pyGEolyIE98828lOfEoeWg5zeH6x22rc= github.com/bodgit/sevenzip v1.6.0/go.mod h1:zOBh9nJUof7tcrlqJFv1koWRrhz3LbDbUNngkuZxLMc=
github.com/bodgit/windows v1.0.1 h1:tF7K6KOluPYygXa3Z2594zxlkbKPAOvqr97etrGNIz4= github.com/bodgit/windows v1.0.1 h1:tF7K6KOluPYygXa3Z2594zxlkbKPAOvqr97etrGNIz4=
github.com/bodgit/windows v1.0.1/go.mod h1:a6JLwrB4KrTR5hBpp8FI9/9W9jJfeQ2h4XDXU74ZCdM= github.com/bodgit/windows v1.0.1/go.mod h1:a6JLwrB4KrTR5hBpp8FI9/9W9jJfeQ2h4XDXU74ZCdM=
github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs= github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs=
github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= github.com/bytedance/sonic v1.13.1 h1:Jyd5CIvdFnkOWuKXr+wm4Nyk2h0yAFsr8ucJgEasO3g=
github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM= github.com/bytedance/sonic v1.13.1/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4=
github.com/bytedance/sonic v1.11.3 h1:jRN+yEjakWh8aK5FzrciUHG8OFXK+4/KrAX/ysEtHAA= github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
github.com/bytedance/sonic v1.11.3/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4= github.com/bytedance/sonic/loader v0.2.4 h1:ZWCw4stuXUsn1/+zQDqeE7JKP+QO47tz7QCNan80NzY=
github.com/bytedance/sonic/loader v0.2.4/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0=
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpVsBuRksnlj1mLy4AWzRNQYxauNi62uWcE3to6eA=
github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog=
github.com/chenzhuoyu/iasm v0.9.1 h1:tUHQJXo3NhBqw6s33wkGn9SP3bvrWLdlVIJ3hQBL7P0=
github.com/chenzhuoyu/iasm v0.9.1/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4=
github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
github.com/containerd/errdefs v0.3.0 h1:FSZgGOeK4yuT/+DnF07/Olde/q4KBoMsaamhXxIMDp4=
github.com/containerd/errdefs v0.3.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M=
github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE=
github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk=
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= 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/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/creack/pty v1.1.17 h1:QeVUsEDNrLBW4tMgZHvxy18sKtr6VI492kBhUfhDJNI= github.com/creack/pty v1.1.17 h1:QeVUsEDNrLBW4tMgZHvxy18sKtr6VI492kBhUfhDJNI=
github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
github.com/creasty/defaults v1.7.0 h1:eNdqZvc5B509z18lD8yc212CAqJNvfT1Jq6L8WowdBA= github.com/creasty/defaults v1.8.0 h1:z27FJxCAa0JKt3utc0sCImAEb+spPucmKoOdLHvHYKk=
github.com/creasty/defaults v1.7.0/go.mod h1:iGzKe6pbEHnpMPtfDXZEr0NVxWnPTjb1bbDy08fPzYM= github.com/creasty/defaults v1.8.0/go.mod h1:iGzKe6pbEHnpMPtfDXZEr0NVxWnPTjb1bbDy08fPzYM=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= 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/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/docker/docker v25.0.4+incompatible h1:XITZTrq+52tZyZxUOtFIahUf3aH367FLxJzt9vZeAF8= github.com/docker/docker v28.3.3+incompatible h1:Dypm25kh4rmk49v1eiVbsAtpAsYURjYkaKubwuBdxEI=
github.com/docker/docker v25.0.4+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v28.3.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= 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-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 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 h1:iFaUwBSo5Svw6L7HYpRu/0lE3e0BaElwnNO1qkNQxBY= github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707 h1:2tV76y6Q9BB+NEBasnqvs7e49aEBFI8ejC89PSnWH+4=
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5/go.mod h1:qssHWj60/X5sZFNxpG4HBPDHVqxNm4DfnCKgrbZOT+s= github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707/go.mod h1:qssHWj60/X5sZFNxpG4HBPDHVqxNm4DfnCKgrbZOT+s=
github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY= github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
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.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/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/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/franela/goblin v0.0.0-20211003143422-0a4f594942bf h1:NrF81UtW8gG2LBGkXFQFqlfNnvMt9WdB46sfdJY4oqc= github.com/franela/goblin v0.0.0-20211003143422-0a4f594942bf h1:NrF81UtW8gG2LBGkXFQFqlfNnvMt9WdB46sfdJY4oqc=
github.com/franela/goblin v0.0.0-20211003143422-0a4f594942bf/go.mod h1:VzmDKDJVZI3aJmnRI9VjAn9nJ8qPPsN1fqzr9dqInIo= github.com/franela/goblin v0.0.0-20211003143422-0a4f594942bf/go.mod h1:VzmDKDJVZI3aJmnRI9VjAn9nJ8qPPsN1fqzr9dqInIo=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM=
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8=
github.com/gammazero/deque v0.2.1 h1:qSdsbG6pgp6nL7A0+K/B7s12mcCY/5l5SIUpMOl+dC0= github.com/gammazero/deque v1.0.0 h1:LTmimT8H7bXkkCy6gZX7zNLtkbz4NdS2z8LZuor3j34=
github.com/gammazero/deque v0.2.1/go.mod h1:LFroj8x4cMYCukHJDbxFCkT+r9AndaJnFMuZDV34tuU= github.com/gammazero/deque v1.0.0/go.mod h1:iflpYvtGfM3U8S8j+sZEKIak3SAKYpA5/SQewgfXDKo=
github.com/gammazero/workerpool v1.1.3 h1:WixN4xzukFoN0XSeXF6puqEqFTl2mECI9S6W44HWy9Q= github.com/gammazero/workerpool v1.1.3 h1:WixN4xzukFoN0XSeXF6puqEqFTl2mECI9S6W44HWy9Q=
github.com/gammazero/workerpool v1.1.3/go.mod h1:wPjyBLDbyKnUn2XwwyD3EEwo9dHutia9/fwNmSHWACc= github.com/gammazero/workerpool v1.1.3/go.mod h1:wPjyBLDbyKnUn2XwwyD3EEwo9dHutia9/fwNmSHWACc=
github.com/gbrlsnchs/jwt/v3 v3.0.1 h1:lbUmgAKpxnClrKloyIwpxm4OuWeDl5wLk52G91ODPw4= github.com/gbrlsnchs/jwt/v3 v3.0.1 h1:lbUmgAKpxnClrKloyIwpxm4OuWeDl5wLk52G91ODPw4=
github.com/gbrlsnchs/jwt/v3 v3.0.1/go.mod h1:AncDcjXz18xetI3A6STfXq2w+LuTx8pQ8bGEwRN8zVM= github.com/gbrlsnchs/jwt/v3 v3.0.1/go.mod h1:AncDcjXz18xetI3A6STfXq2w+LuTx8pQ8bGEwRN8zVM=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v1.0.0 h1:y3bT1mUWUxDpW4JLQg/HnTqV4rozuW4tC9eFKTxYI9E=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-contrib/sse v1.0.0/go.mod h1:zNuFdwarAygJBht0NTKiSi3jRf6RbqeILZ9Sp6Slhe0=
github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= github.com/gin-gonic/gin v1.10.1 h1:T0ujvqyCSqRopADpgPgiTT63DUQVSfojyME59Ei63pQ=
github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= github.com/gin-gonic/gin v1.10.1/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
github.com/glebarez/go-sqlite v1.22.0 h1:uAcMJhaA6r3LHMTFgP0SifzgXg46yJkgxqyuyec+ruQ= github.com/glebarez/go-sqlite v1.22.0 h1:uAcMJhaA6r3LHMTFgP0SifzgXg46yJkgxqyuyec+ruQ=
github.com/glebarez/go-sqlite v1.22.0/go.mod h1:PlBIdHe0+aUEFn+r2/uthrWq4FxbzugL0L8Li6yQJbc= github.com/glebarez/go-sqlite v1.22.0/go.mod h1:PlBIdHe0+aUEFn+r2/uthrWq4FxbzugL0L8Li6yQJbc=
github.com/glebarez/sqlite v1.11.0 h1:wSG0irqzP6VurnMEpFGer5Li19RpIRi2qvQz++w0GMw= github.com/glebarez/sqlite v1.11.0 h1:wSG0irqzP6VurnMEpFGer5Li19RpIRi2qvQz++w0GMw=
@ -131,8 +135,8 @@ github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9
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-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= 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.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
@ -141,10 +145,10 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.19.0 h1:ol+5Fu+cSq9JD7SoSqe04GMI92cbn0+wvQ3bZ8b/AU4= github.com/go-playground/validator/v10 v10.25.0 h1:5Dh7cjvzR7BRZadnsVOzPhWsrwUr0nmsZJxEAnFLNO8=
github.com/go-playground/validator/v10 v10.19.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= github.com/go-playground/validator/v10 v10.25.0/go.mod h1:GGzBIJMuE98Ic/kJsBXbz1x/7cByt++cQ+YOuDM5wus=
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
@ -159,10 +163,6 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y
github.com/golang/protobuf v1.3.1/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= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 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/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
@ -170,15 +170,16 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.4.0/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.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
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-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ= github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd h1:gbpYu9NMq8jhDVbvlGkMFWCjLFlqqEZjEmObmhUy6Vo=
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo= github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
@ -186,8 +187,8 @@ 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/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.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 h1:Wqo399gCIufwto+VfwCSvsnfGpF/w5E9CNxSwbpD6No= github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 h1:Wqo399gCIufwto+VfwCSvsnfGpF/w5E9CNxSwbpD6No=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0/go.mod h1:qmOFXW2epJhM0qSnUUYpldc7gVz2KMQwJ/QYCDIa7XU= github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0/go.mod h1:qmOFXW2epJhM0qSnUUYpldc7gVz2KMQwJ/QYCDIa7XU=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
@ -226,12 +227,12 @@ github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:C
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= 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/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU= github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE=
github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU= github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU=
github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
@ -241,8 +242,9 @@ github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFB
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
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/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
@ -252,24 +254,33 @@ github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjS
github.com/magefile/mage v1.9.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A= github.com/magefile/mage v1.9.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
github.com/magefile/mage v1.15.0 h1:BvGheCMAsG3bWUDbZ8AyXXpCNwU9u5CB6sM+HNb9HYg= github.com/magefile/mage v1.15.0 h1:BvGheCMAsG3bWUDbZ8AyXXpCNwU9u5CB6sM+HNb9HYg=
github.com/magefile/mage v1.15.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A= github.com/magefile/mage v1.15.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.9 h1:nWcCbLq1N2v/cpNsy5WvQ37Fb+YElfq20WJ/a8RkpQM=
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/magiconair/properties v1.8.9/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI= github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI=
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
github.com/mholt/archiver/v4 v4.0.0-alpha.8 h1:tRGQuDVPh66WCOelqe6LIGh0gwmfwxUrSSDunscGsRM= github.com/mholt/archives v0.1.3 h1:aEAaOtNra78G+TvV5ohmXrJOAzf++dIlYeDW3N9q458=
github.com/mholt/archiver/v4 v4.0.0-alpha.8/go.mod h1:5f7FUYGXdJWUjESffJaYR4R60VhnHxb2X3T1teMyv5A= github.com/mholt/archives v0.1.3/go.mod h1:LUCGp++/IbV/I0Xq4SzcIR6uwgeh2yjnQWamjRQfLTU=
github.com/mikelolasagasti/xz v1.0.1 h1:Q2F2jX0RYJUG3+WsM+FJknv+6eVjsjXNDV0KJXZzkD0=
github.com/mikelolasagasti/xz v1.0.1/go.mod h1:muAirjiOUxPRXwm9HdDtB3uoRPrGnL85XHtokL9Hcgc=
github.com/minio/minlz v1.0.0 h1:Kj7aJZ1//LlTP1DM8Jm7lNKvvJS2m74gyyXXn3+uJWQ=
github.com/minio/minlz v1.0.0/go.mod h1:qT0aEB35q79LLornSzeDH75LBf3aH1MV+jB5w9Wasec=
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ= github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ=
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw= github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw=
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/sys/atomicwriter v0.1.0 h1:kw5D/EqkBwsBFi0ss9v1VG3wIkVhzGvLklJ+w3A14Sw=
github.com/moby/sys/atomicwriter v0.1.0/go.mod h1:Ul8oqv2ZMNHOceF643P6FKPXeCmYtlQMvpizfsSoaWs=
github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU=
github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko=
github.com/moby/term v0.0.0-20220808134915-39b0c02b01ae h1:O4SWKdcHVCvYqyDV+9CJA1fcDN2L11Bule0iFy3YlAI= github.com/moby/term v0.0.0-20220808134915-39b0c02b01ae h1:O4SWKdcHVCvYqyDV+9CJA1fcDN2L11Bule0iFy3YlAI=
github.com/moby/term v0.0.0-20220808134915-39b0c02b01ae/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw= github.com/moby/term v0.0.0-20220808134915-39b0c02b01ae/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@ -281,26 +292,26 @@ github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4= github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
github.com/nwaples/rardecode/v2 v2.0.0-beta.2 h1:e3mzJFJs4k83GXBEiTaQ5HgSc/kOK8q0rDaRO0MPaOk= github.com/nwaples/rardecode/v2 v2.1.1 h1:OJaYalXdliBUXPmC8CZGQ7oZDxzX1/5mQmgn0/GASew=
github.com/nwaples/rardecode/v2 v2.0.0-beta.2/go.mod h1:yntwv/HfMc/Hbvtq9I19D1n58te3h6KsqCf3GxyfBGY= github.com/nwaples/rardecode/v2 v2.1.1/go.mod h1:7uz379lSxPe6j9nvzxUZ+n7mnJNgjsRNb6IbvGVHRmw=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= 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/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.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=
github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
github.com/pelletier/go-toml/v2 v2.2.0 h1:QLgLl2yMN7N+ruc31VynXs1vhMZa7CeHHejIeBAsoHo= github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
github.com/pelletier/go-toml/v2 v2.2.0/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ= github.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU=
github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pierrec/lz4/v4 v4.1.22/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/sftp v1.13.6 h1:JFZT4XbOU7l77xGSpOdW+pwIMqP044IyjXX6FGyEKFo= github.com/pkg/sftp v1.13.9 h1:4NGkvGudBL7GteO3m6qnaQ4pC0Kvf0onSVc9gR3EWBw=
github.com/pkg/sftp v1.13.6/go.mod h1:tz1ryNURKu77RL+GuCzmoJYxQczL3wLNNpPWagdg4Qk= github.com/pkg/sftp v1.13.9/go.mod h1:OBN7bVXdstkFFN/gdnHPUb5TE8eb8G1Rp9wCItqjkkA=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 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/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
@ -311,8 +322,9 @@ github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzG
github.com/rogpeppe/fastuuid v1.1.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/fastuuid v1.1.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg=
github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o=
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk= github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk=
github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06 h1:OkMGxebDjyw0ULyrTYWeN0UNCCkmCWfjPnIA2W6oviI= github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06 h1:OkMGxebDjyw0ULyrTYWeN0UNCCkmCWfjPnIA2W6oviI=
@ -323,11 +335,13 @@ github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVs
github.com/smartystreets/assertions v1.0.0/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM= github.com/smartystreets/assertions v1.0.0/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM=
github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9/go.mod h1:SnhjPscd9TpLiy1LpzGSKh3bXCfxxXuqd9xmQJy3slM= github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9/go.mod h1:SnhjPscd9TpLiy1LpzGSKh3bXCfxxXuqd9xmQJy3slM=
github.com/smartystreets/gunit v1.0.0/go.mod h1:qwPWnhz6pn0NnRBP++URONOVyNkPyr4SauJk4cUOwJs= github.com/smartystreets/gunit v1.0.0/go.mod h1:qwPWnhz6pn0NnRBP++URONOVyNkPyr4SauJk4cUOwJs=
github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= github.com/sorairolake/lzip-go v0.3.5 h1:ms5Xri9o1JBIWvOFAorYtUNik6HI3HgBTkISiqu0Cwg=
github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= github.com/sorairolake/lzip-go v0.3.5/go.mod h1:N0KYq5iWrMXI0ZEXKXaS9hCyOjZUQdBDEIbXfoUwbdk=
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 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.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
@ -341,10 +355,8 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/therootcompany/xz v1.0.1 h1:CmOtsn1CbtmyYiusbfmhmkpAAETj0wBIH6kCYaX+xzw=
github.com/therootcompany/xz v1.0.1/go.mod h1:3K3UH1yCKgBneZYhuQUvJ9HPD19UEXEI0BWbMn8qNMY=
github.com/tj/assert v0.0.0-20171129193455-018094318fb0/go.mod h1:mZ9/Rh9oLWpLLDRpvE+3b7gP/C2YyLFYxNmcLnPTMe0= github.com/tj/assert v0.0.0-20171129193455-018094318fb0/go.mod h1:mZ9/Rh9oLWpLLDRpvE+3b7gP/C2YyLFYxNmcLnPTMe0=
github.com/tj/assert v0.0.3 h1:Df/BlaZ20mq6kuai7f5z2TvPFiwC3xaWJSDQNiIS3Rk= github.com/tj/assert v0.0.3 h1:Df/BlaZ20mq6kuai7f5z2TvPFiwC3xaWJSDQNiIS3Rk=
github.com/tj/assert v0.0.3/go.mod h1:Ne6X72Q+TB1AteidzQncjw9PabbMp4PBMZ1k+vd1Pvk= github.com/tj/assert v0.0.3/go.mod h1:Ne6X72Q+TB1AteidzQncjw9PabbMp4PBMZ1k+vd1Pvk=
@ -357,8 +369,10 @@ github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc= github.com/ulikunitz/xz v0.5.14 h1:uv/0Bq533iFdnMHZdRBTOlaNMdb1+ZxXIlHDZHIHcvg=
github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/ulikunitz/xz v0.5.14/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
@ -366,20 +380,24 @@ 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.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 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.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.50.0 h1:cEPbyTSEHlQR89XVlyo78gqluF8Y3oMeBkXGWzQsfXY= go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.50.0/go.mod h1:DKdbWcT4GH1D0Y3Sqt/PFXt2naRKDWtU+eE6oLdFNA8= go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/otel v1.25.0 h1:gldB5FfhRl7OJQbUHt/8s0a7cE8fbsPAtdpRaApKy4k= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU=
go.opentelemetry.io/otel v1.25.0/go.mod h1:Wa2ds5NOXEMkCmUou1WA7ZBfLTHWIsp034OVD7AO+Vg= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ=
go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.24.0 h1:t6wl9SPayj+c7lEIFgm4ooDBZVb01IhLB4InpomhRw8= 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 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 h1:Xw8U6u2f8DK2XAkGRFV7BBLENgnTGX9i4rQRxJf+/vs=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.24.0/go.mod h1:6KW1Fm6R/s6Z3PGXwSJN2K4eT6wQB3vXX6CVnYX9NmM= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.24.0/go.mod h1:6KW1Fm6R/s6Z3PGXwSJN2K4eT6wQB3vXX6CVnYX9NmM=
go.opentelemetry.io/otel/metric v1.25.0 h1:LUKbS7ArpFL/I2jJHdJcqMGxkRdxpPHE0VU/D4NuEwA= go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M=
go.opentelemetry.io/otel/metric v1.25.0/go.mod h1:rkDLUSd2lC5lq2dFNrX9LGAbINP5B7WBkC78RXCpH5s= go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=
go.opentelemetry.io/otel/sdk v1.24.0 h1:YMPPDNymmQN3ZgczicBY3B6sf9n62Dlj9pWD3ucgoDw= go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY=
go.opentelemetry.io/otel/sdk v1.24.0/go.mod h1:KVrIYw6tEubO9E96HQpcmpTKDVn9gdv35HoYiQWGDFg= go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg=
go.opentelemetry.io/otel/trace v1.25.0 h1:tqukZGLwQYRIFtSQM2u2+yfMVTgGVeqRLPUYx1Dq6RM= go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o=
go.opentelemetry.io/otel/trace v1.25.0/go.mod h1:hCCs70XM/ljO+BeQkyFnbK28SBIJ/Emuha+ccrCRT7I= go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w=
go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=
go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=
go.opentelemetry.io/proto/otlp v1.1.0 h1:2Di21piLrCqJ3U3eXGCTPHE9R8Nh+0uglSnOyxikMeI= 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.opentelemetry.io/proto/otlp v1.1.0/go.mod h1:GpBHCBWiqvVLDqmHZsoMM3C5ySeKTC7ej/RNTae6MdY=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
@ -393,9 +411,8 @@ 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/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go4.org v0.0.0-20230225012048-214862532bf5 h1:nifaUDeh+rPaBCMPMQHZmvJf+QdpLFnuQPwx+LxVmtc= go4.org v0.0.0-20230225012048-214862532bf5 h1:nifaUDeh+rPaBCMPMQHZmvJf+QdpLFnuQPwx+LxVmtc=
go4.org v0.0.0-20230225012048-214862532bf5/go.mod h1:F57wTi5Lrj6WLyswp5EYV1ncrEbFGHD4hhz6S1ZYeaU= go4.org v0.0.0-20230225012048-214862532bf5/go.mod h1:F57wTi5Lrj6WLyswp5EYV1ncrEbFGHD4hhz6S1ZYeaU=
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/arch v0.15.0 h1:QtOrQd0bTUnhNVNndMpLHNWrDmYzZ2KDqSrEymqInZw=
golang.org/x/arch v0.7.0 h1:pskyeJh/3AmoQ8CPE95vxHLqp1G1GfGNXTmcl9NEKTc= golang.org/x/arch v0.15.0/go.mod h1:JmwW7aLIoRUKgaTzhkiEFxvcEiQGyOg9BMonBJUS7EE=
golang.org/x/arch v0.7.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
@ -404,9 +421,12 @@ golang.org/x/crypto v0.0.0-20190927123631-a832865fa7ad/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/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-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4=
golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 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-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@ -415,6 +435,8 @@ golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20191227195350-da58074b4299/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-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 h1:nDVHiLt8aIbd/VzvPWN6kSOPE7+F/fNFDSXLVYkE/Iw=
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394/go.mod h1:sIifuuw/Yco/y6yb6+bDNfyeQ/MdPUy/hKEMYQV17cM=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= 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/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= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@ -434,8 +456,12 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 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.3.0/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.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.26.0 h1:EGMPT//Ezu+ylkCijjPc+f4Aih7sZvaAr+O3EHBxvZg=
golang.org/x/mod v0.26.0/go.mod h1:/j6NAhSk8iQ723BGAUyoAcn7SlD7s15Dp9Nd/SfeaFQ=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 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-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@ -454,10 +480,14 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=
golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 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-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@ -471,8 +501,13 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/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-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/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-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -494,18 +529,26 @@ golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/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-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-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/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.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/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.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk= golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4=
golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 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.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.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@ -514,8 +557,13 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= 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.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 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-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20220922220347-f3bd1da661af h1:Yx9k8YCG3dvF87UAn2tu2HQLf2dt/eR1bXxpLMWeH+Y= golang.org/x/time v0.0.0-20220922220347-f3bd1da661af h1:Yx9k8YCG3dvF87UAn2tu2HQLf2dt/eR1bXxpLMWeH+Y=
@ -549,14 +597,17 @@ golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapK
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/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-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/tools v0.35.0 h1:mBffYraMEf7aa0sB+NuKnuCy8qI/9Bughn8dC2Gu5r0=
golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 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-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-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU= golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da h1:noIWHXmPHxILtqtCOPIhSt0ABwskkZKjD3bXGnZGpNY=
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= 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= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
@ -586,8 +637,8 @@ google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfG
google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80 h1:KAeGQVN3M9nD0/bQXnr/ClcEMJ968gUXJQ9pwfSynuQ= google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80 h1:KAeGQVN3M9nD0/bQXnr/ClcEMJ968gUXJQ9pwfSynuQ=
google.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917 h1:rcS6EyEaoCO52hQDupoSfrxI3R6C2Tq741is7X8OvnM= google.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917 h1:rcS6EyEaoCO52hQDupoSfrxI3R6C2Tq741is7X8OvnM=
google.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917/go.mod h1:CmlNWB9lSezaYELKS5Ym1r44VrrbPUa7JTvw+6MbpJ0= google.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917/go.mod h1:CmlNWB9lSezaYELKS5Ym1r44VrrbPUa7JTvw+6MbpJ0=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 h1:AjyfHzEPEFp/NpvfN5g+KDla3EMojjhRVZc1i7cj+oM= google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 h1:pPJltXNxVzT4pK9yD8vR9X75DaWYYmLGMsEvBfFQZzQ=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80/go.mod h1:PAREbraiVEVGVdTZsVWjSbbTtSyGbAgIIvni8a8CD5s= google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 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.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
@ -595,10 +646,10 @@ google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyac
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.62.0 h1:HQKZ/fa1bXkX1oFOvSjmZEUL8wLSaZTjCcLAlmZRtdk= google.golang.org/grpc v1.67.0 h1:IdH9y6PF5MPSdAntIcpjQ+tXO41pcQsfZV2RxtQgVcw=
google.golang.org/grpc v1.62.0/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE= google.golang.org/grpc v1.67.0/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA=
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@ -617,8 +668,8 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C
gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/gorm v1.25.9 h1:wct0gxZIELDk8+ZqF/MVnHLkA1rvYlBWUMv2EdsK1g8= gorm.io/gorm v1.26.0 h1:9lqQVPG5aNNS6AyHdRiwScAVnXHg/L/Srzx55G5fOgs=
gorm.io/gorm v1.25.9/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= gorm.io/gorm v1.26.0/go.mod h1:8Z33v652h4//uMA76KjeDH8mJXPm1QNCYrMeatR0DOE=
gotest.tools/v3 v3.0.2 h1:kG1BFyqVHuQoVQiR1bWGnfz/fmHvvuiSPIV7rvl360E= gotest.tools/v3 v3.0.2 h1:kG1BFyqVHuQoVQiR1bWGnfz/fmHvvuiSPIV7rvl360E=
gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
@ -626,32 +677,31 @@ honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
modernc.org/cc/v4 v4.20.0 h1:45Or8mQfbUqJOG9WaxvlFYOAQO0lQ5RvqBcFCXngjxk= modernc.org/cc/v4 v4.24.4 h1:TFkx1s6dCkQpd6dKurBNmpo+G8Zl4Sq/ztJ+2+DEsh0=
modernc.org/cc/v4 v4.20.0/go.mod h1:HM7VJTZbUCR3rV8EYBi9wxnJ0ZBRiGE5OeGXNA0IsLQ= modernc.org/cc/v4 v4.24.4/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
modernc.org/ccgo/v4 v4.16.0 h1:ofwORa6vx2FMm0916/CkZjpFPSR70VwTjUCe2Eg5BnA= modernc.org/ccgo/v4 v4.23.16 h1:Z2N+kk38b7SfySC1ZkpGLN2vthNJP1+ZzGZIlH7uBxo=
modernc.org/ccgo/v4 v4.16.0/go.mod h1:dkNyWIjFrVIZ68DTo36vHK+6/ShBn4ysU61So6PIqCI= modernc.org/ccgo/v4 v4.23.16/go.mod h1:nNma8goMTY7aQZQNTyN9AIoJfxav4nvTnvKThAeMDdo=
modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE= modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE=
modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ= modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ=
modernc.org/gc/v2 v2.4.1 h1:9cNzOqPyMJBvrUipmynX0ZohMhcxPtMccYgGOJdOiBw= modernc.org/gc/v2 v2.6.3 h1:aJVhcqAte49LF+mGveZ5KPlsp4tdGdAOT4sipJXADjw=
modernc.org/gc/v2 v2.4.1/go.mod h1:wzN5dK1AzVGoH6XOzc3YZ+ey/jPgYHLuVckd62P0GYU= modernc.org/gc/v2 v2.6.3/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=
modernc.org/libc v1.49.3 h1:j2MRCRdwJI2ls/sGbeSk0t2bypOG/uvPZUsGQFDulqg= modernc.org/libc v1.61.13 h1:3LRd6ZO1ezsFiX1y+bHd1ipyEHIJKvuprv0sLTBwLW8=
modernc.org/libc v1.49.3/go.mod h1:yMZuGkn7pXbKfoT/M35gFJOAEdSKdxL0q64sF7KqCDo= modernc.org/libc v1.61.13/go.mod h1:8F/uJWL/3nNil0Lgt1Dpz+GgkApWh04N3el3hxJcA6E=
modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4= modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo= modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
modernc.org/memory v1.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E= modernc.org/memory v1.8.2 h1:cL9L4bcoAObu4NkxOlKWBWtNHIsnnACGF/TbqQ6sbcI=
modernc.org/memory v1.8.0/go.mod h1:XPZ936zp5OMKGWPqbD3JShgd/ZoQ7899TUuQqxY+peU= modernc.org/memory v1.8.2/go.mod h1:ZbjSvMO5NQ1A2i3bWeDiVMxIorXwdClKE/0SZ+BMotU=
modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4= modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8=
modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
modernc.org/sortutil v1.2.0 h1:jQiD3PfS2REGJNzNCMMaLSp/wdMNieTbKX920Cqdgqc= modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
modernc.org/sortutil v1.2.0/go.mod h1:TKU2s7kJMf1AE84OoiGppNHJwvB753OYfNl2WRb++Ss= modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
modernc.org/sqlite v1.29.6 h1:0lOXGrycJPptfHDuohfYgNqoe4hu+gYuN/pKgY5XjS4= modernc.org/sqlite v1.36.0 h1:EQXNRn4nIS+gfsKeUTymHIz1waxuv5BzU7558dHSfH8=
modernc.org/sqlite v1.29.6/go.mod h1:S02dvcmm7TnTRvGhv8IGYyLnIt7AS2KPaB1F/71p75U= modernc.org/sqlite v1.36.0/go.mod h1:7MPwH7Z6bREicF9ZVUR78P1IKuxfZ8mRIDHD0iD+8TU=
modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA= modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0= modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=

View File

@ -56,6 +56,7 @@ func (ac *activityCron) Run(ctx context.Context) error {
activities = append(activities, v) activities = append(activities, v)
} }
// Delete any invalid activies
if len(ids) > 0 { if len(ids) > 0 {
tx = database.Instance().WithContext(ctx).Where("id IN ?", ids).Delete(&models.Activity{}) tx = database.Instance().WithContext(ctx).Where("id IN ?", ids).Delete(&models.Activity{})
if tx.Error != nil { if tx.Error != nil {
@ -71,16 +72,28 @@ func (ac *activityCron) Run(ctx context.Context) error {
return errors.WrapIf(err, "cron: failed to send activity events to Panel") return errors.WrapIf(err, "cron: failed to send activity events to Panel")
} }
// Add all the successful activities to the list of IDs to delete.
ids = make([]int, len(activities)) ids = make([]int, len(activities))
for i, v := range activities { for i, v := range activities {
ids[i] = v.ID ids[i] = v.ID
} }
// Delete all the activities that were sent to the Panel (or that were invalid). // SQLite has a limitation of how many parameters we can specify in a single
tx = database.Instance().WithContext(ctx).Where("id IN ?", ids).Delete(&models.Activity{}) // query, so we need to delete the activies in chunks of 32,000 instead of
if tx.Error != nil { // all at once.
return errors.WithStack(tx.Error) i := 0
idsLen := len(ids)
for i < idsLen {
start := i
end := min(i+32000, idsLen)
batchSize := end - start
tx = database.Instance().WithContext(ctx).Where("id IN ?", ids[start:end]).Delete(&models.Activity{})
if tx.Error != nil {
return errors.WithStack(tx.Error)
}
i += batchSize
} }
return nil return nil
} }

View File

@ -5,6 +5,7 @@ import (
"reflect" "reflect"
"emperror.dev/errors" "emperror.dev/errors"
"gorm.io/gorm"
"github.com/pterodactyl/wings/internal/database" "github.com/pterodactyl/wings/internal/database"
"github.com/pterodactyl/wings/internal/models" "github.com/pterodactyl/wings/internal/models"
@ -83,9 +84,26 @@ func (sc *sftpCron) Run(ctx context.Context) error {
if err := sc.manager.Client().SendActivityLogs(ctx, events.Elements()); err != nil { if err := sc.manager.Client().SendActivityLogs(ctx, events.Elements()); err != nil {
return errors.Wrap(err, "failed to send sftp activity logs to Panel") return errors.Wrap(err, "failed to send sftp activity logs to Panel")
} }
if tx := database.Instance().Where("id IN ?", events.ids).Delete(&models.Activity{}); tx.Error != nil {
return errors.WithStack(tx.Error) // SQLite has a limitation of how many parameters we can specify in a single
// query, so we need to delete the activies in chunks of 32,000 instead of
// all at once.
i := 0
idsLen := len(events.ids)
var tx *gorm.DB
for i < idsLen {
start := i
end := min(i+32000, idsLen)
batchSize := end - start
tx = database.Instance().WithContext(ctx).Where("id IN ?", events.ids[start:end]).Delete(&models.Activity{})
if tx.Error != nil {
return errors.WithStack(tx.Error)
}
i += batchSize
} }
return nil return nil
} }

View File

@ -2,9 +2,9 @@ package models
import ( import (
"database/sql" "database/sql"
"encoding/json"
"emperror.dev/errors" "emperror.dev/errors"
"github.com/goccy/go-json"
) )
type JsonNullString struct { type JsonNullString struct {

View File

@ -1,5 +1,9 @@
# Filesystem # Filesystem
Coming Soon&trade;
> TODO
## Licensing ## Licensing
Most code in this package is licensed under `MIT` with some exceptions. Most code in this package is licensed under `MIT` with some exceptions.
@ -11,7 +15,7 @@ verbatim or derived from [Go](https://go.dev)'s source code.
- [`mkdir_unix.go`](./mkdir_unix.go) - [`mkdir_unix.go`](./mkdir_unix.go)
- [`path_unix.go`](./path_unix.go) - [`path_unix.go`](./path_unix.go)
- [`removeall_unix.go`](./removeall_unix.go) - [`removeall_unix.go`](./removeall_unix.go)
- [`stat_unix.go`](./stat_unix.go) - [`stat_unix.go`](./stat_unix.go)
- [`walk.go`](./walk.go) - [`walk.go`](./walk.go)
These changes are not associated with nor endorsed by The Go Authors. These changes are not associated with nor endorsed by The Go Authors.

View File

@ -7,6 +7,7 @@ import (
"errors" "errors"
iofs "io/fs" iofs "io/fs"
"os" "os"
"syscall"
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
) )
@ -48,9 +49,9 @@ type PathError = iofs.PathError
// SyscallError records an error from a specific system call. // SyscallError records an error from a specific system call.
type SyscallError = os.SyscallError type SyscallError = os.SyscallError
// NewSyscallError returns, as an error, a new SyscallError // NewSyscallError returns, as an error, a new [*os.SyscallError] with the
// with the given system call name and error details. // given system call name and error details. As a convenience, if err is nil,
// As a convenience, if err is nil, NewSyscallError returns nil. // [NewSyscallError] returns nil.
func NewSyscallError(syscall string, err error) error { func NewSyscallError(syscall string, err error) error {
return os.NewSyscallError(syscall, err) return os.NewSyscallError(syscall, err)
} }
@ -61,60 +62,122 @@ func convertErrorType(err error) error {
if err == nil { if err == nil {
return nil return nil
} }
var pErr *PathError var pErr *PathError
switch { if errors.As(err, &pErr) {
case errors.As(err, &pErr): if errno, ok := pErr.Err.(syscall.Errno); ok {
switch { return errnoToPathError(errno, pErr.Op, pErr.Path)
// File exists }
case errors.Is(pErr.Err, unix.EEXIST): return pErr
return &PathError{ }
Op: pErr.Op,
Path: pErr.Path, // If the error wasn't already a path error and is a errno, wrap it with
Err: ErrExist, // details that we can use to know there is something wrong with our
} // error wrapping somewhere.
// Is a directory var errno syscall.Errno
case errors.Is(pErr.Err, unix.EISDIR): if errors.As(err, &errno) {
return &PathError{ return &PathError{
Op: pErr.Op, Op: "!(UNKNOWN)",
Path: pErr.Path, Path: "!(UNKNOWN)",
Err: ErrIsDirectory, Err: err,
}
// Not a directory
case errors.Is(pErr.Err, unix.ENOTDIR):
return &PathError{
Op: pErr.Op,
Path: pErr.Path,
Err: ErrNotDirectory,
}
// No such file or directory
case errors.Is(pErr.Err, unix.ENOENT):
return &PathError{
Op: pErr.Op,
Path: pErr.Path,
Err: ErrNotExist,
}
// Operation not permitted
case errors.Is(pErr.Err, unix.EPERM):
return &PathError{
Op: pErr.Op,
Path: pErr.Path,
Err: ErrPermission,
}
// Invalid cross-device link
case errors.Is(pErr.Err, unix.EXDEV):
return &PathError{
Op: pErr.Op,
Path: pErr.Path,
Err: ErrBadPathResolution,
}
// Too many levels of symbolic links
case errors.Is(pErr.Err, unix.ELOOP):
return &PathError{
Op: pErr.Op,
Path: pErr.Path,
Err: ErrBadPathResolution,
}
} }
} }
return err return err
} }
// ensurePathError ensures that err is a PathError. The op and path arguments
// are only used of the error isn't already a PathError.
func ensurePathError(err error, op, path string) error {
if err == nil {
return nil
}
// Check if the error is already a PathError.
var pErr *PathError
if errors.As(err, &pErr) {
// If underlying error is a errno, convert it.
//
// DO NOT USE `errors.As` or whatever here, the error will either be
// an errno, or it will be wrapped already.
if errno, ok := pErr.Err.(syscall.Errno); ok {
return errnoToPathError(errno, pErr.Op, pErr.Path)
}
// Return the PathError as-is without modification.
return pErr
}
// If the error is directly an errno, convert it to a PathError.
var errno syscall.Errno
if errors.As(err, &errno) {
return errnoToPathError(errno, op, path)
}
// Otherwise just wrap it as a PathError without any additional changes.
return &PathError{
Op: op,
Path: path,
Err: err,
}
}
// errnoToPathError converts an errno into a proper path error.
func errnoToPathError(err syscall.Errno, op, path string) error {
switch err {
// File exists
case unix.EEXIST:
return &PathError{
Op: op,
Path: path,
Err: ErrExist,
}
// Is a directory
case unix.EISDIR:
return &PathError{
Op: op,
Path: path,
Err: ErrIsDirectory,
}
// Not a directory
case unix.ENOTDIR:
return &PathError{
Op: op,
Path: path,
Err: ErrNotDirectory,
}
// No such file or directory
case unix.ENOENT:
return &PathError{
Op: op,
Path: path,
Err: ErrNotExist,
}
// Operation not permitted
case unix.EPERM:
return &PathError{
Op: op,
Path: path,
Err: ErrPermission,
}
// Invalid cross-device link
case unix.EXDEV:
return &PathError{
Op: op,
Path: path,
Err: ErrBadPathResolution,
}
// Too many levels of symbolic links
case unix.ELOOP:
return &PathError{
Op: op,
Path: path,
Err: ErrBadPathResolution,
}
default:
return &PathError{
Op: op,
Path: path,
Err: err,
}
}
}

View File

@ -146,6 +146,7 @@ const (
ModePerm = iofs.ModePerm ModePerm = iofs.ModePerm
) )
// Re-using the same names as Go's official `unix` and `os` package do.
const ( const (
// O_RDONLY opens the file read-only. // O_RDONLY opens the file read-only.
O_RDONLY = unix.O_RDONLY O_RDONLY = unix.O_RDONLY

View File

@ -7,6 +7,12 @@ import (
"sync/atomic" "sync/atomic"
) )
// Quota is a wrapper around [*UnixFS] that provides the ability to limit the
// disk usage of the filesystem.
//
// NOTE: this is not a full complete quota filesystem, it provides utilities for
// tracking and checking the usage of the filesystem. The only operation that is
// automatically accounted against the quota are file deletions.
type Quota struct { type Quota struct {
// fs is the underlying filesystem that runs the actual I/O operations. // fs is the underlying filesystem that runs the actual I/O operations.
*UnixFS *UnixFS
@ -26,6 +32,7 @@ type Quota struct {
usage atomic.Int64 usage atomic.Int64
} }
// NewQuota creates a new Quota filesystem using an existing UnixFS and a limit.
func NewQuota(fs *UnixFS, limit int64) *Quota { func NewQuota(fs *UnixFS, limit int64) *Quota {
qfs := Quota{UnixFS: fs} qfs := Quota{UnixFS: fs}
qfs.limit.Store(limit) qfs.limit.Store(limit)
@ -101,6 +108,9 @@ func (fs *Quota) CanFit(size int64) bool {
return false return false
} }
// Remove removes the named file or (empty) directory.
//
// If there is an error, it will be of type [*PathError].
func (fs *Quota) Remove(name string) error { func (fs *Quota) Remove(name string) error {
// For information on why this interface is used here, check its // For information on why this interface is used here, check its
// documentation. // documentation.
@ -125,7 +135,7 @@ func (fs *Quota) Remove(name string) error {
// it encounters. If the path does not exist, RemoveAll // it encounters. If the path does not exist, RemoveAll
// returns nil (no error). // returns nil (no error).
// //
// If there is an error, it will be of type *PathError. // If there is an error, it will be of type [*PathError].
func (fs *Quota) RemoveAll(name string) error { func (fs *Quota) RemoveAll(name string) error {
name, err := fs.unsafePath(name) name, err := fs.unsafePath(name)
if err != nil { if err != nil {

View File

@ -12,7 +12,6 @@ import (
"path/filepath" "path/filepath"
"strconv" "strconv"
"strings" "strings"
"sync/atomic"
"time" "time"
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
@ -26,10 +25,6 @@ type UnixFS struct {
// basePath is the base path for file operations to take place in. // basePath is the base path for file operations to take place in.
basePath string basePath string
// dirfd holds the file descriptor of BasePath and is used to ensure
// operations are restricted into descendants of BasePath.
dirfd atomic.Int64
// useOpenat2 controls whether the `openat2` syscall is used instead of the // useOpenat2 controls whether the `openat2` syscall is used instead of the
// older `openat` syscall. // older `openat` syscall.
useOpenat2 bool useOpenat2 bool
@ -41,18 +36,10 @@ type UnixFS struct {
// checked and prevented from enabling an escape in a non-raceable manor. // checked and prevented from enabling an escape in a non-raceable manor.
func NewUnixFS(basePath string, useOpenat2 bool) (*UnixFS, error) { func NewUnixFS(basePath string, useOpenat2 bool) (*UnixFS, error) {
basePath = strings.TrimSuffix(basePath, "/") basePath = strings.TrimSuffix(basePath, "/")
// We don't need Openat2, if we are given a basePath that is already unsafe
// I give up on trying to sandbox it.
dirfd, err := unix.Openat(AT_EMPTY_PATH, basePath, O_DIRECTORY|O_RDONLY, 0)
if err != nil {
return nil, convertErrorType(err)
}
fs := &UnixFS{ fs := &UnixFS{
basePath: basePath, basePath: basePath,
useOpenat2: useOpenat2, useOpenat2: useOpenat2,
} }
fs.dirfd.Store(int64(dirfd))
return fs, nil return fs, nil
} }
@ -66,12 +53,7 @@ func (fs *UnixFS) BasePath() string {
// Close releases the file descriptor used to sandbox operations within the // Close releases the file descriptor used to sandbox operations within the
// base path of the filesystem. // base path of the filesystem.
func (fs *UnixFS) Close() error { func (fs *UnixFS) Close() error {
// Once closed, change dirfd to something invalid to detect when it has been return nil
// closed.
defer func() {
fs.dirfd.Store(-1)
}()
return unix.Close(int(fs.dirfd.Load()))
} }
// Chmod changes the mode of the named file to mode. // Chmod changes the mode of the named file to mode.
@ -99,7 +81,16 @@ func (fs *UnixFS) Chmod(name string, mode FileMode) error {
if err != nil { if err != nil {
return err return err
} }
return convertErrorType(unix.Fchmodat(dirfd, name, uint32(mode), 0)) return fs.fchmodat("chmod", dirfd, name, mode)
}
// Chmodat is like Chmod but it takes a dirfd and name instead of a full path.
func (fs *UnixFS) Chmodat(dirfd int, name string, mode FileMode) error {
return fs.fchmodat("chmodat", dirfd, name, mode)
}
func (fs *UnixFS) fchmodat(op string, dirfd int, name string, mode FileMode) error {
return ensurePathError(unix.Fchmodat(dirfd, name, uint32(mode), 0), op, name)
} }
// Chown changes the numeric uid and gid of the named file. // Chown changes the numeric uid and gid of the named file.
@ -111,7 +102,7 @@ func (fs *UnixFS) Chmod(name string, mode FileMode) error {
// On Windows or Plan 9, Chown always returns the syscall.EWINDOWS or // On Windows or Plan 9, Chown always returns the syscall.EWINDOWS or
// EPLAN9 error, wrapped in *PathError. // EPLAN9 error, wrapped in *PathError.
func (fs *UnixFS) Chown(name string, uid, gid int) error { func (fs *UnixFS) Chown(name string, uid, gid int) error {
return fs.fchown(name, uid, gid, 0) return ensurePathError(fs.fchown(name, uid, gid, 0), "chown", name)
} }
// Lchown changes the numeric uid and gid of the named file. // Lchown changes the numeric uid and gid of the named file.
@ -124,7 +115,7 @@ func (fs *UnixFS) Chown(name string, uid, gid int) error {
func (fs *UnixFS) Lchown(name string, uid, gid int) error { func (fs *UnixFS) Lchown(name string, uid, gid int) error {
// With AT_SYMLINK_NOFOLLOW, Fchownat acts like Lchown but allows us to // With AT_SYMLINK_NOFOLLOW, Fchownat acts like Lchown but allows us to
// pass a dirfd. // pass a dirfd.
return fs.fchown(name, uid, gid, AT_SYMLINK_NOFOLLOW) return ensurePathError(fs.fchown(name, uid, gid, AT_SYMLINK_NOFOLLOW), "lchown", name)
} }
// fchown is a re-usable Fchownat syscall used by Chown and Lchown. // fchown is a re-usable Fchownat syscall used by Chown and Lchown.
@ -134,19 +125,19 @@ func (fs *UnixFS) fchown(name string, uid, gid, flags int) error {
if err != nil { if err != nil {
return err return err
} }
return convertErrorType(unix.Fchownat(dirfd, name, uid, gid, flags)) return unix.Fchownat(dirfd, name, uid, gid, flags)
} }
// Chownat is like Chown but allows passing an existing directory file // Chownat is like Chown but allows passing an existing directory file
// descriptor rather than needing to resolve one. // descriptor rather than needing to resolve one.
func (fs *UnixFS) Chownat(dirfd int, name string, uid, gid int) error { func (fs *UnixFS) Chownat(dirfd int, name string, uid, gid int) error {
return convertErrorType(unix.Fchownat(dirfd, name, uid, gid, 0)) return ensurePathError(unix.Fchownat(dirfd, name, uid, gid, 0), "chownat", name)
} }
// Lchownat is like Lchown but allows passing an existing directory file // Lchownat is like Lchown but allows passing an existing directory file
// descriptor rather than needing to resolve one. // descriptor rather than needing to resolve one.
func (fs *UnixFS) Lchownat(dirfd int, name string, uid, gid int) error { func (fs *UnixFS) Lchownat(dirfd int, name string, uid, gid int) error {
return convertErrorType(unix.Fchownat(dirfd, name, uid, gid, AT_SYMLINK_NOFOLLOW)) return ensurePathError(unix.Fchownat(dirfd, name, uid, gid, AT_SYMLINK_NOFOLLOW), "lchownat", name)
} }
// Chtimes changes the access and modification times of the named // Chtimes changes the access and modification times of the named
@ -178,11 +169,9 @@ func (fs *UnixFS) Chtimesat(dirfd int, name string, atime, mtime time.Time) erro
} }
set(0, atime) set(0, atime)
set(1, mtime) set(1, mtime)
// This does support `AT_SYMLINK_NOFOLLOW` as well if needed. // This does support `AT_SYMLINK_NOFOLLOW` as well if needed.
if err := unix.UtimesNanoAt(dirfd, name, utimes[0:], 0); err != nil { return ensurePathError(unix.UtimesNanoAt(dirfd, name, utimes[0:], 0), "chtimes", name)
return convertErrorType(&PathError{Op: "chtimes", Path: name, Err: err})
}
return nil
} }
// Create creates or truncates the named file. If the file already exists, // Create creates or truncates the named file. If the file already exists,
@ -206,11 +195,15 @@ func (fs *UnixFS) Mkdir(name string, mode FileMode) error {
if err != nil { if err != nil {
return err return err
} }
return fs.Mkdirat(dirfd, name, mode) return fs.mkdirat("mkdir", dirfd, name, mode)
} }
func (fs *UnixFS) Mkdirat(dirfd int, name string, mode FileMode) error { func (fs *UnixFS) Mkdirat(dirfd int, name string, mode FileMode) error {
return convertErrorType(unix.Mkdirat(dirfd, name, uint32(mode))) return fs.mkdirat("mkdirat", dirfd, name, mode)
}
func (fs *UnixFS) mkdirat(op string, dirfd int, name string, mode FileMode) error {
return ensurePathError(unix.Mkdirat(dirfd, name, uint32(mode)), op, name)
} }
// MkdirAll creates a directory named path, along with any necessary // MkdirAll creates a directory named path, along with any necessary
@ -294,8 +287,10 @@ func (fs *UnixFS) ReadDir(path string) ([]DirEntry, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer unix.Close(fd) defer func() {
return fs.readDir(fd, name, nil) _ = unix.Close(fd)
}()
return fs.readDir(fd, name, ".", nil)
} }
// RemoveStat is a combination of Stat and Remove, it is used to more // RemoveStat is a combination of Stat and Remove, it is used to more
@ -329,7 +324,7 @@ func (fs *UnixFS) RemoveStat(name string) (FileInfo, error) {
err = fs.unlinkat(dirfd, name, 0) err = fs.unlinkat(dirfd, name, 0)
} }
if err != nil { if err != nil {
return s, convertErrorType(&PathError{Op: "remove", Path: name, Err: err}) return s, ensurePathError(err, "rename", name)
} }
return s, nil return s, nil
} }
@ -378,7 +373,7 @@ func (fs *UnixFS) Remove(name string) error {
if err1 != unix.ENOTDIR { if err1 != unix.ENOTDIR {
err = err1 err = err1
} }
return convertErrorType(&PathError{Op: "remove", Path: name, Err: err}) return ensurePathError(err, "remove", name)
} }
// RemoveAll removes path and any children it contains. // RemoveAll removes path and any children it contains.
@ -393,6 +388,7 @@ func (fs *UnixFS) RemoveAll(name string) error {
if err != nil { if err != nil {
return err return err
} }
// While removeAll internally checks this, I want to make sure we check it // While removeAll internally checks this, I want to make sure we check it
// and return the proper error so our tests can ensure that this will never // and return the proper error so our tests can ensure that this will never
// be a possibility. // be a possibility.
@ -403,9 +399,29 @@ func (fs *UnixFS) RemoveAll(name string) error {
Err: ErrBadPathResolution, Err: ErrBadPathResolution,
} }
} }
return fs.removeAll(name) return fs.removeAll(name)
} }
// RemoveContents recursively removes the contents of name.
//
// It removes everything it can but returns the first error
// it encounters. If the path does not exist, RemoveContents
// returns nil (no error).
//
// If there is an error, it will be of type [*PathError].
func (fs *UnixFS) RemoveContents(name string) error {
name, err := fs.unsafePath(name)
if err != nil {
return err
}
// Unlike RemoveAll, we don't remove `name` itself, only it's contents.
// So there is no need to check for a name of `.` here.
return fs.removeContents(name)
}
func (fs *UnixFS) unlinkat(dirfd int, name string, flags int) error { func (fs *UnixFS) unlinkat(dirfd int, name string, flags int) error {
return ignoringEINTR(func() error { return ignoringEINTR(func() error {
return unix.Unlinkat(dirfd, name, flags) return unix.Unlinkat(dirfd, name, flags)
@ -434,11 +450,11 @@ func (fs *UnixFS) Rename(oldpath, newpath string) error {
// While unix.Renameat ends up throwing a "device or resource busy" error, // While unix.Renameat ends up throwing a "device or resource busy" error,
// that doesn't mean we are protecting the system properly. // that doesn't mean we are protecting the system properly.
if oldname == "." { if oldname == "." {
return convertErrorType(&PathError{ return &PathError{
Op: "rename", Op: "rename",
Path: oldname, Path: oldname,
Err: ErrBadPathResolution, Err: ErrBadPathResolution,
}) }
} }
// Stat the old target to return proper errors. // Stat the old target to return proper errors.
if _, err := fs.Lstatat(olddirfd, oldname); err != nil { if _, err := fs.Lstatat(olddirfd, oldname); err != nil {
@ -449,11 +465,11 @@ func (fs *UnixFS) Rename(oldpath, newpath string) error {
if err != nil { if err != nil {
closeFd2() closeFd2()
if !errors.Is(err, ErrNotExist) { if !errors.Is(err, ErrNotExist) {
return convertErrorType(err) return err
} }
var pathErr *PathError var pathErr *PathError
if !errors.As(err, &pathErr) { if !errors.As(err, &pathErr) {
return convertErrorType(err) return err
} }
if err := fs.MkdirAll(pathErr.Path, 0o755); err != nil { if err := fs.MkdirAll(pathErr.Path, 0o755); err != nil {
return err return err
@ -471,38 +487,41 @@ func (fs *UnixFS) Rename(oldpath, newpath string) error {
// While unix.Renameat ends up throwing a "device or resource busy" error, // While unix.Renameat ends up throwing a "device or resource busy" error,
// that doesn't mean we are protecting the system properly. // that doesn't mean we are protecting the system properly.
if newname == "." { if newname == "." {
return convertErrorType(&PathError{ return &PathError{
Op: "rename", Op: "rename",
Path: newname, Path: newname,
Err: ErrBadPathResolution, Err: ErrBadPathResolution,
}) }
} }
// Stat the new target to return proper errors. // Stat the new target to return proper errors.
_, err = fs.Lstatat(newdirfd, newname) _, err = fs.Lstatat(newdirfd, newname)
switch { switch {
case err == nil: case err == nil:
return convertErrorType(&PathError{ return &PathError{
Op: "rename", Op: "rename",
Path: newname, Path: newname,
Err: ErrExist, Err: ErrExist,
}) }
case !errors.Is(err, ErrNotExist): case !errors.Is(err, ErrNotExist):
return err return err
} }
return unix.Renameat(olddirfd, oldname, newdirfd, newname) if err := unix.Renameat(olddirfd, oldname, newdirfd, newname); err != nil {
return &LinkError{Op: "rename", Old: oldpath, New: newpath, Err: err}
}
return nil
} }
// Stat returns a FileInfo describing the named file. // Stat returns a FileInfo describing the named file.
// //
// If there is an error, it will be of type *PathError. // If there is an error, it will be of type *PathError.
func (fs *UnixFS) Stat(name string) (FileInfo, error) { func (fs *UnixFS) Stat(name string) (FileInfo, error) {
return fs.fstat(name, 0) return fs._fstat("stat", name, 0)
} }
// Statat is like Stat but allows passing an existing directory file // Statat is like Stat but allows passing an existing directory file
// descriptor rather than needing to resolve one. // descriptor rather than needing to resolve one.
func (fs *UnixFS) Statat(dirfd int, name string) (FileInfo, error) { func (fs *UnixFS) Statat(dirfd int, name string) (FileInfo, error) {
return fs.fstatat(dirfd, name, 0) return fs._fstatat("statat", dirfd, name, 0)
} }
// Lstat returns a FileInfo describing the named file. // Lstat returns a FileInfo describing the named file.
@ -512,30 +531,38 @@ func (fs *UnixFS) Statat(dirfd int, name string) (FileInfo, error) {
// //
// If there is an error, it will be of type *PathError. // If there is an error, it will be of type *PathError.
func (fs *UnixFS) Lstat(name string) (FileInfo, error) { func (fs *UnixFS) Lstat(name string) (FileInfo, error) {
return fs.fstat(name, AT_SYMLINK_NOFOLLOW) return fs._fstat("lstat", name, AT_SYMLINK_NOFOLLOW)
} }
// Lstatat is like Lstat but allows passing an existing directory file // Lstatat is like Lstat but allows passing an existing directory file
// descriptor rather than needing to resolve one. // descriptor rather than needing to resolve one.
func (fs *UnixFS) Lstatat(dirfd int, name string) (FileInfo, error) { func (fs *UnixFS) Lstatat(dirfd int, name string) (FileInfo, error) {
return fs.fstatat(dirfd, name, AT_SYMLINK_NOFOLLOW) return fs._fstatat("lstatat", dirfd, name, AT_SYMLINK_NOFOLLOW)
} }
func (fs *UnixFS) fstat(name string, flags int) (FileInfo, error) { func (fs *UnixFS) fstat(name string, flags int) (FileInfo, error) {
return fs._fstat("fstat", name, flags)
}
func (fs *UnixFS) _fstat(op string, name string, flags int) (FileInfo, error) {
dirfd, name, closeFd, err := fs.safePath(name) dirfd, name, closeFd, err := fs.safePath(name)
defer closeFd() defer closeFd()
if err != nil { if err != nil {
return nil, err return nil, err
} }
return fs.fstatat(dirfd, name, flags) return fs._fstatat(op, dirfd, name, flags)
} }
func (fs *UnixFS) fstatat(dirfd int, name string, flags int) (FileInfo, error) { func (fs *UnixFS) fstatat(dirfd int, name string, flags int) (FileInfo, error) {
return fs._fstatat("fstatat", dirfd, name, flags)
}
func (fs *UnixFS) _fstatat(op string, dirfd int, name string, flags int) (FileInfo, error) {
var s fileStat var s fileStat
if err := ignoringEINTR(func() error { if err := ignoringEINTR(func() error {
return unix.Fstatat(dirfd, name, &s.sys, flags) return unix.Fstatat(dirfd, name, &s.sys, flags)
}); err != nil { }); err != nil {
return nil, &PathError{Op: "stat", Path: name, Err: err} return nil, ensurePathError(err, op, name)
} }
fillFileStatFromSys(&s, name) fillFileStatFromSys(&s, name)
return &s, nil return &s, nil
@ -571,23 +598,42 @@ func (fs *UnixFS) Touch(path string, flag int, mode FileMode) (File, error) {
if flag&O_CREATE == 0 { if flag&O_CREATE == 0 {
flag |= O_CREATE flag |= O_CREATE
} }
dirfd, name, closeFd, err := fs.safePath(path) dirfd, name, closeFd, err, _ := fs.TouchPath(path)
defer closeFd() defer closeFd()
if err == nil { if err != nil {
return fs.OpenFileat(dirfd, name, flag, mode)
}
if !errors.Is(err, ErrNotExist) {
return nil, err return nil, err
} }
return fs.OpenFileat(dirfd, name, flag, mode)
}
// TouchPath is like SafePath except that it will create any missing directories
// in the path. Unlike SafePath, TouchPath returns an additional boolean which
// indicates whether the parent directories already existed, this is intended to
// be used as a way to know if the final destination could already exist.
func (fs *UnixFS) TouchPath(path string) (int, string, func(), error, bool) {
dirfd, name, closeFd, err := fs.safePath(path)
switch {
case err == nil:
return dirfd, name, closeFd, nil, true
case !errors.Is(err, ErrNotExist):
return dirfd, name, closeFd, err, false
}
var pathErr *PathError var pathErr *PathError
if !errors.As(err, &pathErr) { if !errors.As(err, &pathErr) {
return nil, err return dirfd, name, closeFd, err, false
} }
if err := fs.MkdirAll(pathErr.Path, 0o755); err != nil { if err := fs.MkdirAll(pathErr.Path, 0o755); err != nil {
return nil, err return dirfd, name, closeFd, err, false
} }
// Try to open the file one more time after creating its parent directories.
return fs.OpenFile(path, flag, mode) // Close the previous file descriptor since we are going to be opening
// a new one.
closeFd()
// Run safe path again now that the parent directories have been created.
dirfd, name, closeFd, err = fs.safePath(path)
return dirfd, name, closeFd, err, false
} }
// WalkDir walks the file tree rooted at root, calling fn for each file or // WalkDir walks the file tree rooted at root, calling fn for each file or
@ -629,43 +675,54 @@ func (fs *UnixFS) openat(dirfd int, name string, flag int, mode FileMode) (int,
if err == unix.EINTR { if err == unix.EINTR {
continue continue
} }
return 0, convertErrorType(err) return 0, err
}
// If we are using openat2, we don't need the additional security checks.
if fs.useOpenat2 {
return fd, nil
} }
// If we are not using openat2, do additional path checking. This assumes // If we are not using openat2, do additional path checking. This assumes
// that openat2 is using `RESOLVE_BENEATH` to avoid the same security // that openat2 is using `RESOLVE_BENEATH` to avoid the same security
// issue. // issue.
if !fs.useOpenat2 { var finalPath string
var finalPath string finalPath, err := filepath.EvalSymlinks(filepath.Join("/proc/self/fd/", strconv.Itoa(fd)))
finalPath, err := filepath.EvalSymlinks(filepath.Join("/proc/self/fd/", strconv.Itoa(dirfd))) if err != nil {
if err != nil { if !errors.Is(err, ErrNotExist) {
return fd, convertErrorType(err) return fd, fmt.Errorf("failed to evaluate symlink: %w", convertErrorType(err))
}
if err != nil {
if !errors.Is(err, ErrNotExist) {
return fd, fmt.Errorf("failed to evaluate symlink: %w", convertErrorType(err))
}
// The target of one of the symlinks (EvalSymlinks is recursive)
// does not exist. So get the path that does not exist and use
// that for further validation instead.
var pErr *PathError
if ok := errors.As(err, &pErr); !ok {
return fd, fmt.Errorf("failed to evaluate symlink: %w", convertErrorType(err))
}
finalPath = pErr.Path
} }
// Check if the path is within our root. // The target of one of the symlinks (EvalSymlinks is recursive)
if !fs.unsafeIsPathInsideOfBase(finalPath) { // does not exist. So get the path that does not exist and use
return fd, convertErrorType(&PathError{ // that for further validation instead.
Op: "openat", var pErr *PathError
Path: name, if !errors.As(err, &pErr) {
Err: ErrBadPathResolution, return fd, fmt.Errorf("failed to evaluate symlink: %w", convertErrorType(err))
}) }
// Update the final path to whatever directory or path didn't exist while
// recursing any symlinks.
finalPath = pErr.Path
// Ensure the error is wrapped correctly.
err = convertErrorType(err)
}
// Check if the path is within our root.
if !fs.unsafeIsPathInsideOfBase(finalPath) {
op := "openat"
if fs.useOpenat2 {
op = "openat2"
}
return fd, &PathError{
Op: op,
Path: name,
Err: ErrBadPathResolution,
} }
} }
return fd, nil
// Return the file descriptor and any potential error.
return fd, err
} }
// _openat is a wrapper around unix.Openat. This method should never be directly // _openat is a wrapper around unix.Openat. This method should never be directly
@ -683,11 +740,11 @@ func (fs *UnixFS) _openat(dirfd int, name string, flag int, mode uint32) (int, e
case err == nil: case err == nil:
return fd, nil return fd, nil
case err == unix.EINTR: case err == unix.EINTR:
return 0, err return fd, err
case err == unix.EAGAIN: case err == unix.EAGAIN:
return 0, err return fd, err
default: default:
return 0, &PathError{Op: "openat", Path: name, Err: err} return fd, ensurePathError(err, "openat", name)
} }
} }
@ -697,7 +754,7 @@ func (fs *UnixFS) _openat(dirfd int, name string, flag int, mode uint32) (int, e
// present in Kernel 5.6 and above. // present in Kernel 5.6 and above.
// //
// This method should never be directly called, use `openat` instead. // This method should never be directly called, use `openat` instead.
func (fs *UnixFS) _openat2(dirfd int, name string, flag uint64, mode uint64) (int, error) { func (fs *UnixFS) _openat2(dirfd int, name string, flag, mode uint64) (int, error) {
// Ensure the O_CLOEXEC flag is set. // Ensure the O_CLOEXEC flag is set.
// Go sets this when using the os package, but since we are directly using // Go sets this when using the os package, but since we are directly using
// the unix package we need to set it ourselves. // the unix package we need to set it ourselves.
@ -722,11 +779,11 @@ func (fs *UnixFS) _openat2(dirfd int, name string, flag uint64, mode uint64) (in
case err == nil: case err == nil:
return fd, nil return fd, nil
case err == unix.EINTR: case err == unix.EINTR:
return 0, err return fd, err
case err == unix.EAGAIN: case err == unix.EAGAIN:
return 0, err return fd, err
default: default:
return 0, &PathError{Op: "openat2", Path: name, Err: err} return fd, ensurePathError(err, "openat2", name)
} }
} }
@ -745,11 +802,11 @@ func (fs *UnixFS) safePath(path string) (dirfd int, file string, closeFd func(),
return return
} }
// Check if dirfd was closed, this will happen if (*UnixFS).Close() // Open the base path. We use this as the sandbox root for any further
// was called. // operations.
fsDirfd := int(fs.dirfd.Load()) var fsDirfd int
if fsDirfd == -1 { fsDirfd, err = fs._openat(AT_EMPTY_PATH, fs.basePath, O_DIRECTORY|O_RDONLY, 0)
err = ErrClosed if err != nil {
return return
} }
@ -759,9 +816,8 @@ func (fs *UnixFS) safePath(path string) (dirfd int, file string, closeFd func(),
dir, file = filepath.Split(name) dir, file = filepath.Split(name)
// If dir is empty then name is not nested. // If dir is empty then name is not nested.
if dir == "" { if dir == "" {
// We don't need to set closeFd here as it will default to a NO-OP and
// `fs.dirfd` is re-used until the filesystem is no-longer needed.
dirfd = fsDirfd dirfd = fsDirfd
closeFd = func() { _ = unix.Close(dirfd) }
// Return dirfd, name, an empty closeFd func, and no error // Return dirfd, name, an empty closeFd func, and no error
return return
@ -771,26 +827,36 @@ func (fs *UnixFS) safePath(path string) (dirfd int, file string, closeFd func(),
// trim slashes. // trim slashes.
dir = strings.TrimSuffix(dir, "/") dir = strings.TrimSuffix(dir, "/")
dirfd, err = fs.openat(fsDirfd, dir, O_DIRECTORY|O_RDONLY, 0) dirfd, err = fs.openat(fsDirfd, dir, O_DIRECTORY|O_RDONLY, 0)
if dirfd != 0 { if err != nil {
// An error occurred while opening the directory, but we already opened
// the filesystem root, so we still need to ensure it gets closed.
closeFd = func() { _ = unix.Close(fsDirfd) }
} else {
// Set closeFd to close the newly opened directory file descriptor. // Set closeFd to close the newly opened directory file descriptor.
closeFd = func() { _ = unix.Close(dirfd) } closeFd = func() {
_ = unix.Close(dirfd)
_ = unix.Close(fsDirfd)
}
} }
// Return dirfd, name, the closeFd func, and err // Return dirfd, name, the closeFd func, and err
return return
} }
// unsafePath prefixes the given path and prefixes it with the filesystem's // unsafePath strips and joins the given path with the filesystem's base path,
// base path, cleaning the result. The path returned by this function may not // cleaning the result. The cleaned path is then checked if it starts with the
// be inside the filesystem's base path, additional checks are required to // filesystem's base path to obvious any obvious path traversal escapes. The
// safely use paths returned by this function. // fully resolved path (if symlinks are followed) may not be within the
// filesystem's base path, additional checks are required to safely use paths
// returned by this function.
func (fs *UnixFS) unsafePath(path string) (string, error) { func (fs *UnixFS) unsafePath(path string) (string, error) {
// Calling filepath.Clean on the joined directory will resolve it to the // Calling filepath.Clean on the path will resolve it to it's absolute path,
// absolute path, removing any ../ type of resolution arguments, and leaving // removing any path traversal arguments (such as ..), leaving us with an
// us with a direct path link. // absolute path we can then use.
// //
// This will also trim the existing root path off the beginning of the path // This will also trim the filesystem's base path from the given path and
// passed to the function since that can get a bit messy. // join the base path back on to ensure the path starts with the base path
// without appending it twice.
r := filepath.Clean(filepath.Join(fs.basePath, strings.TrimPrefix(path, fs.basePath))) r := filepath.Clean(filepath.Join(fs.basePath, strings.TrimPrefix(path, fs.basePath)))
if fs.unsafeIsPathInsideOfBase(r) { if fs.unsafeIsPathInsideOfBase(r) {
@ -817,6 +883,10 @@ func (fs *UnixFS) unsafePath(path string) (string, error) {
// unsafeIsPathInsideOfBase checks if the given path is inside the filesystem's // unsafeIsPathInsideOfBase checks if the given path is inside the filesystem's
// base path. // base path.
//
// NOTE: this method doesn't clean the given path or attempt to join the
// filesystem's base path. This is purely a basic prefix check against the
// given path.
func (fs *UnixFS) unsafeIsPathInsideOfBase(path string) bool { func (fs *UnixFS) unsafeIsPathInsideOfBase(path string) bool {
return strings.HasPrefix( return strings.HasPrefix(
strings.TrimSuffix(path, "/")+"/", strings.TrimSuffix(path, "/")+"/",

View File

@ -9,6 +9,9 @@ import (
"errors" "errors"
"os" "os"
"path/filepath" "path/filepath"
"reflect"
"slices"
"strconv"
"testing" "testing"
"github.com/pterodactyl/wings/internal/ufs" "github.com/pterodactyl/wings/internal/ufs"
@ -35,8 +38,8 @@ func newTestUnixFS() (*testUnixFS, error) {
if err := os.Mkdir(root, 0o755); err != nil { if err := os.Mkdir(root, 0o755); err != nil {
return nil, err return nil, err
} }
// TODO: test both disabled and enabled. // fmt.Println(tmpDir)
fs, err := ufs.NewUnixFS(root, false) fs, err := ufs.NewUnixFS(root, true)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -48,6 +51,323 @@ func newTestUnixFS() (*testUnixFS, error) {
return tfs, nil return tfs, nil
} }
func TestUnixFS(t *testing.T) {
t.Parallel()
fs, err := newTestUnixFS()
if err != nil {
t.Fatal(err)
return
}
defer fs.Cleanup()
// Test creating a file within the root.
_, _, closeFd, err := fs.SafePath("/")
closeFd()
if err != nil {
t.Error(err)
return
}
f, err := fs.Touch("directory/file", ufs.O_RDWR, 0o644)
if err != nil {
t.Error(err)
return
}
_ = f.Close()
// Test creating a file within the root.
f, err = fs.Create("test")
if err != nil {
t.Error(err)
return
}
_ = f.Close()
// Test stating a file within the root.
if _, err := fs.Stat("test"); err != nil {
t.Error(err)
return
}
// Test creating a directory within the root.
if err := fs.Mkdir("ima_directory", 0o755); err != nil {
t.Error(err)
return
}
// Test creating a nested directory within the root.
if err := fs.Mkdir("ima_directory/ima_nother_directory", 0o755); err != nil {
t.Error(err)
return
}
// Test creating a file inside a directory within the root.
f, err = fs.Create("ima_directory/ima_file")
if err != nil {
t.Error(err)
return
}
_ = f.Close()
// Test listing directory entries.
if _, err := fs.ReadDir("ima_directory"); err != nil {
t.Error(err)
return
}
// Test symlink pointing outside the root.
if err := os.Symlink(fs.TmpDir, filepath.Join(fs.Root, "ima_bad_link")); err != nil {
t.Error(err)
return
}
f, err = fs.Create("ima_bad_link/ima_bad_file")
if err == nil {
_ = f.Close()
t.Error("expected an error")
return
}
if err := fs.Mkdir("ima_bad_link/ima_bad_directory", 0o755); err == nil {
t.Error("expected an error")
return
}
// Test symlink pointing outside the root inside a parent directory.
if err := fs.Symlink(fs.TmpDir, filepath.Join(fs.Root, "ima_directory/ima_bad_link")); err != nil {
t.Error(err)
return
}
if err := fs.Mkdir("ima_directory/ima_bad_link/ima_bad_directory", 0o755); err == nil {
t.Error("expected an error")
return
}
// Test symlink pointing outside the root with a child directory.
if err := os.Mkdir(filepath.Join(fs.TmpDir, "ima_directory"), 0o755); err != nil {
t.Error(err)
return
}
f, err = fs.Create("ima_bad_link/ima_directory/ima_bad_file")
if err == nil {
_ = f.Close()
t.Error("expected an error")
return
}
if err := fs.Mkdir("ima_bad_link/ima_directory/ima_bad_directory", 0o755); err == nil {
t.Error("expected an error")
return
}
if _, err := fs.ReadDir("ima_bad_link/ima_directory"); err == nil {
t.Error("expected an error")
return
}
// Create multiple nested directories.
if err := fs.MkdirAll("ima_directory/ima_directory/ima_directory/ima_directory", 0o755); err != nil {
t.Error(err)
return
}
if _, err := fs.ReadDir("ima_directory/ima_directory"); err != nil {
t.Error(err)
return
}
// Test creating a directory under a symlink with a pre-existing directory.
if err := fs.MkdirAll("ima_bad_link/ima_directory/ima_bad_directory/ima_bad_directory", 0o755); err == nil {
t.Error("expected an error")
return
}
// Test deletion
if err := fs.Remove("test"); err != nil {
t.Error(err)
return
}
if err := fs.Remove("ima_bad_link"); err != nil {
t.Error(err)
return
}
// Test recursive deletion
if err := fs.RemoveAll("ima_directory"); err != nil {
t.Error(err)
return
}
// Test recursive deletion underneath a bad symlink
if err := fs.Mkdir("ima_directory", 0o755); err != nil {
t.Error(err)
return
}
if err := fs.Symlink(fs.TmpDir, filepath.Join(fs.Root, "ima_directory/ima_bad_link")); err != nil {
t.Error(err)
return
}
if err := fs.RemoveAll("ima_directory/ima_bad_link/ima_bad_file"); err == nil {
t.Error("expected an error")
return
}
// This should delete the symlink itself.
if err := fs.RemoveAll("ima_directory/ima_bad_link"); err != nil {
t.Error(err)
return
}
//for i := 0; i < 5; i++ {
// dirName := "dir" + strconv.Itoa(i)
// if err := fs.Mkdir(dirName, 0o755); err != nil {
// t.Error(err)
// return
// }
// for j := 0; j < 5; j++ {
// f, err := fs.Create(filepath.Join(dirName, "file"+strconv.Itoa(j)))
// if err != nil {
// t.Error(err)
// return
// }
// _ = f.Close()
// }
//}
//
//if err := fs.WalkDir2("", func(fd int, path string, info filesystem.DirEntry, err error) error {
// if err != nil {
// return err
// }
// fmt.Println(path)
// return nil
//}); err != nil {
// t.Error(err)
// return
//}
}
func TestUnixFS_Chmod(t *testing.T) {
t.Parallel()
fs, err := newTestUnixFS()
if err != nil {
t.Fatal(err)
return
}
defer fs.Cleanup()
// TODO: implement
}
func TestUnixFS_Chown(t *testing.T) {
t.Parallel()
fs, err := newTestUnixFS()
if err != nil {
t.Fatal(err)
return
}
defer fs.Cleanup()
// TODO: implement
}
func TestUnixFS_Lchown(t *testing.T) {
t.Parallel()
fs, err := newTestUnixFS()
if err != nil {
t.Fatal(err)
return
}
defer fs.Cleanup()
// TODO: implement
}
func TestUnixFS_Chtimes(t *testing.T) {
t.Parallel()
fs, err := newTestUnixFS()
if err != nil {
t.Fatal(err)
return
}
defer fs.Cleanup()
// TODO: implement
}
func TestUnixFS_Create(t *testing.T) {
t.Parallel()
fs, err := newTestUnixFS()
if err != nil {
t.Fatal(err)
return
}
defer fs.Cleanup()
// TODO: implement
}
func TestUnixFS_Mkdir(t *testing.T) {
t.Parallel()
fs, err := newTestUnixFS()
if err != nil {
t.Fatal(err)
return
}
defer fs.Cleanup()
// TODO: implement
}
func TestUnixFS_MkdirAll(t *testing.T) {
t.Parallel()
fs, err := newTestUnixFS()
if err != nil {
t.Fatal(err)
return
}
defer fs.Cleanup()
if err := fs.MkdirAll("/a/bunch/of/directories", 0o755); err != nil {
t.Error(err)
return
}
// TODO: stat sanity check
}
func TestUnixFS_Open(t *testing.T) {
t.Parallel()
fs, err := newTestUnixFS()
if err != nil {
t.Fatal(err)
return
}
defer fs.Cleanup()
// TODO: implement
}
func TestUnixFS_OpenFile(t *testing.T) {
t.Parallel()
fs, err := newTestUnixFS()
if err != nil {
t.Fatal(err)
return
}
defer fs.Cleanup()
// TODO: implement
}
func TestUnixFS_ReadDir(t *testing.T) {
t.Parallel()
fs, err := newTestUnixFS()
if err != nil {
t.Fatal(err)
return
}
defer fs.Cleanup()
// TODO: implement
}
func TestUnixFS_Remove(t *testing.T) { func TestUnixFS_Remove(t *testing.T) {
t.Parallel() t.Parallel()
fs, err := newTestUnixFS() fs, err := newTestUnixFS()
@ -154,12 +474,12 @@ func TestUnixFS_Rename(t *testing.T) {
t.Run("file rename", func(t *testing.T) { t.Run("file rename", func(t *testing.T) {
// Create a directory to rename to something else. // Create a directory to rename to something else.
if f, err := fs.Create("test_file"); err != nil { f, err := fs.Create("test_file")
if err != nil {
t.Error(err) t.Error(err)
return return
} else {
_ = f.Close()
} }
_ = f.Close()
// Try to rename "test_file" to "file". // Try to rename "test_file" to "file".
if err := fs.Rename("test_file", "file"); err != nil { if err := fs.Rename("test_file", "file"); err != nil {
@ -175,6 +495,42 @@ func TestUnixFS_Rename(t *testing.T) {
}) })
} }
func TestUnixFS_Stat(t *testing.T) {
t.Parallel()
fs, err := newTestUnixFS()
if err != nil {
t.Fatal(err)
return
}
defer fs.Cleanup()
// TODO: implement
}
func TestUnixFS_Lstat(t *testing.T) {
t.Parallel()
fs, err := newTestUnixFS()
if err != nil {
t.Fatal(err)
return
}
defer fs.Cleanup()
// TODO: implement
}
func TestUnixFS_Symlink(t *testing.T) {
t.Parallel()
fs, err := newTestUnixFS()
if err != nil {
t.Fatal(err)
return
}
defer fs.Cleanup()
// TODO: implement
}
func TestUnixFS_Touch(t *testing.T) { func TestUnixFS_Touch(t *testing.T) {
t.Parallel() t.Parallel()
fs, err := newTestUnixFS() fs, err := newTestUnixFS()
@ -253,3 +609,160 @@ func TestUnixFS_Touch(t *testing.T) {
} }
}) })
} }
func TestUnixFS_WalkDir(t *testing.T) {
t.Parallel()
fs, err := newTestUnixFS()
if err != nil {
t.Fatal(err)
return
}
defer fs.Cleanup()
//for i := 0; i < 5; i++ {
// dirName := "dir" + strconv.Itoa(i)
// if err := fs.Mkdir(dirName, 0o755); err != nil {
// t.Error(err)
// return
// }
// for j := 0; j < 5; j++ {
// f, err := fs.Create(filepath.Join(dirName, "file"+strconv.Itoa(j)))
// if err != nil {
// t.Error(err)
// return
// }
// _ = f.Close()
// }
//}
//
//if err := fs.WalkDir(".", func(path string, info ufs.DirEntry, err error) error {
// if err != nil {
// return err
// }
// t.Log(path)
// return nil
//}); err != nil {
// t.Error(err)
// return
//}
}
func TestUnixFS_WalkDirat(t *testing.T) {
t.Parallel()
fs, err := newTestUnixFS()
if err != nil {
t.Fatal(err)
return
}
defer fs.Cleanup()
for i := 0; i < 2; i++ {
dirName := "base" + strconv.Itoa(i)
if err := fs.Mkdir(dirName, 0o755); err != nil {
t.Error(err)
return
}
for j := 0; j < 1; j++ {
f, err := fs.Create(filepath.Join(dirName, "file"+strconv.Itoa(j)))
if err != nil {
t.Error(err)
return
}
_ = f.Close()
if err := fs.Mkdir(filepath.Join(dirName, "dir"+strconv.Itoa(j)), 0o755); err != nil {
t.Error(err)
return
}
f, err = fs.Create(filepath.Join(dirName, "dir"+strconv.Itoa(j), "file"+strconv.Itoa(j)))
if err != nil {
t.Error(err)
return
}
_ = f.Close()
}
}
t.Run("walk starting at the filesystem root", func(t *testing.T) {
pathsTraversed, err := fs.testWalkDirAt("")
if err != nil {
t.Error(err)
return
}
expect := []Path{
{Name: ".", Relative: "."},
{Name: "base0", Relative: "base0"},
{Name: "dir0", Relative: "base0/dir0"},
{Name: "file0", Relative: "base0/dir0/file0"},
{Name: "file0", Relative: "base0/file0"},
{Name: "base1", Relative: "base1"},
{Name: "dir0", Relative: "base1/dir0"},
{Name: "file0", Relative: "base1/dir0/file0"},
{Name: "file0", Relative: "base1/file0"},
}
if !reflect.DeepEqual(pathsTraversed, expect) {
t.Log(pathsTraversed)
t.Log(expect)
t.Error("walk doesn't match")
return
}
})
t.Run("walk starting in a directory", func(t *testing.T) {
pathsTraversed, err := fs.testWalkDirAt("base0")
if err != nil {
t.Error(err)
return
}
expect := []Path{
// TODO: what should relative actually be here?
// The behaviour differs from walking the directory root vs a sub
// directory. When walking from the root, dirfd is the directory we
// are walking from and both name and relative are `.`. However,
// when walking from a subdirectory, fd is the parent of the
// subdirectory, and name is the subdirectory.
{Name: "base0", Relative: "."},
{Name: "dir0", Relative: "dir0"},
{Name: "file0", Relative: "dir0/file0"},
{Name: "file0", Relative: "file0"},
}
if !reflect.DeepEqual(pathsTraversed, expect) {
t.Log(pathsTraversed)
t.Log(expect)
t.Error("walk doesn't match")
return
}
})
}
type Path struct {
Name string
Relative string
}
func (fs *testUnixFS) testWalkDirAt(path string) ([]Path, error) {
dirfd, name, closeFd, err := fs.SafePath(path)
defer closeFd()
if err != nil {
return nil, err
}
var pathsTraversed []Path
if err := fs.WalkDirat(dirfd, name, func(_ int, name, relative string, _ ufs.DirEntry, err error) error {
if err != nil {
return err
}
pathsTraversed = append(pathsTraversed, Path{Name: name, Relative: relative})
return nil
}); err != nil {
return nil, err
}
slices.SortStableFunc(pathsTraversed, func(a, b Path) int {
if a.Relative > b.Relative {
return 1
}
if a.Relative < b.Relative {
return -1
}
return 0
})
return pathsTraversed, nil
}

View File

@ -10,10 +10,6 @@
package ufs package ufs
import (
"golang.org/x/sys/unix"
)
// mkdirAll is a recursive Mkdir implementation that properly handles symlinks. // mkdirAll is a recursive Mkdir implementation that properly handles symlinks.
func (fs *UnixFS) mkdirAll(name string, mode FileMode) error { func (fs *UnixFS) mkdirAll(name string, mode FileMode) error {
// Fast path: if we can tell whether path is a directory or file, stop with success or error. // Fast path: if we can tell whether path is a directory or file, stop with success or error.
@ -30,7 +26,7 @@ func (fs *UnixFS) mkdirAll(name string, mode FileMode) error {
if dir.IsDir() { if dir.IsDir() {
return nil return nil
} }
return convertErrorType(&PathError{Op: "mkdir", Path: name, Err: unix.ENOTDIR}) return &PathError{Op: "mkdir", Path: name, Err: ErrNotDirectory}
} }
// Slow path: make sure parent exists and then call Mkdir for path. // Slow path: make sure parent exists and then call Mkdir for path.

View File

@ -4,7 +4,6 @@
package ufs package ufs
import ( import (
"errors"
"io" "io"
"sync/atomic" "sync/atomic"
) )
@ -33,7 +32,7 @@ func (w *CountedWriter) BytesWritten() int64 {
// Error returns the error from the writer if any. If the error is an EOF, nil // Error returns the error from the writer if any. If the error is an EOF, nil
// will be returned. // will be returned.
func (w *CountedWriter) Error() error { func (w *CountedWriter) Error() error {
if errors.Is(w.err, io.EOF) { if w.err == io.EOF {
return nil return nil
} }
return w.err return w.err
@ -54,9 +53,8 @@ func (w *CountedWriter) Write(p []byte) (int, error) {
// TODO: is this how we actually want to handle errors with this? // TODO: is this how we actually want to handle errors with this?
if err == io.EOF { if err == io.EOF {
return n, io.EOF return n, io.EOF
} else {
return n, nil
} }
return n, nil
} }
func (w *CountedWriter) ReadFrom(r io.Reader) (n int64, err error) { func (w *CountedWriter) ReadFrom(r io.Reader) (n int64, err error) {
@ -92,7 +90,7 @@ func (r *CountedReader) BytesRead() int64 {
// Error returns the error from the reader if any. If the error is an EOF, nil // Error returns the error from the reader if any. If the error is an EOF, nil
// will be returned. // will be returned.
func (r *CountedReader) Error() error { func (r *CountedReader) Error() error {
if errors.Is(r.err, io.EOF) { if r.err == io.EOF {
return nil return nil
} }
return r.err return r.err
@ -109,9 +107,9 @@ func (r *CountedReader) Read(p []byte) (int, error) {
r.counter.Add(int64(n)) r.counter.Add(int64(n))
r.err = err r.err = err
// TODO: is this how we actually want to handle errors with this?
if err == io.EOF { if err == io.EOF {
return n, io.EOF return n, io.EOF
} else {
return n, nil
} }
return n, nil
} }

View File

@ -52,60 +52,69 @@ func removeAll(fs unixFS, path string) error {
parentDir, base := splitPath(path) parentDir, base := splitPath(path)
parent, err := fs.Open(parentDir) parent, err := fs.Open(parentDir)
if errors.Is(err, ErrNotExist) { if err != nil {
if !errors.Is(err, ErrNotExist) {
return err
}
// If parent does not exist, base cannot exist. Fail silently // If parent does not exist, base cannot exist. Fail silently
return nil return nil
} }
if err != nil {
return err
}
defer parent.Close() defer parent.Close()
if err := removeAllFrom(fs, parent, base); err != nil { if err := removeAllFrom(fs, parent, base); err != nil {
if pathErr, ok := err.(*PathError); ok { if pathErr, ok := err.(*PathError); ok {
pathErr.Path = parentDir + string(os.PathSeparator) + pathErr.Path pathErr.Path = parentDir + string(os.PathSeparator) + pathErr.Path
err = pathErr err = convertErrorType(pathErr)
} else {
err = ensurePathError(err, "removeallfrom", base)
} }
return convertErrorType(err) return err
} }
return nil return nil
} }
func removeAllFrom(fs unixFS, parent File, base string) error { func (fs *UnixFS) removeContents(path string) error {
parentFd := int(parent.Fd()) return removeContents(fs, path)
// Simple case: if Unlink (aka remove) works, we're done. }
err := fs.unlinkat(parentFd, base, 0)
if err == nil || errors.Is(err, ErrNotExist) { func removeContents(fs unixFS, path string) error {
if path == "" {
// fail silently to retain compatibility with previous behavior
// of RemoveAll. See issue https://go.dev/issue/28830.
return nil return nil
} }
// EISDIR means that we have a directory, and we need to // RemoveAll recurses by deleting the path base from
// remove its contents. // its parent directory
// EPERM or EACCES means that we don't have write permission on parentDir, base := splitPath(path)
// the parent directory, but this entry might still be a directory
// whose contents need to be removed.
// Otherwise, just return the error.
if err != unix.EISDIR && err != unix.EPERM && err != unix.EACCES {
return &PathError{Op: "unlinkat", Path: base, Err: err}
}
// Is this a directory we need to recurse into? parent, err := fs.Open(parentDir)
var statInfo unix.Stat_t if err != nil {
statErr := ignoringEINTR(func() error { if !errors.Is(err, ErrNotExist) {
return unix.Fstatat(parentFd, base, &statInfo, AT_SYMLINK_NOFOLLOW) return err
})
if statErr != nil {
if errors.Is(statErr, ErrNotExist) {
return nil
} }
return &PathError{Op: "fstatat", Path: base, Err: statErr} // If parent does not exist, base cannot exist. Fail silently
} return nil
if statInfo.Mode&unix.S_IFMT != unix.S_IFDIR {
// Not a directory; return the error from the unix.Unlinkat.
return &PathError{Op: "unlinkat", Path: base, Err: err}
} }
defer parent.Close()
if err := removeContentsFrom(fs, parent, base); err != nil {
if pathErr, ok := err.(*PathError); ok {
pathErr.Path = parentDir + string(os.PathSeparator) + pathErr.Path
err = convertErrorType(pathErr)
} else {
err = ensurePathError(err, "removecontentsfrom", base)
}
return err
}
return nil
}
// removeContentsFrom recursively removes all descendants of parent without
// removing parent itself. Parent must be a directory.
func removeContentsFrom(fs unixFS, parent File, base string) error {
parentFd := int(parent.Fd())
// Remove the directory's entries.
var recurseErr error var recurseErr error
for { for {
const reqSize = 1024 const reqSize = 1024
@ -168,6 +177,50 @@ func removeAllFrom(fs unixFS, parent File, base string) error {
} }
} }
return nil
}
func removeAllFrom(fs unixFS, parent File, base string) error {
parentFd := int(parent.Fd())
// Simple case: if Unlink (aka remove) works, we're done.
err := fs.unlinkat(parentFd, base, 0)
if err == nil || errors.Is(err, ErrNotExist) {
return nil
}
// EISDIR means that we have a directory, and we need to
// remove its contents.
// EPERM or EACCES means that we don't have write permission on
// the parent directory, but this entry might still be a directory
// whose contents need to be removed.
// Otherwise, just return the error.
if err != unix.EISDIR && err != unix.EPERM && err != unix.EACCES {
return &PathError{Op: "unlinkat", Path: base, Err: err}
}
// Is this a directory we need to recurse into?
var statInfo unix.Stat_t
statErr := ignoringEINTR(func() error {
return unix.Fstatat(parentFd, base, &statInfo, AT_SYMLINK_NOFOLLOW)
})
if statErr != nil {
if errors.Is(statErr, ErrNotExist) {
return nil
}
return &PathError{Op: "fstatat", Path: base, Err: statErr}
}
if statInfo.Mode&unix.S_IFMT != unix.S_IFDIR {
// Not a directory; return the error from the unix.Unlinkat.
return &PathError{Op: "unlinkat", Path: base, Err: err}
}
// Remove all contents will remove the contents of the directory.
//
// It was split out of this function to allow the deletion of the
// contents of a directory, without deleting the directory itself.
recurseErr := removeContentsFrom(fs, parent, base)
// Remove the directory itself. // Remove the directory itself.
unlinkErr := fs.unlinkat(parentFd, base, AT_REMOVEDIR) unlinkErr := fs.unlinkat(parentFd, base, AT_REMOVEDIR)
if unlinkErr == nil || errors.Is(unlinkErr, ErrNotExist) { if unlinkErr == nil || errors.Is(unlinkErr, ErrNotExist) {
@ -177,7 +230,8 @@ func removeAllFrom(fs unixFS, parent File, base string) error {
if recurseErr != nil { if recurseErr != nil {
return recurseErr return recurseErr
} }
return &PathError{Op: "unlinkat", Path: base, Err: unlinkErr}
return ensurePathError(err, "unlinkat", base)
} }
// openFdAt opens path relative to the directory in fd. // openFdAt opens path relative to the directory in fd.

View File

@ -119,5 +119,6 @@ func walkDir(fs Filesystem, name string, d DirEntry, walkDirFn WalkDirFunc) erro
return err return err
} }
} }
return nil return nil
} }

View File

@ -8,10 +8,10 @@ package ufs
import ( import (
"bytes" "bytes"
"fmt"
iofs "io/fs" iofs "io/fs"
"os" "os"
"path" "path"
"path/filepath"
"reflect" "reflect"
"unsafe" "unsafe"
@ -21,16 +21,12 @@ import (
type WalkDiratFunc func(dirfd int, name, relative string, d DirEntry, err error) error type WalkDiratFunc func(dirfd int, name, relative string, d DirEntry, err error) error
func (fs *UnixFS) WalkDirat(dirfd int, name string, fn WalkDiratFunc) error { func (fs *UnixFS) WalkDirat(dirfd int, name string, fn WalkDiratFunc) error {
if dirfd == 0 {
// TODO: proper validation, ideally a dedicated function.
dirfd = int(fs.dirfd.Load())
}
info, err := fs.Lstatat(dirfd, name) info, err := fs.Lstatat(dirfd, name)
if err != nil { if err != nil {
err = fn(dirfd, name, name, nil, err) err = fn(dirfd, name, ".", nil, err)
} else { } else {
b := newScratchBuffer() b := newScratchBuffer()
err = fs.walkDir(b, dirfd, name, name, iofs.FileInfoToDirEntry(info), fn) err = fs.walkDir(b, dirfd, name, ".", iofs.FileInfoToDirEntry(info), fn)
} }
if err == SkipDir || err == SkipAll { if err == SkipDir || err == SkipAll {
return nil return nil
@ -48,12 +44,14 @@ func (fs *UnixFS) walkDir(b []byte, parentfd int, name, relative string, d DirEn
} }
dirfd, err := fs.openat(parentfd, name, O_DIRECTORY|O_RDONLY, 0) dirfd, err := fs.openat(parentfd, name, O_DIRECTORY|O_RDONLY, 0)
if dirfd != 0 {
defer unix.Close(dirfd)
}
if err != nil { if err != nil {
return err return err
} }
defer unix.Close(dirfd)
dirs, err := fs.readDir(dirfd, name, b) dirs, err := fs.readDir(dirfd, name, relative, b)
if err != nil { if err != nil {
// Second call, to report ReadDir error. // Second call, to report ReadDir error.
err = walkDirFn(dirfd, name, relative, d, err) err = walkDirFn(dirfd, name, relative, d, err)
@ -66,20 +64,28 @@ func (fs *UnixFS) walkDir(b []byte, parentfd int, name, relative string, d DirEn
} }
for _, d1 := range dirs { for _, d1 := range dirs {
// TODO: the path.Join on this line may actually be partially incorrect. name := d1.Name()
// If we are not walking starting at the root, relative will contain the // This fancy logic ensures that if we start walking from a subdirectory
// name of the directory we are starting the walk from, which will be // that we don't make the path relative to the root of the filesystem.
// relative to the root of the filesystem instead of from where the walk
// was initiated from.
// //
// ref; https://github.com/pterodactyl/panel/issues/5030 // For example, if we walk from the root of a filesystem, relative would
if err := fs.walkDir(b, dirfd, d1.Name(), path.Join(relative, d1.Name()), d1, walkDirFn); err != nil { // be "." and path.Join would end up just returning name. But if relative
// was a subdirectory, relative could be "dir" and path.Join would make
// it "dir/child" even though we are walking starting at dir.
var rel string
if relative == "." {
rel = name
} else {
rel = path.Join(relative, name)
}
if err := fs.walkDir(b, dirfd, name, rel, d1, walkDirFn); err != nil {
if err == SkipDir { if err == SkipDir {
break break
} }
return err return err
} }
} }
return nil return nil
} }
@ -97,7 +103,7 @@ func ReadDirMap[T any](fs *UnixFS, path string, fn func(DirEntry) (T, error)) ([
} }
defer unix.Close(fd) defer unix.Close(fd)
entries, err := fs.readDir(fd, ".", nil) entries, err := fs.readDir(fd, ".", path, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -112,6 +118,7 @@ func ReadDirMap[T any](fs *UnixFS, path string, fn func(DirEntry) (T, error)) ([
} }
out[idx] = v out[idx] = v
} }
return out, nil return out, nil
} }
@ -157,7 +164,7 @@ func nameFromDirent(de *unix.Dirent) (name []byte) {
// //
// When the syscall constant is not recognized, this function falls back to a // When the syscall constant is not recognized, this function falls back to a
// Stat on the file system. // Stat on the file system.
func (fs *UnixFS) modeTypeFromDirent(fd int, de *unix.Dirent, osDirname, osBasename string) (FileMode, error) { func (fs *UnixFS) modeTypeFromDirent(de *unix.Dirent, fd int, name string) (FileMode, error) {
switch de.Type { switch de.Type {
case unix.DT_REG: case unix.DT_REG:
return 0, nil return 0, nil
@ -176,7 +183,7 @@ func (fs *UnixFS) modeTypeFromDirent(fd int, de *unix.Dirent, osDirname, osBasen
default: default:
// If syscall returned unknown type (e.g., DT_UNKNOWN, DT_WHT), then // If syscall returned unknown type (e.g., DT_UNKNOWN, DT_WHT), then
// resolve actual mode by reading file information. // resolve actual mode by reading file information.
return fs.modeType(fd, filepath.Join(osDirname, osBasename)) return fs.modeType(fd, name)
} }
} }
@ -189,12 +196,12 @@ func (fs *UnixFS) modeTypeFromDirent(fd int, de *unix.Dirent, osDirname, osBasen
// from syscall or stat call. Therefore, mask out the additional file mode bits // from syscall or stat call. Therefore, mask out the additional file mode bits
// that are provided by stat but not by the syscall, so users can rely on their // that are provided by stat but not by the syscall, so users can rely on their
// values. // values.
func (fs *UnixFS) modeType(dirfd int, name string) (os.FileMode, error) { func (fs *UnixFS) modeType(dirfd int, name string) (FileMode, error) {
fi, err := fs.Lstatat(dirfd, name) fi, err := fs.Lstatat(dirfd, name)
if err == nil { if err != nil {
return fi.Mode() & ModeType, nil return 0, fmt.Errorf("ufs: error finding mode type for %s during readDir: %w", name, err)
} }
return 0, err return fi.Mode() & ModeType, nil
} }
var minimumScratchBufferSize = os.Getpagesize() var minimumScratchBufferSize = os.Getpagesize()
@ -203,7 +210,7 @@ func newScratchBuffer() []byte {
return make([]byte, minimumScratchBufferSize) return make([]byte, minimumScratchBufferSize)
} }
func (fs *UnixFS) readDir(fd int, name string, b []byte) ([]DirEntry, error) { func (fs *UnixFS) readDir(fd int, name, relative string, b []byte) ([]DirEntry, error) {
scratchBuffer := b scratchBuffer := b
if scratchBuffer == nil || len(scratchBuffer) < minimumScratchBufferSize { if scratchBuffer == nil || len(scratchBuffer) < minimumScratchBufferSize {
scratchBuffer = newScratchBuffer() scratchBuffer = newScratchBuffer()
@ -220,7 +227,7 @@ func (fs *UnixFS) readDir(fd int, name string, b []byte) ([]DirEntry, error) {
if err == unix.EINTR { if err == unix.EINTR {
continue continue
} }
return nil, convertErrorType(err) return nil, ensurePathError(err, "getdents", name)
} }
if n <= 0 { if n <= 0 {
// end of directory: normal exit // end of directory: normal exit
@ -245,23 +252,29 @@ func (fs *UnixFS) readDir(fd int, name string, b []byte) ([]DirEntry, error) {
} }
childName := string(nameSlice) childName := string(nameSlice)
mt, err := fs.modeTypeFromDirent(fd, &sde, name, childName) mt, err := fs.modeTypeFromDirent(&sde, fd, childName)
if err != nil { if err != nil {
return nil, convertErrorType(err) return nil, err
} }
entries = append(entries, &dirent{name: childName, path: name, modeType: mt, dirfd: fd, fs: fs}) var rel string
if relative == "." {
rel = name
} else {
rel = path.Join(relative, childName)
}
entries = append(entries, &dirent{dirfd: fd, name: childName, path: rel, modeType: mt, fs: fs})
} }
} }
// dirent stores the name and file system mode type of discovered file system // dirent stores the name and file system mode type of discovered file system
// entries. // entries.
type dirent struct { type dirent struct {
dirfd int
name string name string
path string path string
modeType FileMode modeType FileMode
dirfd int fs *UnixFS
fs *UnixFS
} }
func (de dirent) Name() string { func (de dirent) Name() string {
@ -281,6 +294,7 @@ func (de dirent) Info() (FileInfo, error) {
return nil, nil return nil, nil
} }
return de.fs.Lstatat(de.dirfd, de.name) return de.fs.Lstatat(de.dirfd, de.name)
// return de.fs.Lstat(de.path)
} }
func (de dirent) Open() (File, error) { func (de dirent) Open() (File, error) {
@ -288,6 +302,7 @@ func (de dirent) Open() (File, error) {
return nil, nil return nil, nil
} }
return de.fs.OpenFileat(de.dirfd, de.name, O_RDONLY, 0) return de.fs.OpenFileat(de.dirfd, de.name, O_RDONLY, 0)
// return de.fs.OpenFile(de.path, O_RDONLY, 0)
} }
// reset releases memory held by entry err and name, and resets mode type to 0. // reset releases memory held by entry err and name, and resets mode type to 0.
@ -295,4 +310,5 @@ func (de *dirent) reset() {
de.name = "" de.name = ""
de.path = "" de.path = ""
de.modeType = 0 de.modeType = 0
de.dirfd = 0
} }

View File

@ -3,6 +3,7 @@ package parser
import ( import (
"bufio" "bufio"
"bytes" "bytes"
"encoding/json"
"io" "io"
"strconv" "strconv"
"strings" "strings"
@ -11,7 +12,6 @@ import (
"github.com/apex/log" "github.com/apex/log"
"github.com/beevik/etree" "github.com/beevik/etree"
"github.com/buger/jsonparser" "github.com/buger/jsonparser"
"github.com/goccy/go-json"
"github.com/icza/dyno" "github.com/icza/dyno"
"github.com/magiconair/properties" "github.com/magiconair/properties"
"gopkg.in/ini.v1" "gopkg.in/ini.v1"
@ -191,7 +191,7 @@ func (cfr *ConfigurationFileReplacement) UnmarshalJSON(data []byte) error {
// Parse parses a given configuration file and updates all the values within // Parse parses a given configuration file and updates all the values within
// as defined in the API response from the Panel. // as defined in the API response from the Panel.
func (f *ConfigurationFile) Parse(file ufs.File) error { func (f *ConfigurationFile) Parse(file ufs.File) error {
//log.WithField("path", path).WithField("parser", f.Parser.String()).Debug("parsing server configuration file") // log.WithField("path", path).WithField("parser", f.Parser.String()).Debug("parsing server configuration file")
// What the fuck is going on here? // What the fuck is going on here?
if mb, err := json.Marshal(config.Get()); err != nil { if mb, err := json.Marshal(config.Get()); err != nil {

View File

@ -3,6 +3,7 @@ package remote
import ( import (
"bytes" "bytes"
"context" "context"
"encoding/json"
"fmt" "fmt"
"io" "io"
"net/http" "net/http"
@ -15,7 +16,6 @@ import (
"emperror.dev/errors" "emperror.dev/errors"
"github.com/apex/log" "github.com/apex/log"
"github.com/cenkalti/backoff/v4" "github.com/cenkalti/backoff/v4"
"github.com/goccy/go-json"
"github.com/pterodactyl/wings/system" "github.com/pterodactyl/wings/system"
) )

View File

@ -2,11 +2,11 @@ package remote
import ( import (
"bytes" "bytes"
"encoding/json"
"regexp" "regexp"
"strings" "strings"
"github.com/apex/log" "github.com/apex/log"
"github.com/goccy/go-json"
"github.com/pterodactyl/wings/parser" "github.com/pterodactyl/wings/parser"
) )

View File

@ -2,6 +2,7 @@ package downloader
import ( import (
"context" "context"
"encoding/json"
"fmt" "fmt"
"io" "io"
"mime" "mime"
@ -14,7 +15,6 @@ import (
"time" "time"
"emperror.dev/errors" "emperror.dev/errors"
"github.com/goccy/go-json"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/pterodactyl/wings/server" "github.com/pterodactyl/wings/server"

View File

@ -168,7 +168,6 @@ func RequireAuthorization() gin.HandlerFunc {
// We don't put this value outside this function since the node's authentication // We don't put this value outside this function since the node's authentication
// token can be changed on the fly and the config.Get() call returns a copy, so // token can be changed on the fly and the config.Get() call returns a copy, so
// if it is rotated this value will never properly get updated. // if it is rotated this value will never properly get updated.
token := config.Get().AuthenticationToken
auth := strings.SplitN(c.GetHeader("Authorization"), " ", 2) auth := strings.SplitN(c.GetHeader("Authorization"), " ", 2)
if len(auth) != 2 || auth[0] != "Bearer" { if len(auth) != 2 || auth[0] != "Bearer" {
c.Header("WWW-Authenticate", "Bearer") c.Header("WWW-Authenticate", "Bearer")
@ -179,7 +178,7 @@ func RequireAuthorization() gin.HandlerFunc {
// All requests to Wings must be authorized with the authentication token present in // All requests to Wings must be authorized with the authentication token present in
// the Wings configuration file. Remeber, all requests to Wings come from the Panel // the Wings configuration file. Remeber, all requests to Wings come from the Panel
// backend, or using a signed JWT for temporary authentication. // backend, or using a signed JWT for temporary authentication.
if subtle.ConstantTimeCompare([]byte(auth[1]), []byte(token)) != 1 { if subtle.ConstantTimeCompare([]byte(auth[1]), []byte(config.Get().Token.Token)) != 1 {
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"error": "You are not authorized to access this endpoint."}) c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"error": "You are not authorized to access this endpoint."})
return return
} }

View File

@ -2,10 +2,10 @@ package router
import ( import (
"context" "context"
"encoding/json"
"time" "time"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/goccy/go-json"
ws "github.com/gorilla/websocket" ws "github.com/gorilla/websocket"
"github.com/pterodactyl/wings/router/middleware" "github.com/pterodactyl/wings/router/middleware"

View File

@ -2,11 +2,11 @@ package websocket
import ( import (
"context" "context"
"encoding/json"
"sync" "sync"
"time" "time"
"emperror.dev/errors" "emperror.dev/errors"
"github.com/goccy/go-json"
"github.com/pterodactyl/wings/events" "github.com/pterodactyl/wings/events"
"github.com/pterodactyl/wings/system" "github.com/pterodactyl/wings/system"

View File

@ -2,6 +2,7 @@ package websocket
import ( import (
"context" "context"
"encoding/json"
"fmt" "fmt"
"net/http" "net/http"
"strings" "strings"
@ -14,7 +15,6 @@ import (
"github.com/apex/log" "github.com/apex/log"
"github.com/gbrlsnchs/jwt/v3" "github.com/gbrlsnchs/jwt/v3"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/goccy/go-json"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/gorilla/websocket" "github.com/gorilla/websocket"

View File

@ -11,7 +11,7 @@ import (
"emperror.dev/errors" "emperror.dev/errors"
"github.com/apex/log" "github.com/apex/log"
"github.com/mholt/archiver/v4" "github.com/mholt/archives"
"golang.org/x/sync/errgroup" "golang.org/x/sync/errgroup"
"github.com/pterodactyl/wings/config" "github.com/pterodactyl/wings/config"
@ -19,9 +19,10 @@ import (
"github.com/pterodactyl/wings/server/filesystem" "github.com/pterodactyl/wings/server/filesystem"
) )
var format = archiver.CompressedArchive{ var format = archives.CompressedArchive{
Compression: archiver.Gz{}, Compression: archives.Gz{},
Archival: archiver.Tar{}, Archival: archives.Tar{},
Extraction: archives.Tar{},
} }
type AdapterType string type AdapterType string

View File

@ -7,7 +7,7 @@ import (
"emperror.dev/errors" "emperror.dev/errors"
"github.com/juju/ratelimit" "github.com/juju/ratelimit"
"github.com/mholt/archiver/v4" "github.com/mholt/archives"
"github.com/pterodactyl/wings/config" "github.com/pterodactyl/wings/config"
"github.com/pterodactyl/wings/remote" "github.com/pterodactyl/wings/remote"
@ -93,7 +93,7 @@ func (b *LocalBackup) Restore(ctx context.Context, _ io.Reader, callback Restore
if writeLimit := int64(config.Get().System.Backups.WriteLimit * 1024 * 1024); writeLimit > 0 { if writeLimit := int64(config.Get().System.Backups.WriteLimit * 1024 * 1024); writeLimit > 0 {
reader = ratelimit.Reader(f, ratelimit.NewBucketWithRate(float64(writeLimit), writeLimit)) reader = ratelimit.Reader(f, ratelimit.NewBucketWithRate(float64(writeLimit), writeLimit))
} }
if err := format.Extract(ctx, reader, nil, func(ctx context.Context, f archiver.File) error { if err := format.Extract(ctx, reader, func(ctx context.Context, f archives.FileInfo) error {
r, err := f.Open() r, err := f.Open()
if err != nil { if err != nil {
return err return err

View File

@ -12,7 +12,7 @@ import (
"emperror.dev/errors" "emperror.dev/errors"
"github.com/cenkalti/backoff/v4" "github.com/cenkalti/backoff/v4"
"github.com/juju/ratelimit" "github.com/juju/ratelimit"
"github.com/mholt/archiver/v4" "github.com/mholt/archives"
"github.com/pterodactyl/wings/config" "github.com/pterodactyl/wings/config"
"github.com/pterodactyl/wings/remote" "github.com/pterodactyl/wings/remote"
@ -93,7 +93,7 @@ func (s *S3Backup) Restore(ctx context.Context, r io.Reader, callback RestoreCal
if writeLimit := int64(config.Get().System.Backups.WriteLimit * 1024 * 1024); writeLimit > 0 { if writeLimit := int64(config.Get().System.Backups.WriteLimit * 1024 * 1024); writeLimit > 0 {
reader = ratelimit.Reader(r, ratelimit.NewBucketWithRate(float64(writeLimit), writeLimit)) reader = ratelimit.Reader(r, ratelimit.NewBucketWithRate(float64(writeLimit), writeLimit))
} }
if err := format.Extract(ctx, reader, nil, func(ctx context.Context, f archiver.File) error { if err := format.Extract(ctx, reader, func(ctx context.Context, f archives.FileInfo) error {
r, err := f.Open() r, err := f.Open()
if err != nil { if err != nil {
return err return err
@ -231,7 +231,6 @@ func (fu *s3FileUploader) uploadPart(ctx context.Context, part string, size int6
return nil return nil
}, fu.backoff(ctx)) }, fu.backoff(ctx))
if err != nil { if err != nil {
if v, ok := err.(*backoff.PermanentError); ok { if v, ok := err.(*backoff.PermanentError); ok {
return "", v.Unwrap() return "", v.Unwrap()

View File

@ -10,7 +10,7 @@ import (
"testing" "testing"
. "github.com/franela/goblin" . "github.com/franela/goblin"
"github.com/mholt/archiver/v4" "github.com/mholt/archives"
) )
func TestArchive_Stream(t *testing.T) { func TestArchive_Stream(t *testing.T) {
@ -60,11 +60,11 @@ func TestArchive_Stream(t *testing.T) {
g.Assert(err).IsNil() g.Assert(err).IsNil()
// Open the archive. // Open the archive.
genericFs, err := archiver.FileSystem(context.Background(), archivePath) genericFs, err := archives.FileSystem(context.Background(), archivePath, nil)
g.Assert(err).IsNil() g.Assert(err).IsNil()
// Assert that we are opening an archive. // Assert that we are opening an archive.
afs, ok := genericFs.(archiver.ArchiveFS) afs, ok := genericFs.(iofs.ReadDirFS)
g.Assert(ok).IsTrue() g.Assert(ok).IsTrue()
// Get the names of the files recursively from the archive. // Get the names of the files recursively from the archive.

View File

@ -13,7 +13,7 @@ import (
"io" "io"
"io/fs" "io/fs"
"github.com/mholt/archiver/v4" "github.com/mholt/archives"
) )
// FileFS allows accessing a file on disk using a consistent file system interface. // FileFS allows accessing a file on disk using a consistent file system interface.
@ -29,7 +29,7 @@ type FileFS struct {
// If file is compressed, setting this field will // If file is compressed, setting this field will
// transparently decompress reads. // transparently decompress reads.
Compression archiver.Decompressor Compression archives.Decompressor
} }
// Open opens the named file, which must be the file used to create the file system. // Open opens the named file, which must be the file used to create the file system.

View File

@ -13,7 +13,7 @@ import (
"emperror.dev/errors" "emperror.dev/errors"
"github.com/klauspost/compress/zip" "github.com/klauspost/compress/zip"
"github.com/mholt/archiver/v4" "github.com/mholt/archives"
"github.com/pterodactyl/wings/internal/ufs" "github.com/pterodactyl/wings/internal/ufs"
"github.com/pterodactyl/wings/server/filesystem/archiverext" "github.com/pterodactyl/wings/server/filesystem/archiverext"
@ -58,8 +58,8 @@ func (fs *Filesystem) archiverFileSystem(ctx context.Context, p string) (iofs.FS
} }
// Do not use defer to close `f`, it will likely be used later. // Do not use defer to close `f`, it will likely be used later.
format, _, err := archiver.Identify(filepath.Base(p), f) format, _, err := archives.Identify(ctx, filepath.Base(p), f)
if err != nil && !errors.Is(err, archiver.ErrNoMatch) { if err != nil && !errors.Is(err, archives.NoMatch) {
_ = f.Close() _ = f.Close()
return nil, err return nil, err
} }
@ -78,20 +78,20 @@ func (fs *Filesystem) archiverFileSystem(ctx context.Context, p string) (iofs.FS
if format != nil { if format != nil {
switch ff := format.(type) { switch ff := format.(type) {
case archiver.Zip: case archives.Zip:
// zip.Reader is more performant than ArchiveFS, because zip.Reader caches content information // zip.Reader is more performant than ArchiveFS, because zip.Reader caches content information
// and zip.Reader can open several content files concurrently because of io.ReaderAt requirement // and zip.Reader can open several content files concurrently because of io.ReaderAt requirement
// while ArchiveFS can't. // while ArchiveFS can't.
// zip.Reader doesn't suffer from issue #330 and #310 according to local test (but they should be fixed anyway) // zip.Reader doesn't suffer from issue #330 and #310 according to local test (but they should be fixed anyway)
return zip.NewReader(f, info.Size()) return zip.NewReader(f, info.Size())
case archiver.Archival: case archives.Extraction:
return archiver.ArchiveFS{Stream: io.NewSectionReader(f, 0, info.Size()), Format: ff, Context: ctx}, nil return &archives.ArchiveFS{Stream: io.NewSectionReader(f, 0, info.Size()), Format: ff, Context: ctx}, nil
case archiver.Compression: case archives.Compression:
return archiverext.FileFS{File: f, Compression: ff}, nil return archiverext.FileFS{File: f, Compression: ff}, nil
} }
} }
_ = f.Close() _ = f.Close()
return nil, archiver.ErrNoMatch return nil, archives.NoMatch
} }
// SpaceAvailableForDecompression looks through a given archive and determines // SpaceAvailableForDecompression looks through a given archive and determines
@ -105,7 +105,7 @@ func (fs *Filesystem) SpaceAvailableForDecompression(ctx context.Context, dir st
fsys, err := fs.archiverFileSystem(ctx, filepath.Join(dir, file)) fsys, err := fs.archiverFileSystem(ctx, filepath.Join(dir, file))
if err != nil { if err != nil {
if errors.Is(err, archiver.ErrNoMatch) { if errors.Is(err, archives.NoMatch) {
return newFilesystemError(ErrCodeUnknownArchive, err) return newFilesystemError(ErrCodeUnknownArchive, err)
} }
return err return err
@ -147,9 +147,9 @@ func (fs *Filesystem) DecompressFile(ctx context.Context, dir string, file strin
defer f.Close() defer f.Close()
// Identify the type of archive we are dealing with. // Identify the type of archive we are dealing with.
format, input, err := archiver.Identify(filepath.Base(file), f) format, input, err := archives.Identify(ctx, filepath.Base(file), f)
if err != nil { if err != nil {
if errors.Is(err, archiver.ErrNoMatch) { if errors.Is(err, archives.NoMatch) {
return newFilesystemError(ErrCodeUnknownArchive, err) return newFilesystemError(ErrCodeUnknownArchive, err)
} }
return err return err
@ -165,9 +165,9 @@ func (fs *Filesystem) DecompressFile(ctx context.Context, dir string, file strin
// ExtractStreamUnsafe . // ExtractStreamUnsafe .
func (fs *Filesystem) ExtractStreamUnsafe(ctx context.Context, dir string, r io.Reader) error { func (fs *Filesystem) ExtractStreamUnsafe(ctx context.Context, dir string, r io.Reader) error {
format, input, err := archiver.Identify("archive.tar.gz", r) format, input, err := archives.Identify(ctx, "archive.tar.gz", r)
if err != nil { if err != nil {
if errors.Is(err, archiver.ErrNoMatch) { if errors.Is(err, archives.NoMatch) {
return newFilesystemError(ErrCodeUnknownArchive, err) return newFilesystemError(ErrCodeUnknownArchive, err)
} }
return err return err
@ -185,26 +185,24 @@ type extractStreamOptions struct {
// File name of the archive. // File name of the archive.
FileName string FileName string
// Format of the archive. // Format of the archive.
Format archiver.Format Format archives.Format
// Reader for the archive. // Reader for the archive.
Reader io.Reader Reader io.Reader
} }
func (fs *Filesystem) extractStream(ctx context.Context, opts extractStreamOptions) error { func (fs *Filesystem) extractStream(ctx context.Context, opts extractStreamOptions) error {
// See if it's a compressed archive, such as TAR or a ZIP // See if it's a compressed archive, such as TAR or a ZIP
ex, ok := opts.Format.(archiver.Extractor) ex, ok := opts.Format.(archives.Extractor)
if !ok { if !ok {
// If not, check if it's a single-file compression, such as // If not, check if it's a single-file compression, such as
// .log.gz, .sql.gz, and so on // .log.gz, .sql.gz, and so on
de, ok := opts.Format.(archiver.Decompressor) de, ok := opts.Format.(archives.Decompressor)
if !ok { if !ok {
return nil return nil
} }
// Strip the compression suffix // Strip the compression suffix
p := filepath.Join(opts.Directory, strings.TrimSuffix(opts.FileName, opts.Format.Name())) p := filepath.Join(opts.Directory, strings.TrimSuffix(opts.FileName, opts.Format.Extension()))
// Make sure it's not ignored // Make sure it's not ignored
if err := fs.IsIgnored(p); err != nil { if err := fs.IsIgnored(p); err != nil {
@ -259,7 +257,7 @@ func (fs *Filesystem) extractStream(ctx context.Context, opts extractStreamOptio
} }
// Decompress and extract archive // Decompress and extract archive
return ex.Extract(ctx, opts.Reader, nil, func(ctx context.Context, f archiver.File) error { return ex.Extract(ctx, opts.Reader, func(ctx context.Context, f archives.FileInfo) error {
if f.IsDir() { if f.IsDir() {
return nil return nil
} }

View File

@ -1,6 +1,8 @@
package filesystem package filesystem
import ( import (
"golang.org/x/sys/unix"
"slices"
"sync" "sync"
"sync/atomic" "sync/atomic"
"time" "time"
@ -164,6 +166,8 @@ func (fs *Filesystem) DirectorySize(root string) (int64, error) {
return 0, err return 0, err
} }
var hardLinks []uint64
var size atomic.Int64 var size atomic.Int64
err = fs.unixFS.WalkDirat(dirfd, name, func(dirfd int, name, _ string, d ufs.DirEntry, err error) error { err = fs.unixFS.WalkDirat(dirfd, name, func(dirfd int, name, _ string, d ufs.DirEntry, err error) error {
if err != nil { if err != nil {
@ -180,8 +184,16 @@ func (fs *Filesystem) DirectorySize(root string) (int64, error) {
return errors.Wrap(err, "lstatat err") return errors.Wrap(err, "lstatat err")
} }
// TODO: detect if info is a hard-link and de-duplicate it. var sysFileInfo = info.Sys().(*unix.Stat_t)
// ref; https://github.com/pterodactyl/wings/pull/181/files if sysFileInfo.Nlink > 1 {
// Hard links have the same inode number
if slices.Contains(hardLinks, sysFileInfo.Ino) {
// Don't add hard links size twice
return nil
} else {
hardLinks = append(hardLinks, sysFileInfo.Ino)
}
}
size.Add(info.Size()) size.Add(info.Size())
return nil return nil

View File

@ -480,9 +480,9 @@ func (fs *Filesystem) ListDirectory(p string) ([]Stat, error) {
case a.IsDir() && b.IsDir(): case a.IsDir() && b.IsDir():
return 0 return 0
case a.IsDir(): case a.IsDir():
return 1
default:
return -1 return -1
default:
return 1
} }
}) })

View File

@ -38,7 +38,7 @@ func (s *Stat) MarshalJSON() ([]byte, error) {
Size: s.Size(), Size: s.Size(),
Directory: s.IsDir(), Directory: s.IsDir(),
File: !s.IsDir(), File: !s.IsDir(),
Symlink: s.Mode().Perm()&ufs.ModeSymlink != 0, Symlink: s.Mode().Type()&ufs.ModeSymlink != 0,
Mime: s.Mimetype, Mime: s.Mimetype,
}) })
} }

View File

@ -13,8 +13,8 @@ import (
"emperror.dev/errors" "emperror.dev/errors"
"github.com/apex/log" "github.com/apex/log"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/image"
"github.com/docker/docker/api/types/mount" "github.com/docker/docker/api/types/mount"
"github.com/docker/docker/client" "github.com/docker/docker/client"
@ -161,7 +161,7 @@ func (s *Server) SetRestoring(state bool) {
// RemoveContainer removes the installation container for the server. // RemoveContainer removes the installation container for the server.
func (ip *InstallationProcess) RemoveContainer() error { func (ip *InstallationProcess) RemoveContainer() error {
err := ip.client.ContainerRemove(ip.Server.Context(), ip.Server.ID()+"_installer", types.ContainerRemoveOptions{ err := ip.client.ContainerRemove(ip.Server.Context(), ip.Server.ID()+"_installer", container.RemoveOptions{
RemoveVolumes: true, RemoveVolumes: true,
Force: true, Force: true,
}) })
@ -247,7 +247,7 @@ func (ip *InstallationProcess) pullInstallationImage() error {
} }
// Get the ImagePullOptions. // Get the ImagePullOptions.
imagePullOptions := types.ImagePullOptions{All: false} imagePullOptions := image.PullOptions{All: false}
if registryAuth != nil { if registryAuth != nil {
b64, err := registryAuth.Base64() b64, err := registryAuth.Base64()
if err != nil { if err != nil {
@ -260,7 +260,7 @@ func (ip *InstallationProcess) pullInstallationImage() error {
r, err := ip.client.ImagePull(ip.Server.Context(), ip.Script.ContainerImage, imagePullOptions) r, err := ip.client.ImagePull(ip.Server.Context(), ip.Script.ContainerImage, imagePullOptions)
if err != nil { if err != nil {
images, ierr := ip.client.ImageList(ip.Server.Context(), types.ImageListOptions{}) images, ierr := ip.client.ImageList(ip.Server.Context(), image.ListOptions{})
if ierr != nil { if ierr != nil {
// Well damn, something has gone really wrong here, just go ahead and abort there // Well damn, something has gone really wrong here, just go ahead and abort there
// isn't much anything we can do to try and self-recover from this. // isn't much anything we can do to try and self-recover from this.
@ -332,7 +332,7 @@ func (ip *InstallationProcess) AfterExecute(containerId string) error {
defer ip.RemoveContainer() defer ip.RemoveContainer()
ip.Server.Log().WithField("container_id", containerId).Debug("pulling installation logs for server") ip.Server.Log().WithField("container_id", containerId).Debug("pulling installation logs for server")
reader, err := ip.client.ContainerLogs(ip.Server.Context(), containerId, types.ContainerLogsOptions{ reader, err := ip.client.ContainerLogs(ip.Server.Context(), containerId, container.LogsOptions{
ShowStdout: true, ShowStdout: true,
ShowStderr: true, ShowStderr: true,
Follow: false, Follow: false,
@ -463,7 +463,7 @@ func (ip *InstallationProcess) Execute() (string, error) {
} }
ip.Server.Log().WithField("container_id", r.ID).Info("running installation script for server in container") ip.Server.Log().WithField("container_id", r.ID).Info("running installation script for server in container")
if err := ip.client.ContainerStart(ctx, r.ID, types.ContainerStartOptions{}); err != nil { if err := ip.client.ContainerStart(ctx, r.ID, container.StartOptions{}); err != nil {
return "", err return "", err
} }
@ -498,7 +498,7 @@ func (ip *InstallationProcess) Execute() (string, error) {
// the server configuration directory, as well as to a websocket listener so // the server configuration directory, as well as to a websocket listener so
// that the process can be viewed in the panel by administrators. // that the process can be viewed in the panel by administrators.
func (ip *InstallationProcess) StreamOutput(ctx context.Context, id string) error { func (ip *InstallationProcess) StreamOutput(ctx context.Context, id string) error {
opts := types.ContainerLogsOptions{ShowStdout: true, ShowStderr: true, Follow: true} opts := container.LogsOptions{ShowStdout: true, ShowStderr: true, Follow: true}
reader, err := ip.client.ContainerLogs(ctx, id, opts) reader, err := ip.client.ContainerLogs(ctx, id, opts)
if err != nil { if err != nil {
return err return err

View File

@ -2,6 +2,7 @@ package server
import ( import (
"context" "context"
"encoding/json"
"fmt" "fmt"
"io" "io"
"os" "os"
@ -13,7 +14,6 @@ import (
"emperror.dev/errors" "emperror.dev/errors"
"github.com/apex/log" "github.com/apex/log"
"github.com/gammazero/workerpool" "github.com/gammazero/workerpool"
"github.com/goccy/go-json"
"github.com/pterodactyl/wings/config" "github.com/pterodactyl/wings/config"
"github.com/pterodactyl/wings/environment" "github.com/pterodactyl/wings/environment"

View File

@ -29,6 +29,21 @@ func (s *Server) Mounts() []environment.Mount {
}, },
} }
// Handle mounting a generated `/etc/passwd` if the feature is enabled.
if passwd := config.Get().System.Passwd; passwd.Enable {
s.Log().WithFields(log.Fields{"source_path": passwd.Directory}).Info("mouting generated /etc/{group,passwd} to workaround UID/GID issues")
m = append(m, environment.Mount{
Source: filepath.Join(passwd.Directory, "group"),
Target: "/etc/group",
ReadOnly: true,
})
m = append(m, environment.Mount{
Source: filepath.Join(passwd.Directory, "passwd"),
Target: "/etc/passwd",
ReadOnly: true,
})
}
// Also include any of this server's custom mounts when returning them. // Also include any of this server's custom mounts when returning them.
return append(m, s.customMounts()...) return append(m, s.customMounts()...)
} }
@ -56,14 +71,12 @@ func (s *Server) customMounts() []environment.Mount {
if !strings.HasPrefix(source, filepath.Clean(allowed)) { if !strings.HasPrefix(source, filepath.Clean(allowed)) {
continue continue
} }
mounted = true mounted = true
mounts = append(mounts, environment.Mount{ mounts = append(mounts, environment.Mount{
Source: source, Source: source,
Target: target, Target: target,
ReadOnly: m.ReadOnly, ReadOnly: m.ReadOnly,
}) })
break break
} }

View File

@ -3,7 +3,6 @@ package server
import ( import (
"context" "context"
"fmt" "fmt"
"os"
"time" "time"
"emperror.dev/errors" "emperror.dev/errors"
@ -161,7 +160,7 @@ func (s *Server) HandlePowerAction(action PowerAction, waitSeconds ...int) error
return s.Environment.Start(s.Context()) return s.Environment.Start(s.Context())
case PowerActionTerminate: case PowerActionTerminate:
return s.Environment.Terminate(s.Context(), os.Kill) return s.Environment.Terminate(s.Context(), "SIGKILL")
} }
return errors.New("attempting to handle unknown power action") return errors.New("attempting to handle unknown power action")

View File

@ -2,6 +2,7 @@ package server
import ( import (
"context" "context"
"encoding/json"
"fmt" "fmt"
"net/http" "net/http"
"os" "os"
@ -11,7 +12,6 @@ import (
"emperror.dev/errors" "emperror.dev/errors"
"github.com/apex/log" "github.com/apex/log"
"github.com/creasty/defaults" "github.com/creasty/defaults"
"github.com/goccy/go-json"
"github.com/pterodactyl/wings/config" "github.com/pterodactyl/wings/config"
"github.com/pterodactyl/wings/environment" "github.com/pterodactyl/wings/environment"

View File

@ -28,6 +28,7 @@ func (s *Server) SyncWithEnvironment() {
Mounts: s.Mounts(), Mounts: s.Mounts(),
Allocations: cfg.Allocations, Allocations: cfg.Allocations,
Limits: cfg.Build, Limits: cfg.Build,
Labels: cfg.Labels,
}) })
// For Docker specific environments we also want to update the configured image // For Docker specific environments we also want to update the configured image

View File

@ -31,8 +31,8 @@ func NewSinkPool() *SinkPool {
// On adds a channel to the sink pool instance. // On adds a channel to the sink pool instance.
func (p *SinkPool) On(c chan []byte) { func (p *SinkPool) On(c chan []byte) {
p.mu.Lock() p.mu.Lock()
defer p.mu.Unlock()
p.sinks = append(p.sinks, c) p.sinks = append(p.sinks, c)
p.mu.Unlock()
} }
// Off removes a given channel from the sink pool. If no matching sink is found // Off removes a given channel from the sink pool. If no matching sink is found
@ -69,13 +69,11 @@ func (p *SinkPool) Off(c chan []byte) {
func (p *SinkPool) Destroy() { func (p *SinkPool) Destroy() {
p.mu.Lock() p.mu.Lock()
defer p.mu.Unlock() defer p.mu.Unlock()
for _, c := range p.sinks { for _, c := range p.sinks {
if c != nil { if c != nil {
close(c) close(c)
} }
} }
p.sinks = nil p.sinks = nil
} }
@ -98,6 +96,7 @@ func (p *SinkPool) Destroy() {
func (p *SinkPool) Push(data []byte) { func (p *SinkPool) Push(data []byte) {
p.mu.RLock() p.mu.RLock()
defer p.mu.RUnlock() defer p.mu.RUnlock()
var wg sync.WaitGroup var wg sync.WaitGroup
wg.Add(len(p.sinks)) wg.Add(len(p.sinks))
for _, c := range p.sinks { for _, c := range p.sinks {
@ -105,15 +104,22 @@ func (p *SinkPool) Push(data []byte) {
defer wg.Done() defer wg.Done()
select { select {
case c <- data: case c <- data:
case <-time.After(time.Millisecond * 10): case <-time.After(10 * time.Millisecond):
// If there is nothing in the channel to read, but we also cannot write // If we cannot send the message to the channel within 10ms,
// to the channel, just skip over sending data. If we don't do this you'll // then try to drop the oldest message from the channel, then
// end up blocking the application on the channel read below. // send our message.
if len(c) == 0 { select {
break case <-c:
// Only attempt to send the message if we were able to make
// space for it on the channel.
select {
case c <- data:
default:
}
default:
// Do nothing, this is a fallthrough if there is nothing to
// read from c.
} }
<-c
c <- data
} }
}(c) }(c)
} }

View File

@ -2,7 +2,6 @@ package system
import ( import (
"fmt" "fmt"
"reflect"
"sync" "sync"
"testing" "testing"
"time" "time"
@ -11,20 +10,11 @@ import (
) )
func MutexLocked(m *sync.RWMutex) bool { func MutexLocked(m *sync.RWMutex) bool {
v := reflect.ValueOf(m).Elem() unlocked := m.TryLock()
if unlocked {
state := v.FieldByName("w").FieldByName("state") m.Unlock()
readerCountField := v.FieldByName("readerCount")
// go1.20 changed readerCount to an atomic
// ref; https://github.com/golang/go/commit/e509452727b469d89a3fc4a7d1cbf9d3f110efee
var readerCount int64
if readerCountField.Kind() == reflect.Struct {
readerCount = readerCountField.FieldByName("v").Int()
} else {
readerCount = readerCountField.Int()
} }
return state.Int()&1 == 1 || readerCount > 0 return !unlocked
} }
func TestSink(t *testing.T) { func TestSink(t *testing.T) {

View File

@ -3,13 +3,13 @@ package system
import ( import (
"bufio" "bufio"
"bytes" "bytes"
"encoding/json"
"fmt" "fmt"
"io" "io"
"strconv" "strconv"
"sync" "sync"
"emperror.dev/errors" "emperror.dev/errors"
"github.com/goccy/go-json"
) )
var ( var (

View File

@ -1,6 +1,7 @@
package system package system
import ( import (
"encoding/json"
"math/rand" "math/rand"
"strings" "strings"
"sync" "sync"
@ -8,7 +9,6 @@ import (
"time" "time"
. "github.com/franela/goblin" . "github.com/franela/goblin"
"github.com/goccy/go-json"
) )
func Test_Utils(t *testing.T) { func Test_Utils(t *testing.T) {

Binary file not shown.