mirror of
https://github.com/psviderski/uncloud.git
synced 2026-08-26 19:13:34 +00:00
Compare commits
89
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3310b478a1 | ||
|
|
8384d4162c | ||
|
|
90af550693 | ||
|
|
fe829efbf5 | ||
|
|
cd07278858 | ||
|
|
45cf87a0cb | ||
|
|
9ce624facd | ||
|
|
c38d916dbb | ||
|
|
50057c42e0 | ||
|
|
f379bf4c14 | ||
|
|
dd3d809c6e | ||
|
|
44897f351f | ||
|
|
c888e0a76f | ||
|
|
7b554cd703 | ||
|
|
d340659370 | ||
|
|
6fb68c22c5 | ||
|
|
b1897ce719 | ||
|
|
47199c36ce | ||
|
|
d812cf8f6e | ||
|
|
39690eed72 | ||
|
|
56a54ab3fd | ||
|
|
303c8e4506 | ||
|
|
8afe52367f | ||
|
|
5b5cd44d68 | ||
|
|
19c13f6cd2 | ||
|
|
0f5f8727a3 | ||
|
|
2045819735 | ||
|
|
cb72f67560 | ||
|
|
b852d34068 | ||
|
|
7e1b91c372 | ||
|
|
6409a47c71 | ||
|
|
f44ada06b9 | ||
|
|
3a79aeffcc | ||
|
|
84990ad692 | ||
|
|
8d023f5c53 | ||
|
|
1c0d48cb46 | ||
|
|
4d5ca42a9d | ||
|
|
fdbffbf020 | ||
|
|
102421df19 | ||
|
|
f8234916cb | ||
|
|
208a561003 | ||
|
|
6e9acefbe6 | ||
|
|
39181708dd | ||
|
|
e8111a419f | ||
|
|
9cfcf49d28 | ||
|
|
60ff088971 | ||
|
|
3eda6cdc22 | ||
|
|
b1be80adf2 | ||
|
|
02a318cd2e | ||
|
|
f41590157f | ||
|
|
dd989a281e | ||
|
|
f60a9ff293 | ||
|
|
07e380b95f | ||
|
|
4e060914ec | ||
|
|
f317dde168 | ||
|
|
f2f6fee89a | ||
|
|
b3b33a82ba | ||
|
|
cd149e4506 | ||
|
|
eff1bc88d8 | ||
|
|
ab644f6a47 | ||
|
|
883f184e37 | ||
|
|
897f30fd36 | ||
|
|
943fea0515 | ||
|
|
e96cce88c1 | ||
|
|
1930bb5766 | ||
|
|
a844cc6f67 | ||
|
|
043b85ab70 | ||
|
|
fe813302b7 | ||
|
|
3e30a59d4c | ||
|
|
0da9df17a4 | ||
|
|
0e820f9c52 | ||
|
|
772b31b57f | ||
|
|
1c8b77054a | ||
|
|
cef047221c | ||
|
|
4b34c42b76 | ||
|
|
7ec8879af3 | ||
|
|
cb77d42107 | ||
|
|
d45b5a488e | ||
|
|
f4581eac11 | ||
|
|
0651b9d0ac | ||
|
|
62976f3338 | ||
|
|
d38312fec6 | ||
|
|
636307d3ea | ||
|
|
e22cdf0b18 | ||
|
|
9aee75ba28 | ||
|
|
37c937a570 | ||
|
|
a1bde30b57 | ||
|
|
06c2bc55f2 | ||
|
|
0a9a1db815 |
Binary file not shown.
|
Before Width: | Height: | Size: 525 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 635 KiB |
@@ -0,0 +1,98 @@
|
|||||||
|
name: Nightly Build
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
|
||||||
|
env:
|
||||||
|
MISE_VERSION: "2026.3.7"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
nightly:
|
||||||
|
# Run on macOS to be able to build the fsevent dependency required by compose on macOS.
|
||||||
|
runs-on: macos-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Install Mise
|
||||||
|
uses: jdx/mise-action@1648a7812b9aeae629881980618f079932869151 # v4.0.1
|
||||||
|
with:
|
||||||
|
version: ${{ env.MISE_VERSION }}
|
||||||
|
- name: Reset changes in mise.lock after fresh mise install
|
||||||
|
run: git checkout mise.lock
|
||||||
|
|
||||||
|
- name: Cache Go build and module cache
|
||||||
|
uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/Library/Caches/go-build
|
||||||
|
~/go/pkg/mod
|
||||||
|
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}-${{ hashFiles('**/*.go') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}-
|
||||||
|
${{ runner.os }}-go-
|
||||||
|
|
||||||
|
- name: Run GoReleaser snapshot
|
||||||
|
run: goreleaser release --snapshot --clean
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Delete existing nightly release
|
||||||
|
run: gh release delete nightly --yes --cleanup-tag || true
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Create nightly release
|
||||||
|
run: |
|
||||||
|
COMMIT=$(git rev-parse --short HEAD)
|
||||||
|
DATE=$(date -u '+%Y-%m-%d %H:%M UTC')
|
||||||
|
RELEASE_NOTES=$(cat <<EOF
|
||||||
|
Built from \`main\` at ${COMMIT} on ${DATE}.
|
||||||
|
|
||||||
|
The commands below show how to install the nightly version of uncloud.
|
||||||
|
|
||||||
|
> **Warning**: These are nightly development builds and may be unstable.
|
||||||
|
|
||||||
|
**Install uncloud CLI:**
|
||||||
|
|
||||||
|
\`\`\`
|
||||||
|
curl -fsS https://get.uncloud.run/install.sh | VERSION=nightly sh
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
**Initialize a cluster or add a machine:**
|
||||||
|
|
||||||
|
\`\`\`
|
||||||
|
uc machine init --version nightly user@host
|
||||||
|
uc machine add --version nightly user@host
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
**Upgrade machine daemon:**
|
||||||
|
|
||||||
|
\`\`\`
|
||||||
|
ARCH=\$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/')
|
||||||
|
curl -fsSL -o uncloudd.tar.gz https://github.com/psviderski/uncloud/releases/download/nightly/uncloudd_linux_\${ARCH}.tar.gz
|
||||||
|
tar -xf uncloudd.tar.gz
|
||||||
|
sudo install uncloudd /usr/local/bin/uncloudd
|
||||||
|
rm uncloudd uncloudd.tar.gz
|
||||||
|
sudo systemctl restart uncloud
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
EOF
|
||||||
|
)
|
||||||
|
|
||||||
|
gh release create nightly \
|
||||||
|
--title "Nightly Build" \
|
||||||
|
--notes "${RELEASE_NOTES}" \
|
||||||
|
--prerelease \
|
||||||
|
--target ${{ github.sha }} \
|
||||||
|
dist/*.tar.gz dist/checksums.txt
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
timeout-minutes: 30
|
||||||
@@ -11,6 +11,14 @@ before:
|
|||||||
|
|
||||||
report_sizes: true
|
report_sizes: true
|
||||||
|
|
||||||
|
git:
|
||||||
|
# Skip the stale 'nightly' tag so {{.Version}} resolves to the latest semver tag for --snapshot builds.
|
||||||
|
ignore_tags:
|
||||||
|
- nightly
|
||||||
|
snapshot:
|
||||||
|
# Semver-compliant nightly version, e.g. "0.19.0-nightly-abc1234" when the latest tag is v0.18.0.
|
||||||
|
version_template: "{{ incminor .Version }}-nightly-{{.ShortCommit}}"
|
||||||
|
|
||||||
builds:
|
builds:
|
||||||
- id: uncloud
|
- id: uncloud
|
||||||
main: ./cmd/uncloud
|
main: ./cmd/uncloud
|
||||||
|
|||||||
@@ -197,6 +197,7 @@ uc context use <name> # Switch context
|
|||||||
- Integration tests in `test/e2e/`
|
- Integration tests in `test/e2e/`
|
||||||
- Test fixtures in `test/fixtures/`
|
- Test fixtures in `test/fixtures/`
|
||||||
- Use table driven tests whenever possible
|
- Use table driven tests whenever possible
|
||||||
|
- Use the `testify` library for assertions (e.g., `require.Equal`, `assert.Nil`)
|
||||||
|
|
||||||
### Dependencies
|
### Dependencies
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -46,7 +46,7 @@ RUN apk --no-cache add crane
|
|||||||
RUN crane pull --platform ${TARGETOS}/${TARGETARCH} "${CORROSION_IMAGE}" /corrosion.tar
|
RUN crane pull --platform ${TARGETOS}/${TARGETARCH} "${CORROSION_IMAGE}" /corrosion.tar
|
||||||
|
|
||||||
# Uncloud-in-Docker (ucind) image for running Uncloud test clusters using Docker.
|
# Uncloud-in-Docker (ucind) image for running Uncloud test clusters using Docker.
|
||||||
FROM docker:27.3.1-dind AS ucind
|
FROM docker:29.4.0-dind AS ucind
|
||||||
# Create system group and user 'uncloud'.
|
# Create system group and user 'uncloud'.
|
||||||
RUN addgroup -S uncloud && adduser -SHD -h /nonexistent -G uncloud -g "" uncloud
|
RUN addgroup -S uncloud && adduser -SHD -h /nonexistent -G uncloud -g "" uncloud
|
||||||
RUN apk --no-cache add \
|
RUN apk --no-cache add \
|
||||||
@@ -54,7 +54,7 @@ RUN apk --no-cache add \
|
|||||||
wireguard-tools
|
wireguard-tools
|
||||||
|
|
||||||
COPY --from=corrosion-image-tarball /corrosion.tar /images/corrosion.tar
|
COPY --from=corrosion-image-tarball /corrosion.tar /images/corrosion.tar
|
||||||
COPY scripts/docker/dind scripts/docker/entrypoint.sh /usr/local/bin/
|
COPY scripts/docker/entrypoint.sh /usr/local/bin/
|
||||||
COPY --from=uncloudd /build/uncloudd /usr/local/bin/
|
COPY --from=uncloudd /build/uncloudd /usr/local/bin/
|
||||||
|
|
||||||
ENTRYPOINT ["entrypoint.sh"]
|
ENTRYPOINT ["entrypoint.sh"]
|
||||||
|
|||||||
+4
-3
@@ -5,7 +5,8 @@ A short guide on setting up your local development environment.
|
|||||||
## Prerequisites
|
## Prerequisites
|
||||||
|
|
||||||
- [mise](https://mise.jdx.dev/) (development environment setup tool)
|
- [mise](https://mise.jdx.dev/) (development environment setup tool)
|
||||||
- [Docker](https://docs.docker.com/get-docker/) (for running end-to-end tests)
|
- [Docker](https://docs.docker.com/get-docker/) with BuildKit (for running end-to-end tests). Make sure the
|
||||||
|
[`docker buildx`](https://docs.docker.com/build/concepts/overview/) plugin is installed.
|
||||||
|
|
||||||
## Setup
|
## Setup
|
||||||
|
|
||||||
@@ -123,8 +124,8 @@ make mocks
|
|||||||
The [`ucind`](./cmd/ucind) CLI lets you run Uncloud clusters locally using Docker containers instead of real machines.
|
The [`ucind`](./cmd/ucind) CLI lets you run Uncloud clusters locally using Docker containers instead of real machines.
|
||||||
Each cluster machine runs as a Docker container connected to a shared Docker network.
|
Each cluster machine runs as a Docker container connected to a shared Docker network.
|
||||||
|
|
||||||
Cluster machines use the `ghcr.io/psviderski/ucind:latest` Docker image by default. It may not always have the
|
Cluster machines use the `ghcr.io/psviderski/ucind:latest` Docker image by default. It may not always have the latest
|
||||||
latest changes to the `uncloudd` daemon code as it's not automatically rebuilt on CI yet.
|
changes to the `uncloudd` daemon code as it's not automatically rebuilt on CI yet.
|
||||||
|
|
||||||
You can rebuild it locally to pick up the latest changes, including your work in progress, and test them in a UCinD
|
You can rebuild it locally to pick up the latest changes, including your work in progress, and test them in a UCinD
|
||||||
cluster:
|
cluster:
|
||||||
|
|||||||
@@ -49,13 +49,13 @@ complexity of Kubernetes.
|
|||||||
|
|
||||||
## 🎬 Quick demo
|
## 🎬 Quick demo
|
||||||
|
|
||||||
The screenshot below demonstrates how I use Uncloud to deploy https://uncloud.run website to 2 remote machines from
|
The screenshot below demonstrates how we use Uncloud to deploy https://uncloud.run website to 2 remote machines from
|
||||||
the [`compose.yaml`](website/compose.yaml) file on my local machine.
|
the [`compose.yaml`](website/compose.yaml) file on the local machine.
|
||||||
|
|
||||||
It exposes the container port `8000/tcp` as HTTPS on the domain `uncloud.run`, served by the Caddy reverse proxy on the
|
It exposes the container port `8000/tcp` as HTTPS on `uncloud.run` domain, served by the Caddy reverse proxy on the
|
||||||
remote machines. All managed by Uncloud.
|
remote machines. All managed by Uncloud.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Here is a more advanced use case. Deploy a highly available web app with automatic HTTPS across multiple regions and
|
Here is a more advanced use case. Deploy a highly available web app with automatic HTTPS across multiple regions and
|
||||||
on-premises in just a couple minutes.
|
on-premises in just a couple minutes.
|
||||||
@@ -102,6 +102,9 @@ platform, whether you're running on a $5 VPS, a spare Mac mini, or a rack of bar
|
|||||||
|
|
||||||
See [Installation](https://uncloud.run/docs/getting-started/install-cli) for more options.
|
See [Installation](https://uncloud.run/docs/getting-started/install-cli) for more options.
|
||||||
|
|
||||||
|
There's also a [nightly rolling release](https://github.com/psviderski/uncloud/releases/tag/nightly) available if you
|
||||||
|
want to try the latest features and fixes before they're included in an official release.
|
||||||
|
|
||||||
2. Initialise your first machine:
|
2. Initialise your first machine:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -283,18 +286,24 @@ machine is a full backup of your control plane.
|
|||||||
|
|
||||||
## 🧪 Interactive tutorials
|
## 🧪 Interactive tutorials
|
||||||
|
|
||||||
To give you a chance to play with Uncloud without even leaving your browser, we're providing interactive tutorials and playgrounds on the [iximiuz Labs](https://labs.iximiuz.com/) platform.
|
To give you a chance to play with Uncloud without even leaving your browser, we're providing interactive tutorials and
|
||||||
|
playgrounds on the [iximiuz Labs](https://labs.iximiuz.com/) platform.
|
||||||
|
|
||||||
Available tutorials:
|
Available tutorials:
|
||||||
|
|
||||||
1. [Setting up a new Uncloud cluster](https://labs.iximiuz.com/tutorials/uncloud-create-cluster-ebebf72b) - the tutorial walks you through creating a new cluster with two machines and then deploying a simple web service to it.
|
1. [Setting up a new Uncloud cluster](https://labs.iximiuz.com/tutorials/uncloud-create-cluster-ebebf72b) - the tutorial
|
||||||
|
walks you through creating a new cluster with two machines and then deploying a simple web service to it.
|
||||||
|
2. [Deploying a Django web application](https://labs.iximiuz.com/tutorials/uncloud-deploy-django-app-7a378bc3) - deploy
|
||||||
|
a Django web app from source code to a remote Linux server using Uncloud.
|
||||||
|
|
||||||
You can also launch the [Uncloud playground](https://labs.iximiuz.com/playgrounds/uncloud-cluster-64523f7c) where you can play with an already initialized Uncloud cluster.
|
You can also launch the [Uncloud playground](https://labs.iximiuz.com/playgrounds/uncloud-cluster-64523f7c) where you
|
||||||
|
can play with an already initialized Uncloud cluster.
|
||||||
|
|
||||||
## 🏗 Project status
|
## 🏗 Project status
|
||||||
|
|
||||||
Uncloud is currently in active development and is **not ready for production use**. Features may change significantly
|
Uncloud is under active development and pre-1.0, so expect occasional breaking changes between releases. The core build,
|
||||||
and there may be breaking changes between releases.
|
push, and deploy workflows are stable, and much of the runtime relies on battle-tested projects: Docker, Caddy,
|
||||||
|
WireGuard, and Corrosion.
|
||||||
|
|
||||||
We'd love your input! Here's how you can contribute:
|
We'd love your input! Here's how you can contribute:
|
||||||
|
|
||||||
@@ -332,8 +341,8 @@ SQLite database used to share Uncloud's cluster state.
|
|||||||
|
|
||||||
* Join our [Discord server](https://discord.gg/eR35KQJhPu) for real-time discussions, support, and updates.
|
* Join our [Discord server](https://discord.gg/eR35KQJhPu) for real-time discussions, support, and updates.
|
||||||
* Follow [@psviderski](https://x.com/psviderski) on X/Twitter.
|
* Follow [@psviderski](https://x.com/psviderski) on X/Twitter.
|
||||||
* Subscribe to [my newsletter](https://uncloud.run/#subscribe) to follow the progress, get early insights into new
|
* Subscribe to [our newsletter](https://uncloud.run/#subscribe) to follow the journey of building Uncloud: progress
|
||||||
features, and be the first to know when it's ready for production use.
|
updates, user stories, and the occasional deep dive.
|
||||||
* Watch this repository for releases.
|
* Watch this repository for releases.
|
||||||
|
|
||||||
## 💖 Sponsors
|
## 💖 Sponsors
|
||||||
@@ -351,8 +360,6 @@ These companies and projects are helping Uncloud with their generous sponsorship
|
|||||||
<img alt="Trendshift" src="https://trendshift.io/api/badge/repositories/14069" />
|
<img alt="Trendshift" src="https://trendshift.io/api/badge/repositories/14069" />
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
Thank you [@cedws](https://github.com/cedws) for being the first contributor to Uncloud! 🎉
|
|
||||||
|
|
||||||
<a href="https://github.com/psviderski/uncloud/graphs/contributors">
|
<a href="https://github.com/psviderski/uncloud/graphs/contributors">
|
||||||
<img src="https://contrib.rocks/image?repo=psviderski/uncloud" />
|
<img src="https://contrib.rocks/image?repo=psviderski/uncloud" />
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
package cluster
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/psviderski/uncloud/internal/cli/tui"
|
||||||
|
"github.com/psviderski/uncloud/internal/ucind"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewListCommand() *cobra.Command {
|
||||||
|
cmd := &cobra.Command{
|
||||||
|
Use: "ls",
|
||||||
|
Aliases: []string{"list"},
|
||||||
|
Short: "List clusters.",
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
p := cmd.Context().Value("provisioner").(*ucind.Provisioner)
|
||||||
|
clusters, err := p.ListClusters(cmd.Context())
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("list clusters: %w", err)
|
||||||
|
}
|
||||||
|
t := tui.NewTable()
|
||||||
|
t.Headers("NAME", "MACHINES")
|
||||||
|
for _, cluster := range clusters {
|
||||||
|
machines := []string{}
|
||||||
|
for _, m := range cluster.Machines {
|
||||||
|
machines = append(machines, m.Name)
|
||||||
|
}
|
||||||
|
t.Row(
|
||||||
|
cluster.Name,
|
||||||
|
strings.Join(machines, tui.Faint.Render(", ")),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
fmt.Println(t)
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
@@ -13,7 +13,7 @@ func NewRootCommand() *cobra.Command {
|
|||||||
}
|
}
|
||||||
cmd.AddCommand(
|
cmd.AddCommand(
|
||||||
NewCreateCommand(),
|
NewCreateCommand(),
|
||||||
// NewListCommand(),
|
NewListCommand(),
|
||||||
NewRemoveCommand(),
|
NewRemoveCommand(),
|
||||||
)
|
)
|
||||||
return cmd
|
return cmd
|
||||||
|
|||||||
+4
-2
@@ -8,6 +8,7 @@ import (
|
|||||||
|
|
||||||
"github.com/docker/docker/client"
|
"github.com/docker/docker/client"
|
||||||
"github.com/psviderski/uncloud/cmd/ucind/cluster"
|
"github.com/psviderski/uncloud/cmd/ucind/cluster"
|
||||||
|
"github.com/psviderski/uncloud/internal/cli"
|
||||||
"github.com/psviderski/uncloud/internal/ucind"
|
"github.com/psviderski/uncloud/internal/ucind"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
@@ -23,6 +24,8 @@ func main() {
|
|||||||
SilenceUsage: true,
|
SilenceUsage: true,
|
||||||
SilenceErrors: true,
|
SilenceErrors: true,
|
||||||
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
|
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
cli.BindEnvToFlag(cmd, "uncloud-config", "UNCLOUD_CONFIG")
|
||||||
|
|
||||||
cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
|
cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("create Docker client: %w", err)
|
return fmt.Errorf("create Docker client: %w", err)
|
||||||
@@ -44,9 +47,8 @@ func main() {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: allow to override using UNCLOUD_CONFIG env var.
|
|
||||||
cmd.PersistentFlags().StringVar(&configPath, "uncloud-config", "~/.config/uncloud/config.yaml",
|
cmd.PersistentFlags().StringVar(&configPath, "uncloud-config", "~/.config/uncloud/config.yaml",
|
||||||
"path to the Uncloud configuration file.")
|
"Path to the Uncloud configuration file. [$UNCLOUD_CONFIG]")
|
||||||
_ = cmd.MarkPersistentFlagFilename("uncloud-config", "yaml", "yml")
|
_ = cmd.MarkPersistentFlagFilename("uncloud-config", "yaml", "yml")
|
||||||
|
|
||||||
cmd.AddCommand(
|
cmd.AddCommand(
|
||||||
|
|||||||
@@ -88,12 +88,12 @@ func runDeploy(ctx context.Context, uncli *cli.CLI, opts deployOptions) error {
|
|||||||
if len(currentImages) > 1 {
|
if len(currentImages) > 1 {
|
||||||
formattedImages := make([]string, len(currentImages))
|
formattedImages := make([]string, len(currentImages))
|
||||||
for i, img := range currentImages {
|
for i, img := range currentImages {
|
||||||
formattedImages[i] = tui.FormatImage(img, lipgloss.NewStyle())
|
formattedImages[i] = tui.FormatImage(img, tui.NoStyle)
|
||||||
}
|
}
|
||||||
fmt.Println(tui.Faint.Render("current images (multiple versions detected): ") +
|
fmt.Println(tui.Faint.Render("current images (multiple versions detected): ") +
|
||||||
strings.Join(formattedImages, tui.Faint.Render(", ")))
|
strings.Join(formattedImages, tui.Faint.Render(", ")))
|
||||||
} else {
|
} else {
|
||||||
fmt.Println(tui.Faint.Render("current image: ") + tui.FormatImage(currentImages[0], lipgloss.NewStyle()))
|
fmt.Println(tui.Faint.Render("current image: ") + tui.FormatImage(currentImages[0], tui.NoStyle))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,11 +3,10 @@ package context
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"maps"
|
"maps"
|
||||||
"os"
|
|
||||||
"slices"
|
"slices"
|
||||||
"text/tabwriter"
|
|
||||||
|
|
||||||
"github.com/psviderski/uncloud/internal/cli"
|
"github.com/psviderski/uncloud/internal/cli"
|
||||||
|
"github.com/psviderski/uncloud/internal/cli/tui"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -38,8 +37,8 @@ func list(uncli *cli.CLI) error {
|
|||||||
contextNames := slices.Sorted(maps.Keys(uncli.Config.Contexts))
|
contextNames := slices.Sorted(maps.Keys(uncli.Config.Contexts))
|
||||||
currentContext := uncli.Config.CurrentContext
|
currentContext := uncli.Config.CurrentContext
|
||||||
|
|
||||||
tw := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0)
|
t := tui.NewTable()
|
||||||
fmt.Fprintln(tw, "NAME\tCURRENT\tCONNECTIONS")
|
t.Headers("NAME", "CURRENT", "CONNECTIONS")
|
||||||
|
|
||||||
for _, name := range contextNames {
|
for _, name := range contextNames {
|
||||||
current := ""
|
current := ""
|
||||||
@@ -47,8 +46,9 @@ func list(uncli *cli.CLI) error {
|
|||||||
current = "✓"
|
current = "✓"
|
||||||
}
|
}
|
||||||
connCount := len(uncli.Config.Contexts[name].Connections)
|
connCount := len(uncli.Config.Contexts[name].Connections)
|
||||||
fmt.Fprintf(tw, "%s\t%s\t%d\n", name, current, connCount)
|
t.Row(name, current, fmt.Sprintf("%d", connCount))
|
||||||
}
|
}
|
||||||
|
|
||||||
return tw.Flush()
|
fmt.Println(t)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ func NewRootCommand() *cobra.Command {
|
|||||||
NewListCommand(),
|
NewListCommand(),
|
||||||
NewUseCommand(),
|
NewUseCommand(),
|
||||||
NewConnectionCommand(),
|
NewConnectionCommand(),
|
||||||
|
NewShowCommand(),
|
||||||
)
|
)
|
||||||
|
|
||||||
return cmd
|
return cmd
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
package context
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/psviderski/uncloud/internal/cli"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewShowCommand() *cobra.Command {
|
||||||
|
cmd := &cobra.Command{
|
||||||
|
Use: "show",
|
||||||
|
Short: "Show current cluster context.",
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
uncli := cmd.Context().Value("cli").(*cli.CLI)
|
||||||
|
return show(uncli)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func show(uncli *cli.CLI) error {
|
||||||
|
// discard errors, only show the current context, otherwise nothing
|
||||||
|
if uncli.Config == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(uncli.Config.Contexts) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
fmt.Println(uncli.Config.CurrentContext)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
+71
-1
@@ -9,11 +9,15 @@ import (
|
|||||||
"charm.land/lipgloss/v2"
|
"charm.land/lipgloss/v2"
|
||||||
composecli "github.com/compose-spec/compose-go/v2/cli"
|
composecli "github.com/compose-spec/compose-go/v2/cli"
|
||||||
"github.com/docker/compose/v2/pkg/progress"
|
"github.com/docker/compose/v2/pkg/progress"
|
||||||
|
"github.com/docker/docker/pkg/stringid"
|
||||||
"github.com/psviderski/uncloud/internal/cli"
|
"github.com/psviderski/uncloud/internal/cli"
|
||||||
|
"github.com/psviderski/uncloud/internal/cli/logs"
|
||||||
"github.com/psviderski/uncloud/internal/cli/tui"
|
"github.com/psviderski/uncloud/internal/cli/tui"
|
||||||
|
"github.com/psviderski/uncloud/pkg/api"
|
||||||
"github.com/psviderski/uncloud/pkg/client"
|
"github.com/psviderski/uncloud/pkg/client"
|
||||||
"github.com/psviderski/uncloud/pkg/client/compose"
|
"github.com/psviderski/uncloud/pkg/client/compose"
|
||||||
"github.com/psviderski/uncloud/pkg/client/deploy"
|
"github.com/psviderski/uncloud/pkg/client/deploy"
|
||||||
|
"github.com/psviderski/uncloud/pkg/client/deploy/operation"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -223,10 +227,76 @@ func runDeploy(ctx context.Context, uncli *cli.CLI, opts deployOptions) error {
|
|||||||
if deployTarget != "" {
|
if deployTarget != "" {
|
||||||
title += " to " + tui.NameStyle.Render(deployTarget)
|
title += " to " + tui.NameStyle.Render(deployTarget)
|
||||||
}
|
}
|
||||||
return progress.RunWithTitle(ctx, func(ctx context.Context) error {
|
err = progress.RunWithTitle(ctx, func(ctx context.Context) error {
|
||||||
if err := plan.Execute(ctx, clusterClient); err != nil {
|
if err := plan.Execute(ctx, clusterClient); err != nil {
|
||||||
return fmt.Errorf("deploy services: %w", err)
|
return fmt.Errorf("deploy services: %w", err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}, uncli.ProgressOut(), title)
|
}, uncli.ProgressOut(), title)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println()
|
||||||
|
|
||||||
|
tail := failedContainerLogsTail()
|
||||||
|
if hookErr, ok := errors.AsType[*operation.PreDeployHookError](err); ok {
|
||||||
|
printFailedContainerLogs(ctx, clusterClient,
|
||||||
|
hookErr.ServiceName, hookErr.ContainerID, hookErr.MachineName, tail,
|
||||||
|
fmt.Sprintf("Last %d log lines from failed pre-deploy hook:", tail))
|
||||||
|
fmt.Println()
|
||||||
|
} else if startErr, ok := errors.AsType[*operation.ContainerHealthError](err); ok {
|
||||||
|
printFailedContainerLogs(ctx, clusterClient,
|
||||||
|
startErr.ServiceName, startErr.ContainerID, startErr.MachineName, tail,
|
||||||
|
fmt.Sprintf("Last %d log lines from failed container:", tail))
|
||||||
|
fmt.Println()
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// printFailedContainerLogs fetches the last tail log lines from a container that failed during deployment and prints
|
||||||
|
// them using the standard log formatter under the provided header.
|
||||||
|
func printFailedContainerLogs(
|
||||||
|
ctx context.Context, cli *client.Client, serviceName, containerID, machineName string, tail int, header string,
|
||||||
|
) {
|
||||||
|
_, ch, err := cli.ServiceLogs(ctx, serviceName, api.ServiceLogsOptions{
|
||||||
|
Containers: []string{containerID},
|
||||||
|
Machines: []string{machineName},
|
||||||
|
Tail: tail,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
shortCtrID := stringid.TruncateID(containerID)
|
||||||
|
fmt.Fprintf(os.Stderr, "Failed to fetch container logs '%s/%s': %v\n", serviceName, shortCtrID, err)
|
||||||
|
fmt.Fprintf(os.Stderr, "You can try manually with: uc logs %s/%s\n", serviceName, shortCtrID)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(tui.BoldRed.Render(header))
|
||||||
|
|
||||||
|
logsEmpty := true
|
||||||
|
formatter := logs.NewFormatter([]string{machineName}, []string{serviceName}, false)
|
||||||
|
for entry := range ch {
|
||||||
|
logsEmpty = false
|
||||||
|
formatter.PrintEntry(entry)
|
||||||
|
}
|
||||||
|
|
||||||
|
if logsEmpty {
|
||||||
|
fmt.Println("<no logs available>")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// defaultFailedContainerLogsTail is the default number of recent log lines to print from a failed container to give
|
||||||
|
// the user immediate context without requiring a follow-up 'uc logs' invocation.
|
||||||
|
// Overridable via UNCLOUD_FAILED_CONTAINER_LOGS_TAIL.
|
||||||
|
const defaultFailedContainerLogsTail = 10
|
||||||
|
|
||||||
|
// failedContainerLogsTail returns the number of log lines to fetch from a failed container, honouring the
|
||||||
|
// UNCLOUD_FAILED_CONTAINER_LOGS_TAIL environment variable override when set and valid.
|
||||||
|
func failedContainerLogsTail() int {
|
||||||
|
if v := os.Getenv("UNCLOUD_FAILED_CONTAINER_LOGS_TAIL"); v != "" {
|
||||||
|
if tail, err := logs.Tail(v); err == nil && (tail == -1 || tail > 0) {
|
||||||
|
return tail
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return defaultFailedContainerLogsTail
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,10 @@ import (
|
|||||||
|
|
||||||
const docsDir = "website/docs/9-cli-reference"
|
const docsDir = "website/docs/9-cli-reference"
|
||||||
|
|
||||||
|
type docOptions struct {
|
||||||
|
manual bool
|
||||||
|
}
|
||||||
|
|
||||||
type cmdWrapper struct {
|
type cmdWrapper struct {
|
||||||
cmd *cobra.Command
|
cmd *cobra.Command
|
||||||
}
|
}
|
||||||
@@ -20,6 +24,7 @@ type cmdWrapper struct {
|
|||||||
// NewDocsCommand creates a new hidden command to generate CLI reference docs.
|
// NewDocsCommand creates a new hidden command to generate CLI reference docs.
|
||||||
func NewDocsCommand() *cobra.Command {
|
func NewDocsCommand() *cobra.Command {
|
||||||
wrapper := &cmdWrapper{}
|
wrapper := &cmdWrapper{}
|
||||||
|
opts := docOptions{}
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "docs",
|
Use: "docs",
|
||||||
Short: "Generate Uncloud CLI reference docs",
|
Short: "Generate Uncloud CLI reference docs",
|
||||||
@@ -29,6 +34,28 @@ func NewDocsCommand() *cobra.Command {
|
|||||||
Args: cobra.NoArgs,
|
Args: cobra.NoArgs,
|
||||||
ValidArgsFunction: cobra.NoFileCompletions,
|
ValidArgsFunction: cobra.NoFileCompletions,
|
||||||
RunE: func(cmd *cobra.Command, _ []string) error {
|
RunE: func(cmd *cobra.Command, _ []string) error {
|
||||||
|
if opts.manual {
|
||||||
|
header := &doc.GenManHeader{
|
||||||
|
Title: "Uncloud",
|
||||||
|
Section: "1",
|
||||||
|
Source: "Uncloud https://uncloud.run",
|
||||||
|
}
|
||||||
|
if err := doc.GenManTree(cmd.Root(), header, "."); err != nil {
|
||||||
|
return fmt.Errorf("generate CLI manual pages: %w", err)
|
||||||
|
}
|
||||||
|
completionFiles, err := filepath.Glob("uc-completion*.1")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("list generated manual pages: %w", err)
|
||||||
|
}
|
||||||
|
for _, f := range completionFiles {
|
||||||
|
if err = os.Remove(f); err != nil {
|
||||||
|
return fmt.Errorf("remove '%s': %w", f, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Remove existing markdown files.
|
// Remove existing markdown files.
|
||||||
mdFiles, err := filepath.Glob(filepath.Join(docsDir, "*.md"))
|
mdFiles, err := filepath.Glob(filepath.Join(docsDir, "*.md"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -72,6 +99,8 @@ func NewDocsCommand() *cobra.Command {
|
|||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
cmd.Flags().BoolVar(&opts.manual, "manual", false,
|
||||||
|
"Generate Uncloud manual pages in the current directory.")
|
||||||
|
|
||||||
wrapper.cmd = cmd
|
wrapper.cmd = cmd
|
||||||
return cmd
|
return cmd
|
||||||
|
|||||||
+11
-18
@@ -10,13 +10,13 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"charm.land/lipgloss/v2"
|
"charm.land/lipgloss/v2"
|
||||||
"charm.land/lipgloss/v2/table"
|
|
||||||
"github.com/charmbracelet/colorprofile"
|
"github.com/charmbracelet/colorprofile"
|
||||||
"github.com/containerd/platforms"
|
"github.com/containerd/platforms"
|
||||||
"github.com/docker/docker/api/types/image"
|
"github.com/docker/docker/api/types/image"
|
||||||
"github.com/docker/go-units"
|
"github.com/docker/go-units"
|
||||||
|
|
||||||
"github.com/psviderski/uncloud/internal/cli"
|
"github.com/psviderski/uncloud/internal/cli"
|
||||||
|
"github.com/psviderski/uncloud/internal/cli/tui"
|
||||||
"github.com/psviderski/uncloud/pkg/api"
|
"github.com/psviderski/uncloud/pkg/api"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
@@ -260,22 +260,15 @@ func formatImageTable(rows []imageRow) string {
|
|||||||
columns[5].hide = true
|
columns[5].hide = true
|
||||||
}
|
}
|
||||||
|
|
||||||
t := table.New().
|
// Hide the "STORE" column if all machines use the containerd store.
|
||||||
// Remove the default border.
|
hasNonContainerd := slices.ContainsFunc(rows, func(r imageRow) bool {
|
||||||
Border(lipgloss.Border{}).
|
return r.store != "containerd"
|
||||||
BorderTop(false).
|
})
|
||||||
BorderBottom(false).
|
if !hasNonContainerd {
|
||||||
BorderLeft(false).
|
columns[6].hide = true
|
||||||
BorderRight(false).
|
}
|
||||||
BorderHeader(false).
|
|
||||||
BorderColumn(false).
|
t := tui.NewTable()
|
||||||
StyleFunc(func(row, col int) lipgloss.Style {
|
|
||||||
if row == table.HeaderRow {
|
|
||||||
return lipgloss.NewStyle().Bold(true).PaddingRight(3)
|
|
||||||
}
|
|
||||||
// Regular style for data rows with padding.
|
|
||||||
return lipgloss.NewStyle().PaddingRight(3)
|
|
||||||
})
|
|
||||||
|
|
||||||
var headers []string
|
var headers []string
|
||||||
for _, col := range columns {
|
for _, col := range columns {
|
||||||
@@ -288,7 +281,7 @@ func formatImageTable(rows []imageRow) string {
|
|||||||
for _, row := range rows {
|
for _, row := range rows {
|
||||||
values := []string{
|
values := []string{
|
||||||
row.id,
|
row.id,
|
||||||
row.name,
|
tui.FormatImage(row.name, tui.NoStyle),
|
||||||
row.platforms,
|
row.platforms,
|
||||||
row.createdHuman,
|
row.createdHuman,
|
||||||
row.size,
|
row.size,
|
||||||
|
|||||||
@@ -40,17 +40,18 @@ func NewAddCommand() *cobra.Command {
|
|||||||
Long: `Add a new machine to an existing Uncloud cluster.
|
Long: `Add a new machine to an existing Uncloud cluster.
|
||||||
|
|
||||||
Connection methods:
|
Connection methods:
|
||||||
ssh://user@host - Use built-in SSH library (default, no prefix required)
|
[ssh://]user@host - Use system 'ssh' command with full SSH config support (default, no prefix required)
|
||||||
ssh+cli://user@host - Use system SSH command (supports ProxyJump, SSH config)`,
|
ssh+go://user@host - Use Go's built-in SSH library`,
|
||||||
Args: cobra.ExactArgs(1),
|
Args: cobra.ExactArgs(1),
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
cli.BindEnvToFlag(cmd, "yes", "UNCLOUD_AUTO_CONFIRM")
|
cli.BindEnvToFlag(cmd, "yes", "UNCLOUD_AUTO_CONFIRM")
|
||||||
|
|
||||||
uncli := cmd.Context().Value("cli").(*cli.CLI)
|
uncli := cmd.Context().Value("cli").(*cli.CLI)
|
||||||
|
|
||||||
// Determine if SSH CLI needs to be used and strip scheme
|
// Determine connection mode and strip scheme.
|
||||||
destination := args[0]
|
destination := args[0]
|
||||||
useSSHCLI := strings.HasPrefix(destination, "ssh+cli://")
|
useSSHGo := strings.HasPrefix(destination, "ssh+go://")
|
||||||
|
destination = strings.TrimPrefix(destination, "ssh+go://")
|
||||||
destination = strings.TrimPrefix(destination, "ssh+cli://")
|
destination = strings.TrimPrefix(destination, "ssh+cli://")
|
||||||
destination = strings.TrimPrefix(destination, "ssh://")
|
destination = strings.TrimPrefix(destination, "ssh://")
|
||||||
|
|
||||||
@@ -59,11 +60,11 @@ Connection methods:
|
|||||||
return fmt.Errorf("parse remote machine: %w", err)
|
return fmt.Errorf("parse remote machine: %w", err)
|
||||||
}
|
}
|
||||||
remoteMachine := &cli.RemoteMachine{
|
remoteMachine := &cli.RemoteMachine{
|
||||||
User: user,
|
User: user,
|
||||||
Host: host,
|
Host: host,
|
||||||
Port: port,
|
Port: port,
|
||||||
KeyPath: opts.sshKey,
|
KeyPath: opts.sshKey,
|
||||||
UseSSHCLI: useSSHCLI,
|
UseSSHGo: useSSHGo,
|
||||||
}
|
}
|
||||||
|
|
||||||
return add(cmd.Context(), uncli, remoteMachine, opts)
|
return add(cmd.Context(), uncli, remoteMachine, opts)
|
||||||
|
|||||||
+14
-11
@@ -45,8 +45,8 @@ func NewInitCommand() *cobra.Command {
|
|||||||
This command creates a new context in your Uncloud config to manage the cluster.
|
This command creates a new context in your Uncloud config to manage the cluster.
|
||||||
|
|
||||||
Connection methods:
|
Connection methods:
|
||||||
ssh://user@host - Use built-in SSH library (default, no prefix required)
|
[ssh://]user@host - Use system 'ssh' command with full SSH config support (default, no prefix required)
|
||||||
ssh+cli://user@host - Use system SSH command (supports ProxyJump, SSH config)`,
|
ssh+go://user@host - Use Go's built-in SSH library`,
|
||||||
Example: ` # Initialise a new cluster with default settings.
|
Example: ` # Initialise a new cluster with default settings.
|
||||||
uc machine init root@<your-server-ip>
|
uc machine init root@<your-server-ip>
|
||||||
|
|
||||||
@@ -68,9 +68,10 @@ Connection methods:
|
|||||||
|
|
||||||
var remoteMachine *cli.RemoteMachine
|
var remoteMachine *cli.RemoteMachine
|
||||||
if len(args) > 0 {
|
if len(args) > 0 {
|
||||||
// Determine if SSH CLI is requested and strip scheme
|
// Determine connection mode and strip scheme.
|
||||||
destination := args[0]
|
destination := args[0]
|
||||||
useSSHCLI := strings.HasPrefix(destination, "ssh+cli://")
|
useSSHGo := strings.HasPrefix(destination, "ssh+go://")
|
||||||
|
destination = strings.TrimPrefix(destination, "ssh+go://")
|
||||||
destination = strings.TrimPrefix(destination, "ssh+cli://")
|
destination = strings.TrimPrefix(destination, "ssh+cli://")
|
||||||
destination = strings.TrimPrefix(destination, "ssh://")
|
destination = strings.TrimPrefix(destination, "ssh://")
|
||||||
|
|
||||||
@@ -79,11 +80,11 @@ Connection methods:
|
|||||||
return fmt.Errorf("parse remote machine: %w", err)
|
return fmt.Errorf("parse remote machine: %w", err)
|
||||||
}
|
}
|
||||||
remoteMachine = &cli.RemoteMachine{
|
remoteMachine = &cli.RemoteMachine{
|
||||||
User: user,
|
User: user,
|
||||||
Host: host,
|
Host: host,
|
||||||
Port: port,
|
Port: port,
|
||||||
KeyPath: opts.sshKey,
|
KeyPath: opts.sshKey,
|
||||||
UseSSHCLI: useSSHCLI,
|
UseSSHGo: useSSHGo,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,11 +150,13 @@ Connection methods:
|
|||||||
|
|
||||||
func initCluster(ctx context.Context, uncli *cli.CLI, remoteMachine *cli.RemoteMachine, opts initOptions) error {
|
func initCluster(ctx context.Context, uncli *cli.CLI, remoteMachine *cli.RemoteMachine, opts initOptions) error {
|
||||||
if uncli.Config == nil {
|
if uncli.Config == nil {
|
||||||
// Config is nil when connecting directly to a remote machine (--connect) without using Uncloud config.
|
// Config is nil when connecting directly to a remote machine (--connect) without using Uncloud config
|
||||||
|
// or when being logged in on a machine and using the uncloud socket directly.
|
||||||
return fmt.Errorf(
|
return fmt.Errorf(
|
||||||
"do not use --connect when initialising a new cluster: --connect is for overriding the connection " +
|
"do not use --connect when initialising a new cluster: --connect is for overriding the connection " +
|
||||||
"to an existing cluster, but 'machine init' creates a new one and writes the new cluster context " +
|
"to an existing cluster, but 'machine init' creates a new one and writes the new cluster context " +
|
||||||
"to the Uncloud config file (--uncloud-config)")
|
"to the Uncloud config file (--uncloud-config), when logged in on a cluster machine, 'machine init' " +
|
||||||
|
"is not supported")
|
||||||
}
|
}
|
||||||
|
|
||||||
netPrefix, err := netip.ParsePrefix(opts.network)
|
netPrefix, err := netip.ParsePrefix(opts.network)
|
||||||
|
|||||||
@@ -0,0 +1,132 @@
|
|||||||
|
package machine
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/psviderski/uncloud/internal/cli"
|
||||||
|
"github.com/psviderski/uncloud/internal/cli/logs"
|
||||||
|
"github.com/psviderski/uncloud/internal/journal"
|
||||||
|
"github.com/psviderski/uncloud/pkg/api"
|
||||||
|
"github.com/psviderski/uncloud/pkg/client"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewLogsCommand() *cobra.Command {
|
||||||
|
var options logs.Options
|
||||||
|
|
||||||
|
cmd := &cobra.Command{
|
||||||
|
Use: "logs [SERVICE...]",
|
||||||
|
Aliases: []string{"log"},
|
||||||
|
Short: "View systemd service logs.",
|
||||||
|
Long: `View logs from the specified systemd service(s) across all machines in the cluster.
|
||||||
|
Use -m to restrict to specific machines.
|
||||||
|
|
||||||
|
Supported services:
|
||||||
|
uncloud the Uncloud daemon
|
||||||
|
docker the Docker daemon
|
||||||
|
uncloud-corrosion the Corrosion distributed state store
|
||||||
|
|
||||||
|
If no services are specified, streams logs from the uncloud service.`,
|
||||||
|
Example: ` # View recent logs for the uncloud service.
|
||||||
|
uc machine logs
|
||||||
|
uc machine logs uncloud
|
||||||
|
|
||||||
|
# Stream logs in real-time (follow mode).
|
||||||
|
uc machine logs -f uncloud
|
||||||
|
|
||||||
|
# View logs from multiple services.
|
||||||
|
uc machine logs uncloud docker uncloud-corrosion
|
||||||
|
|
||||||
|
# Show last 20 lines per machine (default is 100).
|
||||||
|
uc machine logs -n 20 docker
|
||||||
|
|
||||||
|
# Show all logs without line limit.
|
||||||
|
uc machine logs -n all docker
|
||||||
|
|
||||||
|
# View logs from a specific time range.
|
||||||
|
uc machine logs --since 3h --until 1h30m docker
|
||||||
|
|
||||||
|
# View logs only from specific machines.
|
||||||
|
uc machine logs -m machine1,machine2 uncloud uncloud-corrosion`,
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
uncli := cmd.Context().Value("cli").(*cli.CLI)
|
||||||
|
return runLogs(cmd.Context(), uncli, args, options)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.Flags().AddFlagSet(logs.Flags(&options))
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func runLogs(ctx context.Context, uncli *cli.CLI, units []string, opts logs.Options) error {
|
||||||
|
if len(units) == 0 {
|
||||||
|
units = []string{journal.UnitUncloud}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, unit := range units {
|
||||||
|
if !journal.ValidUnit(unit) {
|
||||||
|
return fmt.Errorf("invalid systemd service '%s'", unit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tail, err := logs.Tail(opts.Tail)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
c, err := uncli.ConnectCluster(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("connect to cluster: %w", err)
|
||||||
|
}
|
||||||
|
defer c.Close()
|
||||||
|
|
||||||
|
logsOpts := api.ServiceLogsOptions{
|
||||||
|
Follow: opts.Follow,
|
||||||
|
Tail: tail,
|
||||||
|
Since: opts.Since,
|
||||||
|
Until: opts.Until,
|
||||||
|
Machines: cli.ExpandCommaSeparatedValues(opts.Machines),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve machine records for the formatter's column width computation.
|
||||||
|
machines, err := c.ListMachines(ctx, &api.MachineFilter{
|
||||||
|
NamesOrIDs: logsOpts.Machines,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("list machines: %w", err)
|
||||||
|
}
|
||||||
|
machineNames := make([]string, 0, len(machines))
|
||||||
|
for _, m := range machines {
|
||||||
|
machineNames = append(machineNames, m.Machine.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect one log stream per unit. MachineLogs merges across machines internally.
|
||||||
|
unitStreams := make([]<-chan api.ServiceLogEntry, 0, len(units))
|
||||||
|
for _, unit := range units {
|
||||||
|
ch, err := c.MachineLogs(ctx, unit, logsOpts)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("stream logs for systemd service '%s': %w", unit, err)
|
||||||
|
}
|
||||||
|
unitStreams = append(unitStreams, ch)
|
||||||
|
}
|
||||||
|
|
||||||
|
var stream <-chan api.ServiceLogEntry
|
||||||
|
if len(unitStreams) == 1 {
|
||||||
|
stream = unitStreams[0]
|
||||||
|
} else {
|
||||||
|
// Each MachineLogs stream already runs its own inner merger with stall detection,
|
||||||
|
// so the outer merger across units skips it to avoid duplicate warnings.
|
||||||
|
merger := client.NewLogMerger(unitStreams, client.LogMergerOptions{})
|
||||||
|
stream = merger.Stream()
|
||||||
|
}
|
||||||
|
|
||||||
|
formatter := logs.NewFormatter(machineNames, units, opts.UTC)
|
||||||
|
|
||||||
|
// Print merged logs.
|
||||||
|
for entry := range stream {
|
||||||
|
formatter.PrintEntry(entry)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
+15
-15
@@ -4,11 +4,10 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"os"
|
|
||||||
"strings"
|
"strings"
|
||||||
"text/tabwriter"
|
|
||||||
|
|
||||||
"github.com/psviderski/uncloud/internal/cli"
|
"github.com/psviderski/uncloud/internal/cli"
|
||||||
|
"github.com/psviderski/uncloud/internal/cli/tui"
|
||||||
"github.com/psviderski/uncloud/internal/machine/network"
|
"github.com/psviderski/uncloud/internal/machine/network"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
@@ -39,12 +38,9 @@ func list(ctx context.Context, uncli *cli.CLI) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Print the list of machines in a table format.
|
// Print the list of machines in a table format.
|
||||||
tw := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0)
|
t := tui.NewTable()
|
||||||
// Print header.
|
t.Headers("NAME", "STATE", "ADDRESS", "PUBLIC IP", "WIREGUARD ENDPOINTS", "MACHINE ID")
|
||||||
if _, err = fmt.Fprintln(tw, "NAME\tSTATE\tADDRESS\tPUBLIC IP\tWIREGUARD ENDPOINTS\tMACHINE ID"); err != nil {
|
|
||||||
return fmt.Errorf("write header: %w", err)
|
|
||||||
}
|
|
||||||
// Print rows.
|
|
||||||
for _, member := range machines {
|
for _, member := range machines {
|
||||||
m := member.Machine
|
m := member.Machine
|
||||||
subnet, _ := m.Network.Subnet.ToPrefix()
|
subnet, _ := m.Network.Subnet.ToPrefix()
|
||||||
@@ -62,14 +58,18 @@ func list(ctx context.Context, uncli *cli.CLI) error {
|
|||||||
endpoints[i] = addrPort.String()
|
endpoints[i] = addrPort.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err = fmt.Fprintf(
|
t.Row(
|
||||||
tw, "%s\t%s\t%s\t%s\t%s\t%s\n", m.Name, capitalise(member.State.String()), subnet, publicIP,
|
m.Name,
|
||||||
strings.Join(endpoints, ", "), member.Machine.Id,
|
capitalise(member.State.String()),
|
||||||
); err != nil {
|
subnet.String(),
|
||||||
return fmt.Errorf("write row: %w", err)
|
publicIP,
|
||||||
}
|
strings.Join(endpoints, tui.Faint.Render(", ")),
|
||||||
|
member.Machine.Id,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
return tw.Flush()
|
|
||||||
|
fmt.Println(t)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// capitalise returns a string where the first character is upper case, and the rest is lower case.
|
// capitalise returns a string where the first character is upper case, and the rest is lower case.
|
||||||
|
|||||||
@@ -76,9 +76,12 @@ func remove(ctx context.Context, uncli *cli.CLI, nameOrID string, opts removeOpt
|
|||||||
return fmt.Errorf("list machines: %w", err)
|
return fmt.Errorf("list machines: %w", err)
|
||||||
}
|
}
|
||||||
if len(allMachines) > 1 {
|
if len(allMachines) > 1 {
|
||||||
return errors.New("cannot remove the machine you are currently connected to. " +
|
return fmt.Errorf("cannot remove the machine you are currently connected to. "+
|
||||||
"Please connect to another machine in the cluster and try again. " +
|
"Please connect to another machine in the cluster and try again. "+
|
||||||
"Use --connect flag or update 'connections' for the cluster context in your Uncloud config")
|
"Change the default connection for the current cluster using 'uc ctx conn' or "+
|
||||||
|
"manually update 'connections' in your Uncloud config (%s). "+
|
||||||
|
"For more information on connecting to a cluster, see "+
|
||||||
|
"https://uncloud.run/docs/concepts/clusters/connecting/", uncli.Config.Path())
|
||||||
// It's ok to remove the proxy machine if it's the last one in the cluster.
|
// It's ok to remove the proxy machine if it's the last one in the cluster.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -206,7 +209,7 @@ func formatContainerTree(containers []api.ServiceContainer) string {
|
|||||||
// Add containers as children.
|
// Add containers as children.
|
||||||
for _, ctr := range ctrs {
|
for _, ctr := range ctrs {
|
||||||
state, _ := ctr.HumanState()
|
state, _ := ctr.HumanState()
|
||||||
info := fmt.Sprintf("%s • %s • %s", ctr.Name, ctr.Config.Image, state)
|
info := fmt.Sprintf("%s • %s • %s", ctr.Name, tui.FormatImage(ctr.Config.Image, tui.NoStyle), state)
|
||||||
t.Child(info)
|
t.Child(info)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,8 +14,10 @@ func NewRootCommand() *cobra.Command {
|
|||||||
NewAddCommand(),
|
NewAddCommand(),
|
||||||
NewInitCommand(),
|
NewInitCommand(),
|
||||||
NewListCommand(),
|
NewListCommand(),
|
||||||
|
NewLogsCommand(),
|
||||||
NewRenameCommand(),
|
NewRenameCommand(),
|
||||||
NewRmCommand(),
|
NewRmCommand(),
|
||||||
|
NewRTTCommand(),
|
||||||
NewUpdateCommand(),
|
NewUpdateCommand(),
|
||||||
)
|
)
|
||||||
return cmd
|
return cmd
|
||||||
|
|||||||
@@ -0,0 +1,111 @@
|
|||||||
|
package machine
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/psviderski/uncloud/internal/cli"
|
||||||
|
"github.com/psviderski/uncloud/internal/cli/tui"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"google.golang.org/protobuf/types/known/emptypb"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewRTTCommand() *cobra.Command {
|
||||||
|
cmd := &cobra.Command{
|
||||||
|
Use: "rtt",
|
||||||
|
Short: "Show round-trip times between machines.",
|
||||||
|
Long: `Show round-trip times between machines.
|
||||||
|
|
||||||
|
Round-trip time statistics are collected from the Corrosion gossip protocol
|
||||||
|
and represent the median of recent RTT samples between each pair of machines
|
||||||
|
in the cluster. The values shown include the median RTT and standard deviation
|
||||||
|
for each machine-to-machine connection.`,
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
uncli := cmd.Context().Value("cli").(*cli.CLI)
|
||||||
|
return rtt(cmd.Context(), uncli)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func rtt(ctx context.Context, uncli *cli.CLI) error {
|
||||||
|
client, err := uncli.ConnectCluster(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("connect to cluster: %w", err)
|
||||||
|
}
|
||||||
|
defer client.Close()
|
||||||
|
|
||||||
|
// Setup context to proxy request to all machines.
|
||||||
|
ctx, _, err = client.ProxyMachinesContext(ctx, nil)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("setup proxy context: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := client.MachineClient.InspectMachine(ctx, &emptypb.Empty{})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("inspect machines: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map machine IDs to names for display from the response.
|
||||||
|
machineNames := make(map[string]string)
|
||||||
|
for _, m := range resp.Machines {
|
||||||
|
if m.Machine == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
machineNames[m.Machine.Id] = m.Machine.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
type row struct {
|
||||||
|
machine string
|
||||||
|
peer string
|
||||||
|
median time.Duration
|
||||||
|
stdDev time.Duration
|
||||||
|
}
|
||||||
|
var rows []row
|
||||||
|
|
||||||
|
for _, m := range resp.Machines {
|
||||||
|
// Unlikely to occur, but might be a possible edge case when
|
||||||
|
// a machine is still initializing. So just to be safe.
|
||||||
|
if m.Machine == nil || m.Rtts == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for peerID, stats := range m.Rtts {
|
||||||
|
peerName := peerID
|
||||||
|
if name, ok := machineNames[peerID]; ok {
|
||||||
|
peerName = name
|
||||||
|
}
|
||||||
|
rows = append(rows, row{
|
||||||
|
machine: m.Machine.Name,
|
||||||
|
peer: peerName,
|
||||||
|
median: stats.Median.AsDuration(),
|
||||||
|
stdDev: stats.StdDev.AsDuration(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort by machine name then peer name.
|
||||||
|
sort.Slice(rows, func(i, j int) bool {
|
||||||
|
if rows[i].machine == rows[j].machine {
|
||||||
|
return rows[i].peer < rows[j].peer
|
||||||
|
}
|
||||||
|
return rows[i].machine < rows[j].machine
|
||||||
|
})
|
||||||
|
|
||||||
|
// Print table.
|
||||||
|
t := tui.NewTable()
|
||||||
|
t.Headers("MACHINE", "PEER", "MEDIAN", "STDDEV")
|
||||||
|
|
||||||
|
for _, r := range rows {
|
||||||
|
t.Row(r.machine, r.peer, tui.FormatRTT(r.median), formatRTTStdDev(r.stdDev))
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(t)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// formatRTTStdDev formats a round-trip time standard deviation with one decimal place, e.g. "±19.4ms".
|
||||||
|
func formatRTTStdDev(d time.Duration) string {
|
||||||
|
return fmt.Sprintf("±%.1fms", float64(d)/float64(time.Millisecond))
|
||||||
|
}
|
||||||
+23
-8
@@ -11,7 +11,7 @@ import (
|
|||||||
cmdcontext "github.com/psviderski/uncloud/cmd/uncloud/context"
|
cmdcontext "github.com/psviderski/uncloud/cmd/uncloud/context"
|
||||||
"github.com/psviderski/uncloud/cmd/uncloud/dns"
|
"github.com/psviderski/uncloud/cmd/uncloud/dns"
|
||||||
"github.com/psviderski/uncloud/cmd/uncloud/image"
|
"github.com/psviderski/uncloud/cmd/uncloud/image"
|
||||||
"github.com/psviderski/uncloud/cmd/uncloud/machine"
|
cmdmachine "github.com/psviderski/uncloud/cmd/uncloud/machine"
|
||||||
"github.com/psviderski/uncloud/cmd/uncloud/service"
|
"github.com/psviderski/uncloud/cmd/uncloud/service"
|
||||||
"github.com/psviderski/uncloud/cmd/uncloud/volume"
|
"github.com/psviderski/uncloud/cmd/uncloud/volume"
|
||||||
"github.com/psviderski/uncloud/cmd/uncloud/wg"
|
"github.com/psviderski/uncloud/cmd/uncloud/wg"
|
||||||
@@ -19,6 +19,7 @@ import (
|
|||||||
"github.com/psviderski/uncloud/internal/cli/config"
|
"github.com/psviderski/uncloud/internal/cli/config"
|
||||||
"github.com/psviderski/uncloud/internal/fs"
|
"github.com/psviderski/uncloud/internal/fs"
|
||||||
"github.com/psviderski/uncloud/internal/log"
|
"github.com/psviderski/uncloud/internal/log"
|
||||||
|
"github.com/psviderski/uncloud/internal/machine"
|
||||||
"github.com/psviderski/uncloud/internal/version"
|
"github.com/psviderski/uncloud/internal/version"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
@@ -46,24 +47,29 @@ func main() {
|
|||||||
|
|
||||||
var conn *config.MachineConnection
|
var conn *config.MachineConnection
|
||||||
if opts.connect != "" {
|
if opts.connect != "" {
|
||||||
if strings.HasPrefix(opts.connect, "tcp://") {
|
if after, ok := strings.CutPrefix(opts.connect, "tcp://"); ok {
|
||||||
addrPort, err := netip.ParseAddrPort(opts.connect[len("tcp://"):])
|
addrPort, err := netip.ParseAddrPort(after)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("parse TCP address: %w", err)
|
return fmt.Errorf("parse TCP address: %w", err)
|
||||||
}
|
}
|
||||||
conn = &config.MachineConnection{
|
conn = &config.MachineConnection{
|
||||||
TCP: &addrPort,
|
TCP: &addrPort,
|
||||||
}
|
}
|
||||||
} else if strings.HasPrefix(opts.connect, "ssh+cli://") {
|
} else if after, ok := strings.CutPrefix(opts.connect, "ssh+go://"); ok {
|
||||||
dest := opts.connect[len("ssh+cli://"):]
|
|
||||||
conn = &config.MachineConnection{
|
conn = &config.MachineConnection{
|
||||||
SSHCLI: config.SSHDestination(dest),
|
SSHGo: config.SSHDestination(after),
|
||||||
|
}
|
||||||
|
} else if after, ok := strings.CutPrefix(opts.connect, "ssh+cli://"); ok {
|
||||||
|
// Backward-compatible alias for ssh://.
|
||||||
|
conn = &config.MachineConnection{
|
||||||
|
SSH: config.SSHDestination(after),
|
||||||
}
|
}
|
||||||
} else if strings.HasPrefix(opts.connect, "unix://") {
|
} else if strings.HasPrefix(opts.connect, "unix://") {
|
||||||
conn = &config.MachineConnection{
|
conn = &config.MachineConnection{
|
||||||
Unix: opts.connect[len("unix://"):],
|
Unix: opts.connect[len("unix://"):],
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
// Default: system ssh CLI command (no prefix or ssh:// prefix).
|
||||||
dest := strings.TrimPrefix(opts.connect, "ssh://")
|
dest := strings.TrimPrefix(opts.connect, "ssh://")
|
||||||
conn = &config.MachineConnection{
|
conn = &config.MachineConnection{
|
||||||
SSH: config.SSHDestination(dest),
|
SSH: config.SSHDestination(dest),
|
||||||
@@ -72,6 +78,15 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
configPath := fs.ExpandHomeDir(opts.configPath)
|
configPath := fs.ExpandHomeDir(opts.configPath)
|
||||||
|
|
||||||
|
if opts.connect == "" {
|
||||||
|
if !fs.Exists(configPath) && fs.Exists(machine.DefaultUncloudSockPath) {
|
||||||
|
conn = &config.MachineConnection{
|
||||||
|
Unix: machine.DefaultUncloudSockPath,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
uncli, err := cli.New(configPath, conn, opts.context)
|
uncli, err := cli.New(configPath, conn, opts.context)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("initialise CLI: %w", err)
|
return fmt.Errorf("initialise CLI: %w", err)
|
||||||
@@ -83,7 +98,7 @@ func main() {
|
|||||||
|
|
||||||
cmd.PersistentFlags().StringVar(&opts.connect, "connect", "",
|
cmd.PersistentFlags().StringVar(&opts.connect, "connect", "",
|
||||||
"Connect to a remote cluster machine without using the Uncloud configuration file. [$UNCLOUD_CONNECT]\n"+
|
"Connect to a remote cluster machine without using the Uncloud configuration file. [$UNCLOUD_CONNECT]\n"+
|
||||||
"Format: [ssh://]user@host[:port], ssh+cli://user@host[:port], tcp://host:port, or unix:///path/to/uncloud.sock")
|
"Format: [ssh://]user@host[:port], ssh+go://user@host[:port], tcp://host:port, or unix:///path/to/uncloud.sock")
|
||||||
cmd.PersistentFlags().StringVar(&opts.configPath, "uncloud-config", "~/.config/uncloud/config.yaml",
|
cmd.PersistentFlags().StringVar(&opts.configPath, "uncloud-config", "~/.config/uncloud/config.yaml",
|
||||||
"Path to the Uncloud configuration file. [$UNCLOUD_CONFIG]")
|
"Path to the Uncloud configuration file. [$UNCLOUD_CONFIG]")
|
||||||
_ = cmd.MarkPersistentFlagFilename("uncloud-config", "yaml", "yml")
|
_ = cmd.MarkPersistentFlagFilename("uncloud-config", "yaml", "yml")
|
||||||
@@ -123,7 +138,7 @@ func main() {
|
|||||||
cmdcontext.NewRootCommand(),
|
cmdcontext.NewRootCommand(),
|
||||||
dns.NewRootCommand(),
|
dns.NewRootCommand(),
|
||||||
image.NewRootCommand(),
|
image.NewRootCommand(),
|
||||||
machine.NewRootCommand(),
|
cmdmachine.NewRootCommand(),
|
||||||
service.NewRootCommand(),
|
service.NewRootCommand(),
|
||||||
service.NewExecCommand("service"),
|
service.NewExecCommand("service"),
|
||||||
service.NewInspectCommand("service"),
|
service.NewInspectCommand("service"),
|
||||||
|
|||||||
+47
-34
@@ -8,14 +8,13 @@ import (
|
|||||||
|
|
||||||
"charm.land/huh/v2/spinner"
|
"charm.land/huh/v2/spinner"
|
||||||
"charm.land/lipgloss/v2"
|
"charm.land/lipgloss/v2"
|
||||||
"charm.land/lipgloss/v2/table"
|
|
||||||
"github.com/docker/docker/api/types/container"
|
"github.com/docker/docker/api/types/container"
|
||||||
"github.com/docker/go-units"
|
"github.com/docker/go-units"
|
||||||
"github.com/psviderski/uncloud/internal/cli/tui"
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
|
|
||||||
"github.com/psviderski/uncloud/internal/cli"
|
"github.com/psviderski/uncloud/internal/cli"
|
||||||
|
"github.com/psviderski/uncloud/internal/cli/tui"
|
||||||
|
"github.com/psviderski/uncloud/pkg/api"
|
||||||
"github.com/psviderski/uncloud/pkg/client"
|
"github.com/psviderski/uncloud/pkg/client"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -67,12 +66,13 @@ type containerInfo struct {
|
|||||||
serviceName string
|
serviceName string
|
||||||
machineName string
|
machineName string
|
||||||
id string
|
id string
|
||||||
name string
|
|
||||||
image string
|
image string
|
||||||
status string
|
status string
|
||||||
highlight containerHighlight
|
highlight containerHighlight
|
||||||
created time.Time
|
created time.Time
|
||||||
ip string
|
ip string
|
||||||
|
// Hook type (e.g., "pre-deploy"), empty for regular containers.
|
||||||
|
hook string
|
||||||
}
|
}
|
||||||
|
|
||||||
func runPs(ctx context.Context, uncli *cli.CLI, opts psOptions) error {
|
func runPs(ctx context.Context, uncli *cli.CLI, opts psOptions) error {
|
||||||
@@ -132,24 +132,22 @@ func runPs(ctx context.Context, uncli *cli.CLI, opts psOptions) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func printContainers(containers []containerInfo) error {
|
func printContainers(containers []containerInfo) error {
|
||||||
t := table.New().
|
t := tui.NewTable()
|
||||||
// Remove the default border.
|
|
||||||
Border(lipgloss.Border{}).
|
|
||||||
BorderTop(false).
|
|
||||||
BorderBottom(false).
|
|
||||||
BorderLeft(false).
|
|
||||||
BorderRight(false).
|
|
||||||
BorderHeader(false).
|
|
||||||
BorderColumn(false).
|
|
||||||
StyleFunc(func(row, col int) lipgloss.Style {
|
|
||||||
if row == table.HeaderRow {
|
|
||||||
return lipgloss.NewStyle().Bold(true).PaddingRight(3)
|
|
||||||
}
|
|
||||||
// Regular style for data rows with padding.
|
|
||||||
return lipgloss.NewStyle().PaddingRight(3)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Headers("SERVICE", "CONTAINER ID", "CONTAINER NAME", "IMAGE", "CREATED", "STATUS", "IP ADDRESS", "MACHINE")
|
// Show HOOK column only when hook containers are present.
|
||||||
|
hasHooks := false
|
||||||
|
for _, ctr := range containers {
|
||||||
|
if ctr.hook != "" {
|
||||||
|
hasHooks = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if hasHooks {
|
||||||
|
t.Headers("SERVICE", "CONTAINER ID", "IMAGE", "CREATED", "STATUS", "HOOK", "IP ADDRESS", "MACHINE")
|
||||||
|
} else {
|
||||||
|
t.Headers("SERVICE", "CONTAINER ID", "IMAGE", "CREATED", "STATUS", "IP ADDRESS", "MACHINE")
|
||||||
|
}
|
||||||
|
|
||||||
for _, ctr := range containers {
|
for _, ctr := range containers {
|
||||||
id := ctr.id
|
id := ctr.id
|
||||||
@@ -171,16 +169,28 @@ func printContainers(containers []containerInfo) error {
|
|||||||
statusStyle = lipgloss.NewStyle() // Default
|
statusStyle = lipgloss.NewStyle() // Default
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Row(
|
if hasHooks {
|
||||||
ctr.serviceName,
|
t.Row(
|
||||||
id,
|
ctr.serviceName,
|
||||||
ctr.name,
|
id,
|
||||||
ctr.image,
|
tui.FormatImage(ctr.image, tui.NoStyle),
|
||||||
created,
|
created,
|
||||||
statusStyle.Render(ctr.status),
|
statusStyle.Render(ctr.status),
|
||||||
ctr.ip,
|
ctr.hook,
|
||||||
ctr.machineName,
|
ctr.ip,
|
||||||
)
|
ctr.machineName,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
t.Row(
|
||||||
|
ctr.serviceName,
|
||||||
|
id,
|
||||||
|
tui.FormatImage(ctr.image, tui.NoStyle),
|
||||||
|
created,
|
||||||
|
statusStyle.Render(ctr.status),
|
||||||
|
ctr.ip,
|
||||||
|
ctr.machineName,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println(t)
|
fmt.Println(t)
|
||||||
@@ -237,7 +247,7 @@ func collectContainers(ctx context.Context, cli *client.Client) ([]containerInfo
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, ctr := range msc.Containers {
|
for _, ctr := range append(msc.Containers, msc.HookContainers...) {
|
||||||
if ctr.Container.State == nil || ctr.Container.Config == nil {
|
if ctr.Container.State == nil || ctr.Container.Config == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -259,6 +269,9 @@ func collectContainers(ctx context.Context, cli *client.Client) ([]containerInfo
|
|||||||
highlight = highlightSuccess
|
highlight = highlightSuccess
|
||||||
} else if ctr.Container.State.Status == "running" {
|
} else if ctr.Container.State.Status == "running" {
|
||||||
highlight = highlightNormal
|
highlight = highlightNormal
|
||||||
|
} else if ctr.IsHook() && ctr.Container.State.Status == "exited" && ctr.Container.State.ExitCode == 0 {
|
||||||
|
// Hook containers (e.g., pre-deploy) are expected to exit successfully.
|
||||||
|
highlight = highlightNormal
|
||||||
} else { // Other non-critical but noteworthy states
|
} else { // Other non-critical but noteworthy states
|
||||||
highlight = highlightWarning
|
highlight = highlightWarning
|
||||||
}
|
}
|
||||||
@@ -276,12 +289,12 @@ func collectContainers(ctx context.Context, cli *client.Client) ([]containerInfo
|
|||||||
serviceName: ctr.ServiceName(),
|
serviceName: ctr.ServiceName(),
|
||||||
machineName: machineName,
|
machineName: machineName,
|
||||||
id: ctr.Container.ID,
|
id: ctr.Container.ID,
|
||||||
name: ctr.Container.Name,
|
|
||||||
image: ctr.Container.Config.Image,
|
image: ctr.Container.Config.Image,
|
||||||
status: status,
|
status: status,
|
||||||
highlight: highlight,
|
highlight: highlight,
|
||||||
created: created,
|
created: created,
|
||||||
ip: ipStr,
|
ip: ipStr,
|
||||||
|
hook: ctr.Config.Labels[api.LabelHook],
|
||||||
}
|
}
|
||||||
containers = append(containers, info)
|
containers = append(containers, info)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -111,7 +111,6 @@ func TestCollectContainers_NilMetadata(t *testing.T) {
|
|||||||
if len(containers) > 0 {
|
if len(containers) > 0 {
|
||||||
c := containers[0]
|
c := containers[0]
|
||||||
assert.Equal(t, "container1", c.id)
|
assert.Equal(t, "container1", c.id)
|
||||||
assert.Equal(t, "test-container", c.name)
|
|
||||||
assert.Equal(t, "machine-1", c.machineName, "Should fall back to the single machine name when metadata is nil")
|
assert.Equal(t, "machine-1", c.machineName, "Should fall back to the single machine name when metadata is nil")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,8 +48,7 @@ If the service has multiple replicas and no container ID is specified, the comma
|
|||||||
Args: cobra.MinimumNArgs(1),
|
Args: cobra.MinimumNArgs(1),
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
uncli := cmd.Context().Value("cli").(*cli.CLI)
|
uncli := cmd.Context().Value("cli").(*cli.CLI)
|
||||||
serviceName := args[0]
|
serviceName, command := normalizeExecArgs(args)
|
||||||
command := args[1:]
|
|
||||||
if len(command) == 0 {
|
if len(command) == 0 {
|
||||||
command = DEFAULT_COMMAND
|
command = DEFAULT_COMMAND
|
||||||
}
|
}
|
||||||
@@ -82,6 +81,15 @@ If the service has multiple replicas and no container ID is specified, the comma
|
|||||||
return execCmd
|
return execCmd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func normalizeExecArgs(args []string) (serviceName string, command []string) {
|
||||||
|
serviceName = args[0]
|
||||||
|
command = args[1:]
|
||||||
|
if len(command) > 0 && command[0] == "--" {
|
||||||
|
command = command[1:]
|
||||||
|
}
|
||||||
|
return serviceName, command
|
||||||
|
}
|
||||||
|
|
||||||
func runExec(ctx context.Context, uncli *cli.CLI, serviceName string, command []string, opts execCliOptions) error {
|
func runExec(ctx context.Context, uncli *cli.CLI, serviceName string, command []string, opts execCliOptions) error {
|
||||||
// Disable TTY allocation if not connected to a terminal
|
// Disable TTY allocation if not connected to a terminal
|
||||||
if !tui.IsStdoutTerminal() {
|
if !tui.IsStdoutTerminal() {
|
||||||
|
|||||||
@@ -0,0 +1,61 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNormalizeExecArgs(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args []string
|
||||||
|
wantServiceName string
|
||||||
|
wantCommand []string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "service only",
|
||||||
|
args: []string{"test-service"},
|
||||||
|
wantServiceName: "test-service",
|
||||||
|
wantCommand: []string{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "service with command",
|
||||||
|
args: []string{"test-service", "echo", "hello"},
|
||||||
|
wantServiceName: "test-service",
|
||||||
|
wantCommand: []string{"echo", "hello"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "service with separator and command",
|
||||||
|
args: []string{"test-service", "--", "echo", "hello"},
|
||||||
|
wantServiceName: "test-service",
|
||||||
|
wantCommand: []string{"echo", "hello"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "service with separator only",
|
||||||
|
args: []string{"test-service", "--"},
|
||||||
|
wantServiceName: "test-service",
|
||||||
|
wantCommand: []string{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "separator preserves command flag",
|
||||||
|
args: []string{"test-service", "--", "--help"},
|
||||||
|
wantServiceName: "test-service",
|
||||||
|
wantCommand: []string{"--help"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "only first separator is removed",
|
||||||
|
args: []string{"test-service", "--", "cmd", "--", "arg"},
|
||||||
|
wantServiceName: "test-service",
|
||||||
|
wantCommand: []string{"cmd", "--", "arg"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
gotServiceName, gotCommand := normalizeExecArgs(tt.args)
|
||||||
|
assert.Equal(t, tt.wantServiceName, gotServiceName)
|
||||||
|
assert.Equal(t, tt.wantCommand, gotCommand)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,14 +3,13 @@ package service
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
|
||||||
"slices"
|
"slices"
|
||||||
"text/tabwriter"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/docker/docker/pkg/stringid"
|
"github.com/docker/docker/pkg/stringid"
|
||||||
"github.com/docker/go-units"
|
"github.com/docker/go-units"
|
||||||
"github.com/psviderski/uncloud/internal/cli"
|
"github.com/psviderski/uncloud/internal/cli"
|
||||||
|
"github.com/psviderski/uncloud/internal/cli/tui"
|
||||||
"github.com/psviderski/uncloud/pkg/api"
|
"github.com/psviderski/uncloud/pkg/api"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
@@ -61,25 +60,33 @@ func inspect(ctx context.Context, uncli *cli.CLI, opts inspectOptions) error {
|
|||||||
fmt.Printf("Mode: %s\n", svc.Mode)
|
fmt.Printf("Mode: %s\n", svc.Mode)
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
|
|
||||||
|
// Combine regular and hook containers.
|
||||||
|
allContainers := append(svc.Containers, svc.HookContainers...)
|
||||||
|
|
||||||
// Parse created times for sorting and display.
|
// Parse created times for sorting and display.
|
||||||
createdTimes := make(map[string]time.Time, len(svc.Containers))
|
createdTimes := make(map[string]time.Time, len(allContainers))
|
||||||
for _, ctr := range svc.Containers {
|
for _, ctr := range allContainers {
|
||||||
createdTimes[ctr.Container.ID], _ = time.Parse(time.RFC3339Nano, ctr.Container.Created)
|
createdTimes[ctr.Container.ID], _ = time.Parse(time.RFC3339Nano, ctr.Container.Created)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort containers by created time (newest first).
|
// Sort containers by created time (newest first).
|
||||||
slices.SortFunc(svc.Containers, func(a, b api.MachineServiceContainer) int {
|
slices.SortFunc(allContainers, func(a, b api.MachineServiceContainer) int {
|
||||||
return createdTimes[b.Container.ID].Compare(createdTimes[a.Container.ID])
|
return createdTimes[b.Container.ID].Compare(createdTimes[a.Container.ID])
|
||||||
})
|
})
|
||||||
|
|
||||||
// Print the list of containers in a table format.
|
// Print the list of containers in a table format.
|
||||||
tw := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0)
|
// Show HOOK column only when hook containers are present.
|
||||||
if _, err = fmt.Fprintln(tw, "CONTAINER ID\tIMAGE\tCREATED\tSTATUS\tIP ADDRESS\tMACHINE"); err != nil {
|
hasHooks := len(svc.HookContainers) > 0
|
||||||
return fmt.Errorf("write header: %w", err)
|
|
||||||
|
t := tui.NewTable()
|
||||||
|
if hasHooks {
|
||||||
|
t.Headers("CONTAINER ID", "IMAGE", "CREATED", "STATUS", "HOOK", "IP ADDRESS", "MACHINE")
|
||||||
|
} else {
|
||||||
|
t.Headers("CONTAINER ID", "IMAGE", "CREATED", "STATUS", "IP ADDRESS", "MACHINE")
|
||||||
}
|
}
|
||||||
|
|
||||||
now := time.Now().UTC()
|
now := time.Now().UTC()
|
||||||
for _, ctr := range svc.Containers {
|
for _, ctr := range allContainers {
|
||||||
created := units.HumanDuration(now.Sub(createdTimes[ctr.Container.ID])) + " ago"
|
created := units.HumanDuration(now.Sub(createdTimes[ctr.Container.ID])) + " ago"
|
||||||
|
|
||||||
machine := machinesNamesByID[ctr.MachineID]
|
machine := machinesNamesByID[ctr.MachineID]
|
||||||
@@ -98,19 +105,28 @@ func inspect(ctx context.Context, uncli *cli.CLI, opts inspectOptions) error {
|
|||||||
ipStr = ip.String()
|
ipStr = ip.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = fmt.Fprintf(
|
if hasHooks {
|
||||||
tw,
|
t.Row(
|
||||||
"%s\t%s\t%s\t%s\t%s\t%s\n",
|
stringid.TruncateID(ctr.Container.ID),
|
||||||
stringid.TruncateID(ctr.Container.ID),
|
tui.FormatImage(ctr.Container.Config.Image, tui.NoStyle),
|
||||||
ctr.Container.Config.Image,
|
created,
|
||||||
created,
|
state,
|
||||||
state,
|
ctr.Container.Config.Labels[api.LabelHook],
|
||||||
ipStr,
|
ipStr,
|
||||||
machine,
|
machine,
|
||||||
)
|
)
|
||||||
if err != nil {
|
} else {
|
||||||
return fmt.Errorf("write row: %w", err)
|
t.Row(
|
||||||
|
stringid.TruncateID(ctr.Container.ID),
|
||||||
|
tui.FormatImage(ctr.Container.Config.Image, tui.NoStyle),
|
||||||
|
created,
|
||||||
|
state,
|
||||||
|
ipStr,
|
||||||
|
machine,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return tw.Flush()
|
|
||||||
|
fmt.Println(t)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
+49
-225
@@ -4,17 +4,11 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"image/color"
|
|
||||||
"os"
|
|
||||||
"slices"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
|
||||||
|
|
||||||
"charm.land/lipgloss/v2"
|
|
||||||
mapset "github.com/deckarep/golang-set/v2"
|
mapset "github.com/deckarep/golang-set/v2"
|
||||||
"github.com/docker/docker/pkg/stringid"
|
|
||||||
"github.com/psviderski/uncloud/internal/cli"
|
"github.com/psviderski/uncloud/internal/cli"
|
||||||
|
"github.com/psviderski/uncloud/internal/cli/logs"
|
||||||
"github.com/psviderski/uncloud/internal/cli/tui"
|
"github.com/psviderski/uncloud/internal/cli/tui"
|
||||||
"github.com/psviderski/uncloud/pkg/api"
|
"github.com/psviderski/uncloud/pkg/api"
|
||||||
"github.com/psviderski/uncloud/pkg/client"
|
"github.com/psviderski/uncloud/pkg/client"
|
||||||
@@ -22,25 +16,18 @@ import (
|
|||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
type logsOptions struct {
|
|
||||||
files []string
|
|
||||||
follow bool
|
|
||||||
tail string
|
|
||||||
since string
|
|
||||||
until string
|
|
||||||
utc bool
|
|
||||||
machines []string
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewLogsCommand(groupID string) *cobra.Command {
|
func NewLogsCommand(groupID string) *cobra.Command {
|
||||||
var options logsOptions
|
var options logs.Options
|
||||||
|
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "logs [SERVICE...]",
|
Use: "logs [SERVICE[/CONTAINER]...]",
|
||||||
Aliases: []string{"log"},
|
Aliases: []string{"log"},
|
||||||
Short: "View service logs.",
|
Short: "View service logs.",
|
||||||
Long: `View logs from all replicas of the specified service(s) across all machines in the cluster.
|
Long: `View logs from all replicas of the specified service(s) across all machines in the cluster.
|
||||||
|
|
||||||
|
To view logs from specific replicas (containers) within a service, use the SERVICE/CONTAINER form,
|
||||||
|
where CONTAINER is a container name, full ID, or unique ID prefix.
|
||||||
|
|
||||||
If no services are specified, streams logs from all services defined in the Compose file
|
If no services are specified, streams logs from all services defined in the Compose file
|
||||||
(compose.yaml by default or the file(s) specified with --file).`,
|
(compose.yaml by default or the file(s) specified with --file).`,
|
||||||
Example: ` # View recent logs for a service.
|
Example: ` # View recent logs for a service.
|
||||||
@@ -64,6 +51,9 @@ If no services are specified, streams logs from all services defined in the Comp
|
|||||||
# View logs from a specific time range.
|
# View logs from a specific time range.
|
||||||
uc logs --since 3h --until 1h30m web
|
uc logs --since 3h --until 1h30m web
|
||||||
|
|
||||||
|
# View logs only from specific replicas (containers).
|
||||||
|
uc logs web/61d57fd3428f api/2f60
|
||||||
|
|
||||||
# View logs only from replicas running on specific machines.
|
# View logs only from replicas running on specific machines.
|
||||||
uc logs -m machine1,machine2 web api`,
|
uc logs -m machine1,machine2 web api`,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
@@ -73,38 +63,24 @@ If no services are specified, streams logs from all services defined in the Comp
|
|||||||
GroupID: groupID,
|
GroupID: groupID,
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd.Flags().StringSliceVar(&options.files, "file", nil,
|
cmd.Flags().StringSliceVar(&options.Files, "file", nil,
|
||||||
"One or more Compose files to load service names from when no services are specified. (default compose.yaml)")
|
"One or more Compose files to load service names from when no services are specified. (default compose.yaml)")
|
||||||
cmd.Flags().BoolVarP(&options.follow, "follow", "f", false,
|
|
||||||
"Continually stream new logs.")
|
|
||||||
cmd.Flags().StringSliceVarP(&options.machines, "machine", "m", nil,
|
|
||||||
"Filter logs by machine name or ID. Can be specified multiple times or as a comma-separated list.")
|
|
||||||
cmd.Flags().StringVar(&options.since, "since", "",
|
|
||||||
"Show logs generated on or after the given timestamp. Accepts relative duration, RFC 3339 date, or Unix timestamp.\n"+
|
|
||||||
"Examples:\n"+
|
|
||||||
" --since 2m30s Relative duration (2 minutes 30 seconds ago)\n"+
|
|
||||||
" --since 1h Relative duration (1 hour ago)\n"+
|
|
||||||
" --since 2025-11-24 RFC 3339 date only (midnight using local timezone)\n"+
|
|
||||||
" --since 2024-05-14T22:50:00 RFC 3339 date/time using local timezone\n"+
|
|
||||||
" --since 2024-01-31T10:30:00Z RFC 3339 date/time in UTC\n"+
|
|
||||||
" --since 1763953966 Unix timestamp (seconds since January 1, 1970)")
|
|
||||||
cmd.Flags().StringVarP(&options.tail, "tail", "n", "100",
|
|
||||||
"Show the most recent logs and limit the number of lines shown per replica. Use 'all' to show all logs.")
|
|
||||||
cmd.Flags().StringVar(&options.until, "until", "",
|
|
||||||
"Show logs generated before the given timestamp. Accepts relative duration, RFC 3339 date, or Unix timestamp.\n"+
|
|
||||||
"See --since for examples.")
|
|
||||||
cmd.Flags().BoolVar(&options.utc, "utc", false,
|
|
||||||
"Print timestamps in UTC instead of local timezone.")
|
|
||||||
|
|
||||||
|
cmd.Flags().AddFlagSet(logs.Flags(&options))
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
func runLogs(ctx context.Context, uncli *cli.CLI, serviceNames []string, opts logsOptions) error {
|
func runLogs(ctx context.Context, uncli *cli.CLI, args []string, opts logs.Options) error {
|
||||||
|
serviceArgs, err := logs.ParseServiceArgs(args)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// If no services specified, try to load them from the Compose file(s).
|
// If no services specified, try to load them from the Compose file(s).
|
||||||
fromCompose := false
|
fromCompose := false
|
||||||
if len(serviceNames) == 0 {
|
if len(serviceArgs) == 0 {
|
||||||
fromCompose = true
|
fromCompose = true
|
||||||
project, err := compose.LoadProject(ctx, opts.files)
|
project, err := compose.LoadProject(ctx, opts.Files)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("load Compose file(s): %w", err)
|
return fmt.Errorf("load Compose file(s): %w", err)
|
||||||
}
|
}
|
||||||
@@ -112,20 +88,21 @@ func runLogs(ctx context.Context, uncli *cli.CLI, serviceNames []string, opts lo
|
|||||||
uncli.SetClusterContextIfUnset(compose.ClusterContext(project))
|
uncli.SetClusterContextIfUnset(compose.ClusterContext(project))
|
||||||
|
|
||||||
// View logs for all services, including disabled by inactive profiles.
|
// View logs for all services, including disabled by inactive profiles.
|
||||||
serviceNames = append(project.ServiceNames(), project.DisabledServiceNames()...)
|
composeServices := append(project.ServiceNames(), project.DisabledServiceNames()...)
|
||||||
if len(serviceNames) == 0 {
|
if len(composeServices) == 0 {
|
||||||
return errors.New("no services found in Compose file(s)")
|
return errors.New("no services found in Compose file(s)")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
serviceArgs = make([]logs.ServiceArg, len(composeServices))
|
||||||
|
for i, name := range composeServices {
|
||||||
|
serviceArgs[i] = logs.ServiceArg{Service: name}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse tail option.
|
// Parse tail option.
|
||||||
tail := -1
|
tail, err := logs.Tail(opts.Tail)
|
||||||
if opts.tail != "all" {
|
if err != nil {
|
||||||
tailInt, err := strconv.Atoi(opts.tail)
|
return err
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("invalid --tail value '%s': %w", opts.tail, err)
|
|
||||||
}
|
|
||||||
tail = tailInt
|
|
||||||
}
|
}
|
||||||
|
|
||||||
c, err := uncli.ConnectCluster(ctx)
|
c, err := uncli.ConnectCluster(ctx)
|
||||||
@@ -134,31 +111,34 @@ func runLogs(ctx context.Context, uncli *cli.CLI, serviceNames []string, opts lo
|
|||||||
}
|
}
|
||||||
defer c.Close()
|
defer c.Close()
|
||||||
|
|
||||||
logsOpts := api.ServiceLogsOptions{
|
baseOpts := api.ServiceLogsOptions{
|
||||||
Follow: opts.follow,
|
Follow: opts.Follow,
|
||||||
Tail: tail,
|
Tail: tail,
|
||||||
Since: opts.since,
|
Since: opts.Since,
|
||||||
Until: opts.until,
|
Until: opts.Until,
|
||||||
Machines: cli.ExpandCommaSeparatedValues(opts.machines),
|
Machines: cli.ExpandCommaSeparatedValues(opts.Machines),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Collect log streams from all services. When service names come from a Compose file,
|
// Collect log streams from all services. When service names come from a Compose file,
|
||||||
// skip the ones that are not found in the cluster (they may have been removed or not deployed yet).
|
// skip the ones that are not found in the cluster (they may have been removed or not deployed yet).
|
||||||
machineIDsSet := mapset.NewSet[string]()
|
machineIDsSet := mapset.NewSet[string]()
|
||||||
svcStreams := make([]<-chan api.ServiceLogEntry, 0, len(serviceNames))
|
svcStreams := make([]<-chan api.ServiceLogEntry, 0, len(serviceArgs))
|
||||||
var foundServices, notFoundServices []string
|
var foundServices, notFoundServices []string
|
||||||
|
|
||||||
for _, serviceName := range serviceNames {
|
for _, sa := range serviceArgs {
|
||||||
svc, ch, err := c.ServiceLogs(ctx, serviceName, logsOpts)
|
svcOpts := baseOpts
|
||||||
|
svcOpts.Containers = sa.Containers
|
||||||
|
|
||||||
|
svc, ch, err := c.ServiceLogs(ctx, sa.Service, svcOpts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, api.ErrNotFound) && fromCompose {
|
if errors.Is(err, api.ErrNotFound) && fromCompose {
|
||||||
notFoundServices = append(notFoundServices, serviceName)
|
notFoundServices = append(notFoundServices, sa.Service)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
return fmt.Errorf("stream logs for service '%s': %w", serviceName, err)
|
return fmt.Errorf("stream logs for service '%s': %w", sa.Service, err)
|
||||||
}
|
}
|
||||||
svcStreams = append(svcStreams, ch)
|
svcStreams = append(svcStreams, ch)
|
||||||
foundServices = append(foundServices, serviceName)
|
foundServices = append(foundServices, sa.Service)
|
||||||
|
|
||||||
machineIDs := svc.MachineIDs()
|
machineIDs := svc.MachineIDs()
|
||||||
machineIDsSet.Append(machineIDs...)
|
machineIDsSet.Append(machineIDs...)
|
||||||
@@ -167,9 +147,8 @@ func runLogs(ctx context.Context, uncli *cli.CLI, serviceNames []string, opts lo
|
|||||||
if fromCompose {
|
if fromCompose {
|
||||||
if len(foundServices) == 0 {
|
if len(foundServices) == 0 {
|
||||||
return fmt.Errorf("stream logs for services defined in %s: no services found in the cluster",
|
return fmt.Errorf("stream logs for services defined in %s: no services found in the cluster",
|
||||||
strings.Join(opts.files, ", "))
|
strings.Join(opts.Files, ", "))
|
||||||
}
|
}
|
||||||
serviceNames = foundServices
|
|
||||||
|
|
||||||
for _, name := range notFoundServices {
|
for _, name := range notFoundServices {
|
||||||
tui.PrintWarning(fmt.Sprintf("service '%s' not found in the cluster, skipping", name))
|
tui.PrintWarning(fmt.Sprintf("service '%s' not found in the cluster, skipping", name))
|
||||||
@@ -177,7 +156,7 @@ func runLogs(ctx context.Context, uncli *cli.CLI, serviceNames []string, opts lo
|
|||||||
}
|
}
|
||||||
|
|
||||||
var stream <-chan api.ServiceLogEntry
|
var stream <-chan api.ServiceLogEntry
|
||||||
if len(serviceNames) == 1 {
|
if len(svcStreams) == 1 {
|
||||||
stream = svcStreams[0]
|
stream = svcStreams[0]
|
||||||
} else {
|
} else {
|
||||||
// Merge all service streams into a single sorted stream without stall detection as its handled per-service.
|
// Merge all service streams into a single sorted stream without stall detection as its handled per-service.
|
||||||
@@ -186,6 +165,8 @@ func runLogs(ctx context.Context, uncli *cli.CLI, serviceNames []string, opts lo
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Fetch machine names for all machines (machineIDsSet) service containers are running on.
|
// Fetch machine names for all machines (machineIDsSet) service containers are running on.
|
||||||
|
// Note: this is the full set per service, not narrowed by --machine or per-container filters,
|
||||||
|
// so the formatter may pad columns wider than strictly needed when filters are active.
|
||||||
machines, err := c.ListMachines(ctx, &api.MachineFilter{NamesOrIDs: machineIDsSet.ToSlice()})
|
machines, err := c.ListMachines(ctx, &api.MachineFilter{NamesOrIDs: machineIDsSet.ToSlice()})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("list machines: %w", err)
|
return fmt.Errorf("list machines: %w", err)
|
||||||
@@ -195,169 +176,12 @@ func runLogs(ctx context.Context, uncli *cli.CLI, serviceNames []string, opts lo
|
|||||||
machineNames = append(machineNames, m.Machine.Name)
|
machineNames = append(machineNames, m.Machine.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
formatter := newLogFormatter(machineNames, serviceNames, opts.utc)
|
formatter := logs.NewFormatter(machineNames, foundServices, opts.UTC)
|
||||||
|
|
||||||
// Print merged logs.
|
// Print merged logs.
|
||||||
for entry := range stream {
|
for entry := range stream {
|
||||||
if entry.Err != nil {
|
formatter.PrintEntry(entry)
|
||||||
formatter.printError(entry)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
formatter.printEntry(entry)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Available colors for machine/service differentiation.
|
|
||||||
var colorPalette = []color.Color{
|
|
||||||
lipgloss.BrightGreen,
|
|
||||||
lipgloss.BrightYellow,
|
|
||||||
lipgloss.BrightBlue,
|
|
||||||
lipgloss.BrightMagenta,
|
|
||||||
lipgloss.BrightCyan,
|
|
||||||
lipgloss.Green,
|
|
||||||
lipgloss.Yellow,
|
|
||||||
lipgloss.Blue,
|
|
||||||
lipgloss.Magenta,
|
|
||||||
lipgloss.Cyan,
|
|
||||||
}
|
|
||||||
|
|
||||||
// logFormatter handles formatting and printing of log entries with dynamic column alignment.
|
|
||||||
type logFormatter struct {
|
|
||||||
machineNames []string
|
|
||||||
serviceNames []string
|
|
||||||
|
|
||||||
maxMachineWidth int
|
|
||||||
maxServiceWidth int
|
|
||||||
|
|
||||||
utc bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func newLogFormatter(machineNames, serviceNames []string, utc bool) *logFormatter {
|
|
||||||
slices.Sort(machineNames)
|
|
||||||
slices.Sort(serviceNames)
|
|
||||||
|
|
||||||
maxMachineWidth := 0
|
|
||||||
for _, name := range machineNames {
|
|
||||||
if len(name) > maxMachineWidth {
|
|
||||||
maxMachineWidth = len(name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
maxServiceWidth := 0
|
|
||||||
for _, name := range serviceNames {
|
|
||||||
if len(name) > maxServiceWidth {
|
|
||||||
maxServiceWidth = len(name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return &logFormatter{
|
|
||||||
machineNames: machineNames,
|
|
||||||
serviceNames: serviceNames,
|
|
||||||
maxMachineWidth: maxMachineWidth,
|
|
||||||
maxServiceWidth: maxServiceWidth,
|
|
||||||
utc: utc,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// formatTimestamp formats timestamp using local timezone or UTC if configured.
|
|
||||||
func (f *logFormatter) formatTimestamp(t time.Time) string {
|
|
||||||
if f.utc {
|
|
||||||
t = t.UTC()
|
|
||||||
} else {
|
|
||||||
t = t.In(time.Local)
|
|
||||||
}
|
|
||||||
dimStyle := lipgloss.NewStyle().Faint(true)
|
|
||||||
|
|
||||||
return dimStyle.Render(t.Format(time.StampMilli))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *logFormatter) formatMachine(name string) string {
|
|
||||||
style := lipgloss.NewStyle().Bold(true).PaddingRight(f.maxMachineWidth - len(name))
|
|
||||||
|
|
||||||
if len(f.serviceNames) == 1 {
|
|
||||||
// Machine name is coloured for single-service logs.
|
|
||||||
i := slices.Index(f.machineNames, name)
|
|
||||||
if i == -1 {
|
|
||||||
f.machineNames = append(f.machineNames, name)
|
|
||||||
i = len(f.machineNames) - 1
|
|
||||||
}
|
|
||||||
|
|
||||||
style = style.Foreground(colorPalette[i%len(colorPalette)])
|
|
||||||
}
|
|
||||||
|
|
||||||
return style.Render(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *logFormatter) formatServiceContainer(serviceName, containerID string) string {
|
|
||||||
styleService := lipgloss.NewStyle().Bold(true).PaddingRight(f.maxServiceWidth - len(serviceName))
|
|
||||||
styleContainer := lipgloss.NewStyle().Faint(true)
|
|
||||||
|
|
||||||
if len(f.serviceNames) > 1 {
|
|
||||||
// Service name is coloured for multi-service logs.
|
|
||||||
i := slices.Index(f.serviceNames, serviceName)
|
|
||||||
if i == -1 {
|
|
||||||
f.serviceNames = append(f.serviceNames, serviceName)
|
|
||||||
i = len(f.serviceNames) - 1
|
|
||||||
}
|
|
||||||
|
|
||||||
styleService = styleService.Foreground(colorPalette[i%len(colorPalette)])
|
|
||||||
}
|
|
||||||
|
|
||||||
return styleService.Render(serviceName) + styleContainer.Render("["+containerID[:5]+"]")
|
|
||||||
}
|
|
||||||
|
|
||||||
// printEntry prints a single log entry with proper formatting.
|
|
||||||
func (f *logFormatter) printEntry(entry api.ServiceLogEntry) {
|
|
||||||
if entry.Stream != api.LogStreamStdout && entry.Stream != api.LogStreamStderr {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var output strings.Builder
|
|
||||||
|
|
||||||
// Timestamp
|
|
||||||
output.WriteString(f.formatTimestamp(entry.Timestamp))
|
|
||||||
output.WriteString(" ")
|
|
||||||
|
|
||||||
// Machine name
|
|
||||||
output.WriteString(f.formatMachine(entry.Metadata.MachineName))
|
|
||||||
output.WriteString(" ")
|
|
||||||
|
|
||||||
// Service[container_id]
|
|
||||||
output.WriteString(f.formatServiceContainer(entry.Metadata.ServiceName, entry.Metadata.ContainerID))
|
|
||||||
output.WriteString(" ")
|
|
||||||
|
|
||||||
// Message
|
|
||||||
output.Write(entry.Message)
|
|
||||||
|
|
||||||
// Print to appropriate stream.
|
|
||||||
if entry.Stream == api.LogStreamStderr {
|
|
||||||
fmt.Fprint(os.Stderr, output.String())
|
|
||||||
} else {
|
|
||||||
fmt.Print(output.String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// printError prints an error entry (e.g., stalled stream warning).
|
|
||||||
func (f *logFormatter) printError(entry api.ServiceLogEntry) {
|
|
||||||
if entry.Metadata.ContainerID != "" {
|
|
||||||
msg := fmt.Sprintf("WARNING: log stream from %s[%s] on machine '%s'",
|
|
||||||
entry.Metadata.ServiceName,
|
|
||||||
stringid.TruncateID(entry.Metadata.ContainerID),
|
|
||||||
entry.Metadata.MachineName)
|
|
||||||
|
|
||||||
if errors.Is(entry.Err, api.ErrLogStreamStalled) {
|
|
||||||
msg += " stopped responding"
|
|
||||||
} else {
|
|
||||||
msg += fmt.Sprintf(": %v", entry.Err)
|
|
||||||
}
|
|
||||||
|
|
||||||
style := lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Color("11")) // Bold bright yellow
|
|
||||||
fmt.Fprintln(os.Stderr, style.Render(msg))
|
|
||||||
} else {
|
|
||||||
msg := fmt.Sprintf("ERROR: %v", entry.Err)
|
|
||||||
style := lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Color("9")) // Bold bright red
|
|
||||||
fmt.Fprintln(os.Stderr, style.Render(msg))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
+18
-19
@@ -3,12 +3,11 @@ package service
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
|
||||||
"slices"
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
"text/tabwriter"
|
|
||||||
|
|
||||||
"github.com/psviderski/uncloud/internal/cli"
|
"github.com/psviderski/uncloud/internal/cli"
|
||||||
|
"github.com/psviderski/uncloud/internal/cli/tui"
|
||||||
"github.com/psviderski/uncloud/pkg/api"
|
"github.com/psviderski/uncloud/pkg/api"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
@@ -50,20 +49,22 @@ func list(ctx context.Context, uncli *cli.CLI) error {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Print the list of services in a table format.
|
// Print the list of services in a table format.
|
||||||
tw := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0)
|
t := tui.NewTable()
|
||||||
|
|
||||||
// Include the ID column if there are duplicate service names to differentiate them.
|
// Include the ID column if there are duplicate service names to differentiate them.
|
||||||
|
headers := []string{"NAME", "MODE", "REPLICAS", "IMAGE", "ENDPOINTS"}
|
||||||
if haveDuplicateNames {
|
if haveDuplicateNames {
|
||||||
if _, err = fmt.Fprintf(tw, "ID\t"); err != nil {
|
headers = append([]string{"ID"}, headers...)
|
||||||
return fmt.Errorf("write header: %w", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if _, err = fmt.Fprintln(tw, "NAME\tMODE\tREPLICAS\tIMAGE\tENDPOINTS"); err != nil {
|
|
||||||
return fmt.Errorf("write header: %w", err)
|
|
||||||
}
|
}
|
||||||
|
t.Headers(headers...)
|
||||||
|
|
||||||
for _, s := range services {
|
for _, s := range services {
|
||||||
images := strings.Join(s.Images(), ", ")
|
images := s.Images()
|
||||||
endpoints := strings.Join(s.Endpoints(), ", ")
|
for i, img := range images {
|
||||||
|
images[i] = tui.FormatImage(img, tui.NoStyle)
|
||||||
|
}
|
||||||
|
formattedImages := strings.Join(images, tui.Faint.Render(", "))
|
||||||
|
endpoints := strings.Join(s.Endpoints(), tui.Faint.Render(", "))
|
||||||
|
|
||||||
// If no endpoints from ports, check if the service uses custom Caddy config.
|
// If no endpoints from ports, check if the service uses custom Caddy config.
|
||||||
if endpoints == "" {
|
if endpoints == "" {
|
||||||
@@ -74,15 +75,13 @@ func list(ctx context.Context, uncli *cli.CLI) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
row := []string{s.Name, s.Mode, fmt.Sprintf("%d", len(s.Containers)), formattedImages, endpoints}
|
||||||
if haveDuplicateNames {
|
if haveDuplicateNames {
|
||||||
if _, err = fmt.Fprintf(tw, "%s\t", s.ID); err != nil {
|
row = append([]string{s.ID}, row...)
|
||||||
return fmt.Errorf("write row: %w", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if _, err = fmt.Fprintf(tw, "%s\t%s\t%d\t%s\t%s\n",
|
|
||||||
s.Name, s.Mode, len(s.Containers), images, endpoints); err != nil {
|
|
||||||
return fmt.Errorf("write row: %w", err)
|
|
||||||
}
|
}
|
||||||
|
t.Row(row...)
|
||||||
}
|
}
|
||||||
return tw.Flush()
|
|
||||||
|
fmt.Println(t)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,12 +3,11 @@ package volume
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
|
||||||
"slices"
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
"text/tabwriter"
|
|
||||||
|
|
||||||
"github.com/psviderski/uncloud/internal/cli"
|
"github.com/psviderski/uncloud/internal/cli"
|
||||||
|
"github.com/psviderski/uncloud/internal/cli/tui"
|
||||||
"github.com/psviderski/uncloud/pkg/api"
|
"github.com/psviderski/uncloud/pkg/api"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
@@ -86,16 +85,13 @@ func list(ctx context.Context, uncli *cli.CLI, opts listOptions) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Print the volumes in a table format.
|
// Print the volumes in a table format.
|
||||||
tw := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0)
|
t := tui.NewTable()
|
||||||
fmt.Fprintln(tw, "NAME\tDRIVER\tMACHINE")
|
t.Headers("NAME", "DRIVER", "MACHINE")
|
||||||
|
|
||||||
for _, v := range volumes {
|
for _, v := range volumes {
|
||||||
fmt.Fprintf(tw, "%s\t%s\t%s\n",
|
t.Row(v.Volume.Name, v.Volume.Driver, v.MachineName)
|
||||||
v.Volume.Name,
|
|
||||||
v.Volume.Driver,
|
|
||||||
v.MachineName,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return tw.Flush()
|
fmt.Println(t)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
+27
-22
@@ -3,13 +3,13 @@ package wg
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
|
||||||
"strings"
|
"strings"
|
||||||
"text/tabwriter"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/docker/go-units"
|
"github.com/docker/go-units"
|
||||||
"github.com/psviderski/uncloud/internal/cli"
|
"github.com/psviderski/uncloud/internal/cli"
|
||||||
|
"github.com/psviderski/uncloud/internal/cli/tui"
|
||||||
|
"github.com/psviderski/uncloud/internal/machine/api/pb"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
@@ -75,16 +75,18 @@ func runShow(ctx context.Context, uncli *cli.CLI, opts showOptions) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("list machines: %w", err)
|
return fmt.Errorf("list machines: %w", err)
|
||||||
}
|
}
|
||||||
machinesNamesByPublicKey := make(map[string]string)
|
machinesByPublicKey := make(map[string]*pb.MachineInfo)
|
||||||
for _, m := range machines {
|
for _, m := range machines {
|
||||||
publicKey := wgtypes.Key(m.Machine.Network.PublicKey).String()
|
publicKey := wgtypes.Key(m.Machine.Network.PublicKey).String()
|
||||||
machinesNamesByPublicKey[publicKey] = m.Machine.Name
|
machinesByPublicKey[publicKey] = m.Machine
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch the machine's name for more descriptive output
|
// Fetch the machine's info and RTTs for display.
|
||||||
|
var selfMachine *pb.MachineDetails
|
||||||
inspectResp, err := client.MachineClient.InspectMachine(ctx, nil)
|
inspectResp, err := client.MachineClient.InspectMachine(ctx, nil)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
fmt.Printf("Machine name: %s\n", inspectResp.Machines[0].Machine.Name)
|
selfMachine = inspectResp.Machines[0]
|
||||||
|
fmt.Printf("Machine name: %s\n", selfMachine.Machine.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("WireGuard interface: %s\n", resp.InterfaceName)
|
fmt.Printf("WireGuard interface: %s\n", resp.InterfaceName)
|
||||||
@@ -97,15 +99,20 @@ func runShow(ctx context.Context, uncli *cli.CLI, opts showOptions) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
tw := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0)
|
t := tui.NewTable()
|
||||||
if _, err = fmt.Fprintln(tw, "PEER\tPUBLIC KEY\tENDPOINT\tHANDSHAKE\tRECEIVED\tSENT\tALLOWED IPS"); err != nil {
|
t.Headers("PEER", "PUBLIC KEY", "ENDPOINT", "HANDSHAKE", "RTT", "RECEIVED", "SENT", "ALLOWED IPS")
|
||||||
return fmt.Errorf("write header: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, peer := range resp.Peers {
|
for _, peer := range resp.Peers {
|
||||||
machineName, ok := machinesNamesByPublicKey[wgtypes.Key(peer.PublicKey).String()]
|
publicKeyStr := wgtypes.Key(peer.PublicKey).String()
|
||||||
if !ok {
|
machineName := "(unknown)"
|
||||||
machineName = "(unknown)"
|
rtt := "-"
|
||||||
|
if m, ok := machinesByPublicKey[publicKeyStr]; ok {
|
||||||
|
machineName = m.Name
|
||||||
|
if selfMachine != nil {
|
||||||
|
if stats, ok := selfMachine.Rtts[m.Id]; ok {
|
||||||
|
rtt = tui.FormatRTT(stats.Median.AsDuration())
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
lastHandshake := ""
|
lastHandshake := ""
|
||||||
@@ -113,20 +120,18 @@ func runShow(ctx context.Context, uncli *cli.CLI, opts showOptions) error {
|
|||||||
lastHandshake = time.Since(peer.LastHandshakeTime.AsTime()).Round(time.Second).String() + " ago"
|
lastHandshake = time.Since(peer.LastHandshakeTime.AsTime()).Round(time.Second).String() + " ago"
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = fmt.Fprintf(
|
t.Row(
|
||||||
tw,
|
|
||||||
"%s\t%s\t%s\t%s\t%s\t%s\t%s\n",
|
|
||||||
machineName,
|
machineName,
|
||||||
wgtypes.Key(peer.PublicKey).String(),
|
publicKeyStr,
|
||||||
peer.Endpoint,
|
peer.Endpoint,
|
||||||
lastHandshake,
|
lastHandshake,
|
||||||
|
rtt,
|
||||||
units.HumanSize(float64(peer.ReceiveBytes)),
|
units.HumanSize(float64(peer.ReceiveBytes)),
|
||||||
units.HumanSize(float64(peer.TransmitBytes)),
|
units.HumanSize(float64(peer.TransmitBytes)),
|
||||||
strings.Join(peer.AllowedIps, ", "),
|
strings.Join(peer.AllowedIps, tui.Faint.Render(", ")),
|
||||||
)
|
)
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("write row: %w", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return tw.Flush()
|
|
||||||
|
fmt.Println(t)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,140 @@
|
|||||||
|
module github.com/psviderski/uncloud/experiment
|
||||||
|
|
||||||
|
go 1.26
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/dgraph-io/badger/v3 v3.2103.5
|
||||||
|
github.com/docker/docker v28.5.0+incompatible
|
||||||
|
github.com/hashicorp/memberlist v0.5.1
|
||||||
|
github.com/hashicorp/serf v0.10.1
|
||||||
|
github.com/ipfs/boxo v0.21.0
|
||||||
|
github.com/ipfs/go-cid v0.4.1
|
||||||
|
github.com/ipfs/go-datastore v0.6.0
|
||||||
|
github.com/ipfs/go-ds-badger3 v0.0.2
|
||||||
|
github.com/ipfs/go-ds-crdt v0.5.2
|
||||||
|
github.com/ipfs/go-ipld-format v0.6.0
|
||||||
|
github.com/ipfs/go-log/v2 v2.5.1
|
||||||
|
github.com/lmittmann/tint v1.0.5
|
||||||
|
github.com/psviderski/uncloud v0.0.0
|
||||||
|
github.com/siderolabs/discovery-api v0.1.4
|
||||||
|
github.com/siderolabs/discovery-client v0.1.9
|
||||||
|
go.uber.org/zap v1.27.0
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/Masterminds/goutils v1.1.1 // indirect
|
||||||
|
github.com/Masterminds/semver/v3 v3.4.0 // indirect
|
||||||
|
github.com/Masterminds/sprig/v3 v3.2.3 // indirect
|
||||||
|
github.com/armon/circbuf v0.0.0-20190214190532-5111143e8da2 // indirect
|
||||||
|
github.com/armon/go-metrics v0.4.1 // indirect
|
||||||
|
github.com/armon/go-radix v1.0.0 // indirect
|
||||||
|
github.com/bgentry/speakeasy v0.2.0 // indirect
|
||||||
|
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||||
|
github.com/cespare/xxhash v1.1.0 // indirect
|
||||||
|
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||||
|
github.com/containerd/errdefs v1.0.0 // indirect
|
||||||
|
github.com/containerd/log v0.1.0 // indirect
|
||||||
|
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect
|
||||||
|
github.com/dgraph-io/ristretto v0.1.1 // indirect
|
||||||
|
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c // indirect
|
||||||
|
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||||
|
github.com/fatih/color v1.18.0 // indirect
|
||||||
|
github.com/go-logr/logr v1.4.3 // indirect
|
||||||
|
github.com/go-logr/stdr v1.2.2 // indirect
|
||||||
|
github.com/gogo/protobuf v1.3.2 // indirect
|
||||||
|
github.com/golang/glog v1.2.5 // indirect
|
||||||
|
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||||
|
github.com/golang/protobuf v1.5.4 // indirect
|
||||||
|
github.com/golang/snappy v0.0.4 // indirect
|
||||||
|
github.com/google/btree v1.1.2 // indirect
|
||||||
|
github.com/google/flatbuffers v24.3.25+incompatible // indirect
|
||||||
|
github.com/google/go-cmp v0.7.0 // indirect
|
||||||
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
|
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||||
|
github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
|
||||||
|
github.com/hashicorp/go-immutable-radix/v2 v2.1.0 // indirect
|
||||||
|
github.com/hashicorp/go-msgpack v0.5.5 // indirect
|
||||||
|
github.com/hashicorp/go-msgpack/v2 v2.1.2 // indirect
|
||||||
|
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||||
|
github.com/hashicorp/go-sockaddr v1.0.6 // indirect
|
||||||
|
github.com/hashicorp/go-syslog v1.0.0 // indirect
|
||||||
|
github.com/hashicorp/golang-lru v1.0.2 // indirect
|
||||||
|
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
|
||||||
|
github.com/hashicorp/logutils v1.0.0 // indirect
|
||||||
|
github.com/hashicorp/mdns v1.0.5 // indirect
|
||||||
|
github.com/huandu/xstrings v1.5.0 // indirect
|
||||||
|
github.com/imdario/mergo v0.3.16 // indirect
|
||||||
|
github.com/ipfs/bbloom v0.0.4 // indirect
|
||||||
|
github.com/ipfs/go-block-format v0.2.0 // indirect
|
||||||
|
github.com/ipfs/go-ipfs-util v0.0.3 // indirect
|
||||||
|
github.com/ipfs/go-ipld-legacy v0.2.1 // indirect
|
||||||
|
github.com/ipfs/go-metrics-interface v0.0.1 // indirect
|
||||||
|
github.com/ipld/go-codec-dagpb v1.6.0 // indirect
|
||||||
|
github.com/ipld/go-ipld-prime v0.21.0 // indirect
|
||||||
|
github.com/ishidawataru/sctp v0.0.0-20230406120618-7ff4192f6ff2 // indirect
|
||||||
|
github.com/jbenet/goprocess v0.1.4 // indirect
|
||||||
|
github.com/josharian/native v1.1.0 // indirect
|
||||||
|
github.com/klauspost/compress v1.18.0 // indirect
|
||||||
|
github.com/klauspost/cpuid/v2 v2.2.9 // indirect
|
||||||
|
github.com/libp2p/go-buffer-pool v0.1.0 // indirect
|
||||||
|
github.com/libp2p/go-libp2p v0.35.4 // indirect
|
||||||
|
github.com/libp2p/go-libp2p-pubsub v0.11.0 // indirect
|
||||||
|
github.com/libp2p/go-msgio v0.3.0 // indirect
|
||||||
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
|
github.com/mdlayher/genetlink v1.3.2 // indirect
|
||||||
|
github.com/mdlayher/netlink v1.7.2 // indirect
|
||||||
|
github.com/mdlayher/socket v0.5.1 // indirect
|
||||||
|
github.com/miekg/dns v1.1.65 // indirect
|
||||||
|
github.com/minio/sha256-simd v1.0.1 // indirect
|
||||||
|
github.com/mitchellh/cli v1.1.5 // indirect
|
||||||
|
github.com/mitchellh/copystructure v1.2.0 // indirect
|
||||||
|
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||||
|
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
||||||
|
github.com/mr-tron/base58 v1.2.0 // indirect
|
||||||
|
github.com/multiformats/go-base32 v0.1.0 // indirect
|
||||||
|
github.com/multiformats/go-base36 v0.2.0 // indirect
|
||||||
|
github.com/multiformats/go-multiaddr v0.13.0 // indirect
|
||||||
|
github.com/multiformats/go-multiaddr-fmt v0.1.0 // indirect
|
||||||
|
github.com/multiformats/go-multibase v0.2.0 // indirect
|
||||||
|
github.com/multiformats/go-multicodec v0.9.0 // indirect
|
||||||
|
github.com/multiformats/go-multihash v0.2.3 // indirect
|
||||||
|
github.com/multiformats/go-multistream v0.5.0 // indirect
|
||||||
|
github.com/multiformats/go-varint v0.0.7 // indirect
|
||||||
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
|
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect
|
||||||
|
github.com/polydawn/refmt v0.89.0 // indirect
|
||||||
|
github.com/posener/complete v1.2.3 // indirect
|
||||||
|
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 // indirect
|
||||||
|
github.com/shopspring/decimal v1.4.0 // indirect
|
||||||
|
github.com/siderolabs/gen v0.4.8 // indirect
|
||||||
|
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||||
|
github.com/spaolacci/murmur3 v1.1.0 // indirect
|
||||||
|
github.com/spf13/cast v1.7.0 // indirect
|
||||||
|
github.com/vishvananda/netlink v1.3.1 // indirect
|
||||||
|
github.com/vishvananda/netns v0.0.5 // indirect
|
||||||
|
go.opencensus.io v0.24.0 // indirect
|
||||||
|
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
||||||
|
go.opentelemetry.io/otel v1.36.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/metric v1.36.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/trace v1.36.0 // indirect
|
||||||
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
|
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect
|
||||||
|
golang.org/x/crypto v0.41.0 // indirect
|
||||||
|
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 // indirect
|
||||||
|
golang.org/x/mod v0.27.0 // indirect
|
||||||
|
golang.org/x/net v0.43.0 // indirect
|
||||||
|
golang.org/x/sync v0.19.0 // indirect
|
||||||
|
golang.org/x/sys v0.42.0 // indirect
|
||||||
|
golang.org/x/text v0.28.0 // indirect
|
||||||
|
golang.org/x/tools v0.36.0 // indirect
|
||||||
|
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 // indirect
|
||||||
|
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6 // indirect
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a // indirect
|
||||||
|
google.golang.org/grpc v1.74.2 // indirect
|
||||||
|
google.golang.org/protobuf v1.36.9 // indirect
|
||||||
|
gotest.tools/v3 v3.5.2 // indirect
|
||||||
|
lukechampine.com/blake3 v1.3.0 // indirect
|
||||||
|
)
|
||||||
|
|
||||||
|
replace github.com/psviderski/uncloud => ../
|
||||||
@@ -0,0 +1,782 @@
|
|||||||
|
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
|
github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 h1:cTp8I5+VIoKjsnZuH8vjyaysT/ses3EvZeaV/1UkF2M=
|
||||||
|
github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
|
||||||
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
|
github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
|
||||||
|
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
|
||||||
|
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
|
||||||
|
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
|
||||||
|
github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
|
||||||
|
github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
|
||||||
|
github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
|
||||||
|
github.com/Masterminds/sprig/v3 v3.2.1/go.mod h1:UoaO7Yp8KlPnJIYWTFkMaqPUYKTfGFPhxNuwnnxkKlk=
|
||||||
|
github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA=
|
||||||
|
github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM=
|
||||||
|
github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
|
||||||
|
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||||
|
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||||
|
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||||
|
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||||
|
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||||
|
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||||
|
github.com/armon/circbuf v0.0.0-20190214190532-5111143e8da2 h1:7Ip0wMmLHLRJdrloDxZfhMm0xrLXZS8+COSu2bXmEQs=
|
||||||
|
github.com/armon/circbuf v0.0.0-20190214190532-5111143e8da2/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||||
|
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||||
|
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
||||||
|
github.com/armon/go-metrics v0.4.1 h1:hR91U9KYmb6bLBYLQjyM+3j+rcd/UhE+G78SFnF8gJA=
|
||||||
|
github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4=
|
||||||
|
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||||
|
github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI=
|
||||||
|
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||||
|
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||||
|
github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o=
|
||||||
|
github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||||
|
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||||
|
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||||
|
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||||
|
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||||
|
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||||
|
github.com/bgentry/speakeasy v0.2.0 h1:tgObeVOf8WAvtuAX6DhJ4xks4CFNwPDZiqzGqIHE51E=
|
||||||
|
github.com/bgentry/speakeasy v0.2.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||||
|
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/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
|
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
|
||||||
|
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||||
|
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
|
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||||
|
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
|
github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
|
||||||
|
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
|
||||||
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
|
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||||
|
github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM=
|
||||||
|
github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw=
|
||||||
|
github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI=
|
||||||
|
github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M=
|
||||||
|
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
|
||||||
|
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
|
||||||
|
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||||
|
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
|
||||||
|
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||||
|
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf h1:iW4rZ826su+pqaw19uhpSCzhj44qo35pNgKFGqzDKkU=
|
||||||
|
github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
|
||||||
|
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||||
|
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
||||||
|
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||||
|
github.com/cskr/pubsub v1.0.2 h1:vlOzMhl6PFn60gRlTQQsIfVwaPB/B/8MziK8FhEPt/0=
|
||||||
|
github.com/cskr/pubsub v1.0.2/go.mod h1:/8MzYXk/NJAz782G8RPkFzXTZVu63VotefPnR9TIRis=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||||
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c h1:pFUpOrbxDR6AkioZ1ySsx5yxlDQZ8stG2b88gTPxgJU=
|
||||||
|
github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c/go.mod h1:6UhI8N9EjYm1c2odKpFpAYeR8dsBeM7PtzQhRgxRr9U=
|
||||||
|
github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y=
|
||||||
|
github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo=
|
||||||
|
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 h1:rpfIENRNNilwHwZeG5+P150SMrnNEcHYvcCuK6dPZSg=
|
||||||
|
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0=
|
||||||
|
github.com/dgraph-io/badger v1.6.2 h1:mNw0qs90GVgGGWylh0umH5iag1j6n/PeJtNvL6KY/x8=
|
||||||
|
github.com/dgraph-io/badger v1.6.2/go.mod h1:JW2yswe3V058sS0kZ2h/AXeDSqFjxnZcRrVH//y2UQE=
|
||||||
|
github.com/dgraph-io/badger/v3 v3.2103.5 h1:ylPa6qzbjYRQMU6jokoj4wzcaweHylt//CH0AKt0akg=
|
||||||
|
github.com/dgraph-io/badger/v3 v3.2103.5/go.mod h1:4MPiseMeDQ3FNCYwRbbcBOGJLf5jsE0PPFzRiKjtcdw=
|
||||||
|
github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8=
|
||||||
|
github.com/dgraph-io/ristretto v0.1.1/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkzgwUve0VDWWA=
|
||||||
|
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
|
||||||
|
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y=
|
||||||
|
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
|
||||||
|
github.com/docker/docker v28.5.0+incompatible h1:ZdSQoRUE9XxhFI/B8YLvhnEFMmYN9Pp8Egd2qcaFk1E=
|
||||||
|
github.com/docker/docker v28.5.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||||
|
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c h1:+pKlWGMw7gf6bQ+oDZB4KHQFypsfjYlq/C4rfL7D3g8=
|
||||||
|
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA=
|
||||||
|
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/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||||
|
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/elastic/gosigar v0.14.3 h1:xwkKwPia+hSfg9GqrCUKYdId102m9qTJIIr7egmK/uo=
|
||||||
|
github.com/elastic/gosigar v0.14.3/go.mod h1:iXRIGg2tLnu7LBdpqzyQfGDEidKCfWcCMS0WKyPWoMs=
|
||||||
|
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
|
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
|
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||||
|
github.com/envoyproxy/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.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
|
||||||
|
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
|
||||||
|
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
|
||||||
|
github.com/flynn/noise v1.1.0 h1:KjPQoQCEFdZDiP03phOvGi11+SVVhBG2wOWAorLsstg=
|
||||||
|
github.com/flynn/noise v1.1.0/go.mod h1:xbMo+0i6+IGbYdJhF31t2eR1BIU0CYc12+BNAKwUTag=
|
||||||
|
github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk=
|
||||||
|
github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY=
|
||||||
|
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||||
|
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||||
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
|
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||||
|
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||||
|
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||||
|
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.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||||
|
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||||
|
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||||
|
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||||
|
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||||
|
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
|
||||||
|
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
||||||
|
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
||||||
|
github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0=
|
||||||
|
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
|
||||||
|
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||||
|
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||||
|
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||||
|
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 v1.2.5 h1:DrW6hGnjIhtvhOIiAKT6Psh/Kd/ldepEa81DKeiRJ5I=
|
||||||
|
github.com/golang/glog v1.2.5/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=
|
||||||
|
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
|
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
|
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
|
||||||
|
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
|
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/protobuf v1.3.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.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||||
|
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||||
|
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||||
|
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||||
|
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||||
|
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||||
|
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||||
|
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||||
|
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||||
|
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||||
|
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 v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU=
|
||||||
|
github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
|
||||||
|
github.com/google/flatbuffers v1.12.1/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
|
||||||
|
github.com/google/flatbuffers v24.3.25+incompatible h1:CX395cjN9Kke9mmalRoL3d81AtFUxJM+yDthflgJGkI=
|
||||||
|
github.com/google/flatbuffers v24.3.25+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
|
||||||
|
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||||
|
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
|
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
|
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.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/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8=
|
||||||
|
github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=
|
||||||
|
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8=
|
||||||
|
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
|
||||||
|
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||||
|
github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c h1:7lF+Vz0LqiRidnzC1Oq86fpX1q/iEv2KJdrCtttYjT4=
|
||||||
|
github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||||
|
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||||
|
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
|
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||||
|
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
||||||
|
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||||
|
github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||||
|
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||||
|
github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc=
|
||||||
|
github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||||
|
github.com/hashicorp/go-immutable-radix/v2 v2.1.0 h1:CUW5RYIcysz+D3B+l1mDeXrQ7fUvGGCwJfdASSzbrfo=
|
||||||
|
github.com/hashicorp/go-immutable-radix/v2 v2.1.0/go.mod h1:hgdqLXA4f6NIjRVisM1TJ9aOJVNRqKZj+xDGF6m7PBw=
|
||||||
|
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
||||||
|
github.com/hashicorp/go-msgpack v0.5.5 h1:i9R9JSrqIz0QVLz3sz+i3YJdT7TTSLcfLLzJi9aZTuI=
|
||||||
|
github.com/hashicorp/go-msgpack v0.5.5/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
||||||
|
github.com/hashicorp/go-msgpack/v2 v2.1.2 h1:4Ee8FTp834e+ewB71RDrQ0VKpyFdrKOjvYtnQ/ltVj0=
|
||||||
|
github.com/hashicorp/go-msgpack/v2 v2.1.2/go.mod h1:upybraOAblm4S7rx0+jeNy+CWWhzywQsSRV5033mMu4=
|
||||||
|
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||||
|
github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=
|
||||||
|
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
||||||
|
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||||
|
github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
|
||||||
|
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
|
||||||
|
github.com/hashicorp/go-sockaddr v1.0.6 h1:RSG8rKU28VTUTvEKghe5gIhIQpv8evvNpnDEyqO4u9I=
|
||||||
|
github.com/hashicorp/go-sockaddr v1.0.6/go.mod h1:uoUUmtwU7n9Dv3O4SNLeFvg0SxQ3lyjsj6+CCykpaxI=
|
||||||
|
github.com/hashicorp/go-syslog v1.0.0 h1:KaodqZuhUoZereWVIYmpUgZysurB1kBLX2j0MwMrUAE=
|
||||||
|
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
|
||||||
|
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||||
|
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||||
|
github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
|
||||||
|
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||||
|
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||||
|
github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c=
|
||||||
|
github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
|
||||||
|
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
|
||||||
|
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
|
||||||
|
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||||
|
github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y=
|
||||||
|
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
|
||||||
|
github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc=
|
||||||
|
github.com/hashicorp/mdns v1.0.5 h1:1M5hW1cunYeoXOqHwEb/GBDDHAFo0Yqb/uz/beC6LbE=
|
||||||
|
github.com/hashicorp/mdns v1.0.5/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc=
|
||||||
|
github.com/hashicorp/memberlist v0.5.0/go.mod h1:yvyXLpo0QaGE59Y7hDTsTzDD25JYBZ4mHgHUZ8lrOI0=
|
||||||
|
github.com/hashicorp/memberlist v0.5.1 h1:mk5dRuzeDNis2bi6LLoQIXfMH7JQvAzt3mQD0vNZZUo=
|
||||||
|
github.com/hashicorp/memberlist v0.5.1/go.mod h1:zGDXV6AqbDTKTM6yxW0I4+JtFzZAJVoIPvss4hV8F24=
|
||||||
|
github.com/hashicorp/serf v0.10.1 h1:Z1H2J60yRKvfDYAOZLd2MU0ND4AH/WDz7xYHDWQsIPY=
|
||||||
|
github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4=
|
||||||
|
github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
||||||
|
github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
||||||
|
github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
||||||
|
github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI=
|
||||||
|
github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
||||||
|
github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc=
|
||||||
|
github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8=
|
||||||
|
github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
||||||
|
github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4=
|
||||||
|
github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
|
||||||
|
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||||
|
github.com/ipfs/bbloom v0.0.4 h1:Gi+8EGJ2y5qiD5FbsbpX/TMNcJw8gSqr7eyjHa4Fhvs=
|
||||||
|
github.com/ipfs/bbloom v0.0.4/go.mod h1:cS9YprKXpoZ9lT0n/Mw/a6/aFV6DTjTLYHeA+gyqMG0=
|
||||||
|
github.com/ipfs/boxo v0.21.0 h1:XpGXb+TQQ0IUdYaeAxGzWjSs6ow/Lce148A/2IbRDVE=
|
||||||
|
github.com/ipfs/boxo v0.21.0/go.mod h1:NmweAYeY1USOaJJxouy7DLr/Y5M8UBSsCI2KRivO+TY=
|
||||||
|
github.com/ipfs/go-block-format v0.2.0 h1:ZqrkxBA2ICbDRbK8KJs/u0O3dlp6gmAuuXUJNiW1Ycs=
|
||||||
|
github.com/ipfs/go-block-format v0.2.0/go.mod h1:+jpL11nFx5A/SPpsoBn6Bzkra/zaArfSmsknbPMYgzM=
|
||||||
|
github.com/ipfs/go-cid v0.4.1 h1:A/T3qGvxi4kpKWWcPC/PgbvDA2bjVLO7n4UeVwnbs/s=
|
||||||
|
github.com/ipfs/go-cid v0.4.1/go.mod h1:uQHwDeX4c6CtyrFwdqyhpNcxVewur1M7l7fNU7LKwZk=
|
||||||
|
github.com/ipfs/go-datastore v0.6.0 h1:JKyz+Gvz1QEZw0LsX1IBn+JFCJQH4SJVFtM4uWU0Myk=
|
||||||
|
github.com/ipfs/go-datastore v0.6.0/go.mod h1:rt5M3nNbSO/8q1t4LNkLyUwRs8HupMeN/8O4Vn9YAT8=
|
||||||
|
github.com/ipfs/go-detect-race v0.0.1 h1:qX/xay2W3E4Q1U7d9lNs1sU9nvguX0a7319XbyQ6cOk=
|
||||||
|
github.com/ipfs/go-detect-race v0.0.1/go.mod h1:8BNT7shDZPo99Q74BpGMK+4D8Mn4j46UU0LZ723meps=
|
||||||
|
github.com/ipfs/go-ds-badger v0.3.0 h1:xREL3V0EH9S219kFFueOYJJTcjgNSZ2HY1iSvN7U1Ro=
|
||||||
|
github.com/ipfs/go-ds-badger v0.3.0/go.mod h1:1ke6mXNqeV8K3y5Ak2bAA0osoTfmxUdupVCGm4QUIek=
|
||||||
|
github.com/ipfs/go-ds-badger3 v0.0.2 h1:+pME0YfRnbUKhvySnakNMuCMsUUhmGfwIsH/nnHZ7QY=
|
||||||
|
github.com/ipfs/go-ds-badger3 v0.0.2/go.mod h1:6/yjF1KaOU+IpCaqMV43yoWIdxHqOAJlO9EhWLnZSkI=
|
||||||
|
github.com/ipfs/go-ds-crdt v0.5.2 h1:3cTJ79/UocAoukkXr0OvpPHcH7v4YwN8xCb8F9pfngc=
|
||||||
|
github.com/ipfs/go-ds-crdt v0.5.2/go.mod h1:sJdQqdCp2Vwv1azk1sPl+Dxk780CQc11rf7B8aWmomc=
|
||||||
|
github.com/ipfs/go-ipfs-blocksutil v0.0.1 h1:Eh/H4pc1hsvhzsQoMEP3Bke/aW5P5rVM1IWFJMcGIPQ=
|
||||||
|
github.com/ipfs/go-ipfs-blocksutil v0.0.1/go.mod h1:Yq4M86uIOmxmGPUHv/uI7uKqZNtLb449gwKqXjIsnRk=
|
||||||
|
github.com/ipfs/go-ipfs-delay v0.0.1 h1:r/UXYyRcddO6thwOnhiznIAiSvxMECGgtv35Xs1IeRQ=
|
||||||
|
github.com/ipfs/go-ipfs-delay v0.0.1/go.mod h1:8SP1YXK1M1kXuc4KJZINY3TQQ03J2rwBG9QfXmbRPrw=
|
||||||
|
github.com/ipfs/go-ipfs-pq v0.0.3 h1:YpoHVJB+jzK15mr/xsWC574tyDLkezVrDNeaalQBsTE=
|
||||||
|
github.com/ipfs/go-ipfs-pq v0.0.3/go.mod h1:btNw5hsHBpRcSSgZtiNm/SLj5gYIZ18AKtv3kERkRb4=
|
||||||
|
github.com/ipfs/go-ipfs-util v0.0.3 h1:2RFdGez6bu2ZlZdI+rWfIdbQb1KudQp3VGwPtdNCmE0=
|
||||||
|
github.com/ipfs/go-ipfs-util v0.0.3/go.mod h1:LHzG1a0Ig4G+iZ26UUOMjHd+lfM84LZCrn17xAKWBvs=
|
||||||
|
github.com/ipfs/go-ipld-format v0.6.0 h1:VEJlA2kQ3LqFSIm5Vu6eIlSxD/Ze90xtc4Meten1F5U=
|
||||||
|
github.com/ipfs/go-ipld-format v0.6.0/go.mod h1:g4QVMTn3marU3qXchwjpKPKgJv+zF+OlaKMyhJ4LHPg=
|
||||||
|
github.com/ipfs/go-ipld-legacy v0.2.1 h1:mDFtrBpmU7b//LzLSypVrXsD8QxkEWxu5qVxN99/+tk=
|
||||||
|
github.com/ipfs/go-ipld-legacy v0.2.1/go.mod h1:782MOUghNzMO2DER0FlBR94mllfdCJCkTtDtPM51otM=
|
||||||
|
github.com/ipfs/go-log/v2 v2.5.1 h1:1XdUzF7048prq4aBjDQQ4SL5RxftpRGdXhNRwKSAlcY=
|
||||||
|
github.com/ipfs/go-log/v2 v2.5.1/go.mod h1:prSpmC1Gpllc9UYWxDiZDreBYw7zp4Iqp1kOLU9U5UI=
|
||||||
|
github.com/ipfs/go-metrics-interface v0.0.1 h1:j+cpbjYvu4R8zbleSs36gvB7jR+wsL2fGD6n0jO4kdg=
|
||||||
|
github.com/ipfs/go-metrics-interface v0.0.1/go.mod h1:6s6euYU4zowdslK0GKHmqaIZ3j/b/tL7HTWtJ4VPgWY=
|
||||||
|
github.com/ipfs/go-peertaskqueue v0.8.1 h1:YhxAs1+wxb5jk7RvS0LHdyiILpNmRIRnZVztekOF0pg=
|
||||||
|
github.com/ipfs/go-peertaskqueue v0.8.1/go.mod h1:Oxxd3eaK279FxeydSPPVGHzbwVeHjatZ2GA8XD+KbPU=
|
||||||
|
github.com/ipld/go-codec-dagpb v1.6.0 h1:9nYazfyu9B1p3NAgfVdpRco3Fs2nFC72DqVsMj6rOcc=
|
||||||
|
github.com/ipld/go-codec-dagpb v1.6.0/go.mod h1:ANzFhfP2uMJxRBr8CE+WQWs5UsNa0pYtmKZ+agnUw9s=
|
||||||
|
github.com/ipld/go-ipld-prime v0.21.0 h1:n4JmcpOlPDIxBcY037SVfpd1G+Sj1nKZah0m6QH9C2E=
|
||||||
|
github.com/ipld/go-ipld-prime v0.21.0/go.mod h1:3RLqy//ERg/y5oShXXdx5YIp50cFGOanyMctpPjsvxQ=
|
||||||
|
github.com/ishidawataru/sctp v0.0.0-20230406120618-7ff4192f6ff2 h1:i2fYnDurfLlJH8AyyMOnkLHnHeP8Ff/DDpuZA/D3bPo=
|
||||||
|
github.com/ishidawataru/sctp v0.0.0-20230406120618-7ff4192f6ff2/go.mod h1:co9pwDoBCm1kGxawmb4sPq0cSIOOWNPT4KnHotMP1Zg=
|
||||||
|
github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus=
|
||||||
|
github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc=
|
||||||
|
github.com/jbenet/go-cienv v0.1.0/go.mod h1:TqNnHUmJgXau0nCzC7kXWeotg3J9W34CUv5Djy1+FlA=
|
||||||
|
github.com/jbenet/go-temp-err-catcher v0.1.0 h1:zpb3ZH6wIE8Shj2sKS+khgRvf7T7RABoLk/+KKHggpk=
|
||||||
|
github.com/jbenet/go-temp-err-catcher v0.1.0/go.mod h1:0kJRvmDZXNMIiJirNPEYfhpPwbGVtZVWC34vc5WLsDk=
|
||||||
|
github.com/jbenet/goprocess v0.1.4 h1:DRGOFReOMqqDNXwW70QkacFW0YN9QnwLV0Vqk+3oU0o=
|
||||||
|
github.com/jbenet/goprocess v0.1.4/go.mod h1:5yspPrukOVuOLORacaBi858NqyClJPQxYZlqdZVfqY4=
|
||||||
|
github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA=
|
||||||
|
github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
||||||
|
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||||
|
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||||
|
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
||||||
|
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||||
|
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||||
|
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||||
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
|
github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
|
||||||
|
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||||
|
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||||
|
github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY=
|
||||||
|
github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8=
|
||||||
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
|
github.com/koron/go-ssdp v0.0.4 h1:1IDwrghSKYM7yLf7XCzbByg2sJ/JcNOZRXS2jczTwz0=
|
||||||
|
github.com/koron/go-ssdp v0.0.4/go.mod h1:oDXq+E5IL5q0U8uSBcoAXzTzInwy5lEgC91HoKtbmZk=
|
||||||
|
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||||
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
|
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
|
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||||
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
|
github.com/kr/text v0.1.0/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/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
|
github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8=
|
||||||
|
github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg=
|
||||||
|
github.com/libp2p/go-flow-metrics v0.1.0 h1:0iPhMI8PskQwzh57jB9WxIuIOQ0r+15PChFGkx3Q3WM=
|
||||||
|
github.com/libp2p/go-flow-metrics v0.1.0/go.mod h1:4Xi8MX8wj5aWNDAZttg6UPmc0ZrnFNsMtpsYUClFtro=
|
||||||
|
github.com/libp2p/go-libp2p v0.35.4 h1:FDiBUYLkueFwsuNJUZaxKRdpKvBOWU64qQPL768bSeg=
|
||||||
|
github.com/libp2p/go-libp2p v0.35.4/go.mod h1:RKCDNt30IkFipGL0tl8wQW/3zVWEGFUZo8g2gAKxwjU=
|
||||||
|
github.com/libp2p/go-libp2p-asn-util v0.4.1 h1:xqL7++IKD9TBFMgnLPZR6/6iYhawHKHl950SO9L6n94=
|
||||||
|
github.com/libp2p/go-libp2p-asn-util v0.4.1/go.mod h1:d/NI6XZ9qxw67b4e+NgpQexCIiFYJjErASrYW4PFDN8=
|
||||||
|
github.com/libp2p/go-libp2p-pubsub v0.11.0 h1:+JvS8Kty0OiyUiN0i8H5JbaCgjnJTRnTHe4rU88dLFc=
|
||||||
|
github.com/libp2p/go-libp2p-pubsub v0.11.0/go.mod h1:QEb+hEV9WL9wCiUAnpY29FZR6W3zK8qYlaml8R4q6gQ=
|
||||||
|
github.com/libp2p/go-libp2p-record v0.2.0 h1:oiNUOCWno2BFuxt3my4i1frNrt7PerzB3queqa1NkQ0=
|
||||||
|
github.com/libp2p/go-libp2p-record v0.2.0/go.mod h1:I+3zMkvvg5m2OcSdoL0KPljyJyvNDFGKX7QdlpYUcwk=
|
||||||
|
github.com/libp2p/go-libp2p-testing v0.12.0 h1:EPvBb4kKMWO29qP4mZGyhVzUyR25dvfUIK5WDu6iPUA=
|
||||||
|
github.com/libp2p/go-libp2p-testing v0.12.0/go.mod h1:KcGDRXyN7sQCllucn1cOOS+Dmm7ujhfEyXQL5lvkcPg=
|
||||||
|
github.com/libp2p/go-msgio v0.3.0 h1:mf3Z8B1xcFN314sWX+2vOTShIE0Mmn2TXn3YCUQGNj0=
|
||||||
|
github.com/libp2p/go-msgio v0.3.0/go.mod h1:nyRM819GmVaF9LX3l03RMh10QdOroF++NBbxAb0mmDM=
|
||||||
|
github.com/libp2p/go-nat v0.2.0 h1:Tyz+bUFAYqGyJ/ppPPymMGbIgNRH+WqC5QrT5fKrrGk=
|
||||||
|
github.com/libp2p/go-nat v0.2.0/go.mod h1:3MJr+GRpRkyT65EpVPBstXLvOlAPzUVlG6Pwg9ohLJk=
|
||||||
|
github.com/libp2p/go-netroute v0.2.1 h1:V8kVrpD8GK0Riv15/7VN6RbUQ3URNZVosw7H2v9tksU=
|
||||||
|
github.com/libp2p/go-netroute v0.2.1/go.mod h1:hraioZr0fhBjG0ZRXJJ6Zj2IVEVNx6tDTFQfSmcq7mQ=
|
||||||
|
github.com/libp2p/go-reuseport v0.4.0 h1:nR5KU7hD0WxXCJbmw7r2rhRYruNRl2koHw8fQscQm2s=
|
||||||
|
github.com/libp2p/go-reuseport v0.4.0/go.mod h1:ZtI03j/wO5hZVDFo2jKywN6bYKWLOy8Se6DrI2E1cLU=
|
||||||
|
github.com/libp2p/go-yamux/v4 v4.0.1 h1:FfDR4S1wj6Bw2Pqbc8Uz7pCxeRBPbwsBbEdfwiCypkQ=
|
||||||
|
github.com/libp2p/go-yamux/v4 v4.0.1/go.mod h1:NWjl8ZTLOGlozrXSOZ/HlfG++39iKNnM5wwmtQP1YB4=
|
||||||
|
github.com/lmittmann/tint v1.0.5 h1:NQclAutOfYsqs2F1Lenue6OoWCajs5wJcP3DfWVpePw=
|
||||||
|
github.com/lmittmann/tint v1.0.5/go.mod h1:HIS3gSy7qNwGCj+5oRjAutErFBl4BzdQP6cJZ0NfMwE=
|
||||||
|
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||||
|
github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd h1:br0buuQ854V8u83wA0rVZ8ttrq5CpaPZdvrK0LP2lOk=
|
||||||
|
github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd/go.mod h1:QuCEs1Nt24+FYQEqAAncTDPJIuGs+LxK1MCiFL25pMU=
|
||||||
|
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||||
|
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||||
|
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||||
|
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||||
|
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||||
|
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||||
|
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||||
|
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
|
||||||
|
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||||
|
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||||
|
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/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
|
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||||
|
github.com/mdlayher/genetlink v1.3.2 h1:KdrNKe+CTu+IbZnm/GVUMXSqBBLqcGpRDa0xkQy56gw=
|
||||||
|
github.com/mdlayher/genetlink v1.3.2/go.mod h1:tcC3pkCrPUGIKKsCsp0B3AdaaKuHtaxoJRz3cc+528o=
|
||||||
|
github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g=
|
||||||
|
github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw=
|
||||||
|
github.com/mdlayher/socket v0.5.1 h1:VZaqt6RkGkt2OE9l3GcC6nZkqD3xKeQLyfleW/uBcos=
|
||||||
|
github.com/mdlayher/socket v0.5.1/go.mod h1:TjPLHI1UgwEv5J1B5q0zTZq12A/6H7nKmtTanQE37IQ=
|
||||||
|
github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
|
||||||
|
github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI=
|
||||||
|
github.com/miekg/dns v1.1.65 h1:0+tIPHzUW0GCge7IiK3guGP57VAw7hoPDfApjkMD1Fc=
|
||||||
|
github.com/miekg/dns v1.1.65/go.mod h1:Dzw9769uoKVaLuODMDZz9M6ynFU6Em65csPuoi8G0ck=
|
||||||
|
github.com/mikioh/ipaddr v0.0.0-20190404000644-d465c8ab6721 h1:RlZweED6sbSArvlE924+mUcZuXKLBHA35U7LN621Bws=
|
||||||
|
github.com/mikioh/ipaddr v0.0.0-20190404000644-d465c8ab6721/go.mod h1:Ickgr2WtCLZ2MDGd4Gr0geeCH5HybhRJbonOgQpvSxc=
|
||||||
|
github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b h1:z78hV3sbSMAUoyUMM0I83AUIT6Hu17AWfgjzIbtrYFc=
|
||||||
|
github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b/go.mod h1:lxPUiZwKoFL8DUUmalo2yJJUCxbPKtm8OKfqr2/FTNU=
|
||||||
|
github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc h1:PTfri+PuQmWDqERdnNMiD9ZejrlswWrCpBEZgWOiTrc=
|
||||||
|
github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc/go.mod h1:cGKTAVKx4SxOuR/czcZ/E2RSJ3sfHs8FpHhQ5CWMf9s=
|
||||||
|
github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ=
|
||||||
|
github.com/minio/sha256-simd v0.1.1-0.20190913151208-6de447530771/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM=
|
||||||
|
github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM=
|
||||||
|
github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8=
|
||||||
|
github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI=
|
||||||
|
github.com/mitchellh/cli v1.1.5 h1:OxRIeJXpAMztws/XHlN2vu6imG5Dpq+j61AzAX5fLng=
|
||||||
|
github.com/mitchellh/cli v1.1.5/go.mod h1:v8+iFts2sPIKUV1ltktPXMCC8fumSKFItNcD2cLtRR4=
|
||||||
|
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
|
||||||
|
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
|
||||||
|
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
|
||||||
|
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||||
|
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||||
|
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||||
|
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||||
|
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||||
|
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||||
|
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
|
||||||
|
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
|
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
|
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
|
github.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
|
||||||
|
github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o=
|
||||||
|
github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
|
||||||
|
github.com/multiformats/go-base32 v0.1.0 h1:pVx9xoSPqEIQG8o+UbAe7DNi51oej1NtK+aGkbLYxPE=
|
||||||
|
github.com/multiformats/go-base32 v0.1.0/go.mod h1:Kj3tFY6zNr+ABYMqeUNeGvkIC/UYgtWibDcT0rExnbI=
|
||||||
|
github.com/multiformats/go-base36 v0.2.0 h1:lFsAbNOGeKtuKozrtBsAkSVhv1p9D0/qedU9rQyccr0=
|
||||||
|
github.com/multiformats/go-base36 v0.2.0/go.mod h1:qvnKE++v+2MWCfePClUEjE78Z7P2a1UV0xHgWc0hkp4=
|
||||||
|
github.com/multiformats/go-multiaddr v0.1.1/go.mod h1:aMKBKNEYmzmDmxfX88/vz+J5IU55txyt0p4aiWVohjo=
|
||||||
|
github.com/multiformats/go-multiaddr v0.13.0 h1:BCBzs61E3AGHcYYTv8dqRH43ZfyrqM8RXVPT8t13tLQ=
|
||||||
|
github.com/multiformats/go-multiaddr v0.13.0/go.mod h1:sBXrNzucqkFJhvKOiwwLyqamGa/P5EIXNPLovyhQCII=
|
||||||
|
github.com/multiformats/go-multiaddr-dns v0.3.1 h1:QgQgR+LQVt3NPTjbrLLpsaT2ufAA2y0Mkk+QRVJbW3A=
|
||||||
|
github.com/multiformats/go-multiaddr-dns v0.3.1/go.mod h1:G/245BRQ6FJGmryJCrOuTdB37AMA5AMOVuO6NY3JwTk=
|
||||||
|
github.com/multiformats/go-multiaddr-fmt v0.1.0 h1:WLEFClPycPkp4fnIzoFoV9FVd49/eQsuaL3/CWe167E=
|
||||||
|
github.com/multiformats/go-multiaddr-fmt v0.1.0/go.mod h1:hGtDIW4PU4BqJ50gW2quDuPVjyWNZxToGUh/HwTZYJo=
|
||||||
|
github.com/multiformats/go-multibase v0.2.0 h1:isdYCVLvksgWlMW9OZRYJEa9pZETFivncJHmHnnd87g=
|
||||||
|
github.com/multiformats/go-multibase v0.2.0/go.mod h1:bFBZX4lKCA/2lyOFSAoKH5SS6oPyjtnzK/XTFDPkNuk=
|
||||||
|
github.com/multiformats/go-multicodec v0.9.0 h1:pb/dlPnzee/Sxv/j4PmkDRxCOi3hXTz3IbPKOXWJkmg=
|
||||||
|
github.com/multiformats/go-multicodec v0.9.0/go.mod h1:L3QTQvMIaVBkXOXXtVmYE+LI16i14xuaojr/H7Ai54k=
|
||||||
|
github.com/multiformats/go-multihash v0.0.8/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew=
|
||||||
|
github.com/multiformats/go-multihash v0.2.3 h1:7Lyc8XfX/IY2jWb/gI7JP+o7JEq9hOa7BFvVU9RSh+U=
|
||||||
|
github.com/multiformats/go-multihash v0.2.3/go.mod h1:dXgKXCXjBzdscBLk9JkjINiEsCKRVch90MdaGiKsvSM=
|
||||||
|
github.com/multiformats/go-multistream v0.5.0 h1:5htLSLl7lvJk3xx3qT/8Zm9J4K8vEOf/QGkvOGQAyiE=
|
||||||
|
github.com/multiformats/go-multistream v0.5.0/go.mod h1:n6tMZiwiP2wUsR8DgfDWw1dydlEqV3l6N3/GBsX6ILA=
|
||||||
|
github.com/multiformats/go-varint v0.0.7 h1:sWSGR+f/eu5ABZA2ZpYKBILXTTs9JWpdEM/nEGOHFS8=
|
||||||
|
github.com/multiformats/go-varint v0.0.7/go.mod h1:r8PUYw/fD/SjBCiKOoDlGF6QawOELpZAu9eioSos/OU=
|
||||||
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||||
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||||
|
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||||
|
github.com/onsi/ginkgo/v2 v2.22.0 h1:Yed107/8DjTr0lKCNt7Dn8yQ6ybuDRQoMGrNFKzMfHg=
|
||||||
|
github.com/onsi/ginkgo/v2 v2.22.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo=
|
||||||
|
github.com/opencontainers/runtime-spec v1.2.1 h1:S4k4ryNgEpxW1dzyqffOmhI1BHYcjzU8lpJfSlR0xww=
|
||||||
|
github.com/opencontainers/runtime-spec v1.2.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
|
||||||
|
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||||
|
github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=
|
||||||
|
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||||
|
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0=
|
||||||
|
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y=
|
||||||
|
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||||
|
github.com/pion/datachannel v1.5.6 h1:1IxKJntfSlYkpUj8LlYRSWpYiTTC02nUrOE8T3DqGeg=
|
||||||
|
github.com/pion/datachannel v1.5.6/go.mod h1:1eKT6Q85pRnr2mHiWHxJwO50SfZRtWHTsNIVb/NfGW4=
|
||||||
|
github.com/pion/dtls/v2 v2.2.11 h1:9U/dpCYl1ySttROPWJgqWKEylUdT0fXp/xst6JwY5Ks=
|
||||||
|
github.com/pion/dtls/v2 v2.2.11/go.mod h1:d9SYc9fch0CqK90mRk1dC7AkzzpwJj6u2GU3u+9pqFE=
|
||||||
|
github.com/pion/ice/v2 v2.3.25 h1:M5rJA07dqhi3nobJIg+uPtcVjFECTrhcR3n0ns8kDZs=
|
||||||
|
github.com/pion/ice/v2 v2.3.25/go.mod h1:KXJJcZK7E8WzrBEYnV4UtqEZsGeWfHxsNqhVcVvgjxw=
|
||||||
|
github.com/pion/interceptor v0.1.29 h1:39fsnlP1U8gw2JzOFWdfCU82vHvhW9o0rZnZF56wF+M=
|
||||||
|
github.com/pion/interceptor v0.1.29/go.mod h1:ri+LGNjRUc5xUNtDEPzfdkmSqISixVTBF/z/Zms/6T4=
|
||||||
|
github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY=
|
||||||
|
github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms=
|
||||||
|
github.com/pion/mdns v0.0.12 h1:CiMYlY+O0azojWDmxdNr7ADGrnZ+V6Ilfner+6mSVK8=
|
||||||
|
github.com/pion/mdns v0.0.12/go.mod h1:VExJjv8to/6Wqm1FXK+Ii/Z9tsVk/F5sD/N70cnYFbk=
|
||||||
|
github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA=
|
||||||
|
github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8=
|
||||||
|
github.com/pion/rtcp v1.2.14 h1:KCkGV3vJ+4DAJmvP0vaQShsb0xkRfWkO540Gy102KyE=
|
||||||
|
github.com/pion/rtcp v1.2.14/go.mod h1:sn6qjxvnwyAkkPzPULIbVqSKI5Dv54Rv7VG0kNxh9L4=
|
||||||
|
github.com/pion/rtp v1.8.6 h1:MTmn/b0aWWsAzux2AmP8WGllusBVw4NPYPVFFd7jUPw=
|
||||||
|
github.com/pion/rtp v1.8.6/go.mod h1:pBGHaFt/yW7bf1jjWAoUjpSNoDnw98KTMg+jWWvziqU=
|
||||||
|
github.com/pion/sctp v1.8.16 h1:PKrMs+o9EMLRvFfXq59WFsC+V8mN1wnKzqrv+3D/gYY=
|
||||||
|
github.com/pion/sctp v1.8.16/go.mod h1:P6PbDVA++OJMrVNg2AL3XtYHV4uD6dvfyOovCgMs0PE=
|
||||||
|
github.com/pion/sdp/v3 v3.0.9 h1:pX++dCHoHUwq43kuwf3PyJfHlwIj4hXA7Vrifiq0IJY=
|
||||||
|
github.com/pion/sdp/v3 v3.0.9/go.mod h1:B5xmvENq5IXJimIO4zfp6LAe1fD9N+kFv+V/1lOdz8M=
|
||||||
|
github.com/pion/srtp/v2 v2.0.18 h1:vKpAXfawO9RtTRKZJbG4y0v1b11NZxQnxRl85kGuUlo=
|
||||||
|
github.com/pion/srtp/v2 v2.0.18/go.mod h1:0KJQjA99A6/a0DOVTu1PhDSw0CXF2jTkqOoMg3ODqdA=
|
||||||
|
github.com/pion/stun v0.6.1 h1:8lp6YejULeHBF8NmV8e2787BogQhduZugh5PdhDyyN4=
|
||||||
|
github.com/pion/stun v0.6.1/go.mod h1:/hO7APkX4hZKu/D0f2lHzNyvdkTGtIy3NDmLR7kSz/8=
|
||||||
|
github.com/pion/transport/v2 v2.2.5 h1:iyi25i/21gQck4hfRhomF6SktmUQjRsRW4WJdhfc3Kc=
|
||||||
|
github.com/pion/transport/v2 v2.2.5/go.mod h1:q2U/tf9FEfnSBGSW6w5Qp5PFWRLRj3NjLhCCgpRK4p0=
|
||||||
|
github.com/pion/turn/v2 v2.1.6 h1:Xr2niVsiPTB0FPtt+yAWKFUkU1eotQbGgpTIld4x1Gc=
|
||||||
|
github.com/pion/turn/v2 v2.1.6/go.mod h1:huEpByKKHix2/b9kmTAM3YoX6MKP+/D//0ClgUYR2fY=
|
||||||
|
github.com/pion/webrtc/v3 v3.2.42 h1:WN/ZuMjtpQOoGRCZUg/zFG+JHEvYLVyDKOxU6H1qWlE=
|
||||||
|
github.com/pion/webrtc/v3 v3.2.42/go.mod h1:M1RAe3TNTD1tzyvqHrbVODfwdPGSXOUo/OgpoGGJqFY=
|
||||||
|
github.com/pkg/errors v0.8.0/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/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo=
|
||||||
|
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||||
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/polydawn/refmt v0.89.0 h1:ADJTApkvkeBZsN0tBTx8QjpD9JkmxbKp0cxfr9qszm4=
|
||||||
|
github.com/polydawn/refmt v0.89.0/go.mod h1:/zvteZs/GwLtCgZ4BL6CBsk9IKIlexP43ObX9AxTqTw=
|
||||||
|
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||||
|
github.com/posener/complete v1.2.3 h1:NP0eAhjcjImqslEwo/1hq7gpajME0fTLTezBKDqfXqo=
|
||||||
|
github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s=
|
||||||
|
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||||
|
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
||||||
|
github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
|
||||||
|
github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q=
|
||||||
|
github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0=
|
||||||
|
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||||
|
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
|
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
|
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
|
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
|
||||||
|
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
|
||||||
|
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||||
|
github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
|
||||||
|
github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
|
||||||
|
github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
|
||||||
|
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||||
|
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||||
|
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
|
||||||
|
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
|
||||||
|
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
|
||||||
|
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
|
||||||
|
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
|
||||||
|
github.com/quic-go/quic-go v0.48.2 h1:wsKXZPeGWpMpCGSWqOcqpW2wZYic/8T3aqiOID0/KWE=
|
||||||
|
github.com/quic-go/quic-go v0.48.2/go.mod h1:yBgs3rWBOADpga7F+jJsb6Ybg1LSYiQvwWlLX+/6HMs=
|
||||||
|
github.com/quic-go/webtransport-go v0.8.0 h1:HxSrwun11U+LlmwpgM1kEqIqH90IT4N8auv/cD7QFJg=
|
||||||
|
github.com/quic-go/webtransport-go v0.8.0/go.mod h1:N99tjprW432Ut5ONql/aUhSLT0YVSlwHohQsuac9WaM=
|
||||||
|
github.com/raulk/go-watchdog v1.3.0 h1:oUmdlHxdkXRJlwfG0O9omj8ukerm8MEQavSiDTEtBsk=
|
||||||
|
github.com/raulk/go-watchdog v1.3.0/go.mod h1:fIvOnLbF0b0ZwkB9YU4mOW9Did//4vPZtDqv66NfsMU=
|
||||||
|
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 v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||||
|
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
|
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||||
|
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I=
|
||||||
|
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||||
|
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
||||||
|
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
|
||||||
|
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
|
||||||
|
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||||
|
github.com/siderolabs/discovery-api v0.1.4 h1:2fMEFSMiWaD1zDiBDY5md8VxItvL1rDQRSOfeXNjYKc=
|
||||||
|
github.com/siderolabs/discovery-api v0.1.4/go.mod h1:kaBy+G42v2xd/uAF/NIe383sjNTBE2AhxPTyi9SZI0s=
|
||||||
|
github.com/siderolabs/discovery-client v0.1.9 h1:yDzvts++Nf/2qczdDUfU5GAibkEIgz/eo9RPG/k/rOc=
|
||||||
|
github.com/siderolabs/discovery-client v0.1.9/go.mod h1:Ew1z07eyJwqNwum84IKYH4S649KEKK5WUmRW49HlXS8=
|
||||||
|
github.com/siderolabs/gen v0.4.8 h1:VNpbmDLhkXp7qcSEkKk1Ee7vU2afs3xvHrWLGR2UuiY=
|
||||||
|
github.com/siderolabs/gen v0.4.8/go.mod h1:7ROKVHHB68R3Amrd4a1ZXz/oMrXWF3Mg3lSEgnkJY5c=
|
||||||
|
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||||
|
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||||
|
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||||
|
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||||
|
github.com/smartystreets/assertions v1.2.0 h1:42S6lae5dvLc7BrLu/0ugRtcFVjoJNMC/N3yZFZkDFs=
|
||||||
|
github.com/smartystreets/assertions v1.2.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo=
|
||||||
|
github.com/smartystreets/goconvey v1.7.2 h1:9RBaZCeXEQ3UselpuwUQHltGVXvdwm6cv1hgR6gDIPg=
|
||||||
|
github.com/smartystreets/goconvey v1.7.2/go.mod h1:Vw0tHAZW6lzCRk3xgdin6fKYcG+G3Pg9vgXWeJpQFMM=
|
||||||
|
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||||
|
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
|
||||||
|
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||||
|
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||||
|
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||||
|
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||||
|
github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w=
|
||||||
|
github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||||
|
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
|
||||||
|
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||||
|
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||||
|
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
|
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||||
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
|
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
|
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||||
|
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||||
|
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
|
||||||
|
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||||
|
github.com/urfave/cli v1.22.10/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||||
|
github.com/vishvananda/netlink v1.3.1 h1:3AEMt62VKqz90r0tmNhog0r/PpWKmrEShJU0wJW6bV0=
|
||||||
|
github.com/vishvananda/netlink v1.3.1/go.mod h1:ARtKouGSTGchR8aMwmkzC0qiNPrrWO5JS/XMVl45+b4=
|
||||||
|
github.com/vishvananda/netns v0.0.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zdEY=
|
||||||
|
github.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
|
||||||
|
github.com/warpfork/go-testmark v0.12.1 h1:rMgCpJfwy1sJ50x0M0NgyphxYYPMOODIJHhsXyEHU0s=
|
||||||
|
github.com/warpfork/go-testmark v0.12.1/go.mod h1:kHwy7wfvGSPh1rQJYKayD4AbtNaeyZdcGi9tNJTaa5Y=
|
||||||
|
github.com/warpfork/go-wish v0.0.0-20220906213052-39a1cc7a02d0 h1:GDDkbFiaK8jsSDJfjId/PEGEShv6ugrt4kYsC5UIDaQ=
|
||||||
|
github.com/warpfork/go-wish v0.0.0-20220906213052-39a1cc7a02d0/go.mod h1:x6AKhvSSexNrVSrViXSHUEbICjmGXhtgABaHIySUSGw=
|
||||||
|
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||||
|
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.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||||
|
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
|
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
|
||||||
|
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
|
||||||
|
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
||||||
|
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||||
|
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||||
|
go.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg=
|
||||||
|
go.opentelemetry.io/otel v1.36.0/go.mod h1:/TcFMXYjyRNh8khOAO9ybYkqaDBb/70aVwkNML4pP8E=
|
||||||
|
go.opentelemetry.io/otel/metric v1.36.0 h1:MoWPKVhQvJ+eeXWHFBOPoBOi20jh6Iq2CcCREuTYufE=
|
||||||
|
go.opentelemetry.io/otel/metric v1.36.0/go.mod h1:zC7Ks+yeyJt4xig9DEw9kuUFe5C3zLbVjV2PzT6qzbs=
|
||||||
|
go.opentelemetry.io/otel/sdk v1.36.0 h1:b6SYIuLRs88ztox4EyrvRti80uXIFy+Sqzoh9kFULbs=
|
||||||
|
go.opentelemetry.io/otel/sdk v1.36.0/go.mod h1:+lC+mTgD+MUWfjJubi2vvXWcVxyr9rmlshZni72pXeY=
|
||||||
|
go.opentelemetry.io/otel/sdk/metric v1.36.0 h1:r0ntwwGosWGaa0CrSt8cuNuTcccMXERFwHX4dThiPis=
|
||||||
|
go.opentelemetry.io/otel/sdk/metric v1.36.0/go.mod h1:qTNOhFDfKRwX0yXOqJYegL5WRaW376QbB7P4Pb0qva4=
|
||||||
|
go.opentelemetry.io/otel/trace v1.36.0 h1:ahxWNuqZjpdiFAyrIoQ4GIiAIhxAunQR6MUoKrsNd4w=
|
||||||
|
go.opentelemetry.io/otel/trace v1.36.0/go.mod h1:gQ+OnDZzrybY4k4seLzPAWNwVBBVlF2szhehOBB/tGA=
|
||||||
|
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||||
|
go.uber.org/dig v1.17.1 h1:Tga8Lz8PcYNsWsyHMZ1Vm0OQOUaJNDyvPImgbAu9YSc=
|
||||||
|
go.uber.org/dig v1.17.1/go.mod h1:Us0rSJiThwCv2GteUN0Q7OKvU7n5J4dxZ9JKUXozFdE=
|
||||||
|
go.uber.org/fx v1.22.1 h1:nvvln7mwyT5s1q201YE29V/BFrGor6vMiDNpU/78Mys=
|
||||||
|
go.uber.org/fx v1.22.1/go.mod h1:HT2M7d7RHo+ebKGh9NRcrsrHHfpZ60nW3QRubMRfv48=
|
||||||
|
go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
|
||||||
|
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||||
|
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||||
|
go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y=
|
||||||
|
go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU=
|
||||||
|
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||||
|
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||||
|
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||||
|
go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI=
|
||||||
|
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
|
||||||
|
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||||
|
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M=
|
||||||
|
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
|
||||||
|
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
|
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
|
||||||
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
|
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
|
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
||||||
|
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-20250408133849-7e4ce0ab07d0 h1:R84qjqJb5nVJMxqWYb3np9L5ZsaDtB+a39EqjV0JSUM=
|
||||||
|
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:S9Xr4PYopiDyqSyp5NjCrhFrqg6A5zA2E/iPHPhqnS8=
|
||||||
|
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
|
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||||
|
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
|
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
|
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
|
golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ=
|
||||||
|
golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc=
|
||||||
|
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-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
|
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
|
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||||
|
golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8=
|
||||||
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
|
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
||||||
|
golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
|
||||||
|
golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-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-20210220032951-036812b2e83c/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.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||||
|
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||||
|
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/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-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/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-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
|
||||||
|
golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||||
|
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.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
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.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
|
||||||
|
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
|
||||||
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||||
|
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
|
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
|
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
|
golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
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.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||||
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
|
golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg=
|
||||||
|
golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s=
|
||||||
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 h1:/jFs0duh4rdb8uIfPMv78iAJGcPKDeqAFnaLBropIC4=
|
||||||
|
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173/go.mod h1:tkCQ4FQXmpAgYVh++1cq16/dH4QJtmvpRv19DWGAHSA=
|
||||||
|
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6 h1:CawjfCvYQH2OU3/TnxLx97WDSUDRABfT18pCOYwc2GE=
|
||||||
|
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6/go.mod h1:3rxYc4HtVcSG9gVaTs2GEBdehh+sYPOwKtyUWEOTb80=
|
||||||
|
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||||
|
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
|
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
|
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
|
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||||
|
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a h1:v2PbRU4K3llS09c7zodFpNePeamkAwG3mPrAery9VeE=
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
||||||
|
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.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||||
|
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||||
|
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||||
|
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
||||||
|
google.golang.org/grpc v1.74.2 h1:WoosgB65DlWVC9FqI82dGsZhWFNBSLjQ84bjROOpMu4=
|
||||||
|
google.golang.org/grpc v1.74.2/go.mod h1:CtQ+BGjaAIXHs/5YS3i473GqwBBa1zGQNevxdeBEXrM=
|
||||||
|
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||||
|
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||||
|
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||||
|
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||||
|
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||||
|
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
|
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
|
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
|
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||||
|
google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw=
|
||||||
|
google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
|
||||||
|
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||||
|
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-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
|
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||||
|
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q=
|
||||||
|
gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA=
|
||||||
|
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
lukechampine.com/blake3 v1.3.0 h1:sJ3XhFINmHSrYCgl958hscfIa3bw8x4DqMP3u1YvoYE=
|
||||||
|
lukechampine.com/blake3 v1.3.0/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k=
|
||||||
@@ -14,12 +14,12 @@ require (
|
|||||||
github.com/caddyserver/caddy/v2 v2.8.4
|
github.com/caddyserver/caddy/v2 v2.8.4
|
||||||
github.com/cenkalti/backoff/v4 v4.3.0
|
github.com/cenkalti/backoff/v4 v4.3.0
|
||||||
github.com/charmbracelet/colorprofile v0.4.2
|
github.com/charmbracelet/colorprofile v0.4.2
|
||||||
|
github.com/charmbracelet/x/ansi v0.11.6
|
||||||
github.com/compose-spec/compose-go/v2 v2.9.0
|
github.com/compose-spec/compose-go/v2 v2.9.0
|
||||||
github.com/containerd/errdefs v1.0.0
|
github.com/containerd/errdefs v1.0.0
|
||||||
github.com/containerd/platforms v1.0.0-rc.1
|
github.com/containerd/platforms v1.0.0-rc.1
|
||||||
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf
|
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf
|
||||||
github.com/deckarep/golang-set/v2 v2.8.0
|
github.com/deckarep/golang-set/v2 v2.8.0
|
||||||
github.com/dgraph-io/badger/v3 v3.2103.5
|
|
||||||
github.com/distribution/reference v0.6.0
|
github.com/distribution/reference v0.6.0
|
||||||
github.com/docker/cli v28.5.0+incompatible
|
github.com/docker/cli v28.5.0+incompatible
|
||||||
github.com/docker/compose/v2 v2.40.0
|
github.com/docker/compose/v2 v2.40.0
|
||||||
@@ -29,30 +29,18 @@ require (
|
|||||||
github.com/goccy/go-yaml v1.17.1
|
github.com/goccy/go-yaml v1.17.1
|
||||||
github.com/google/go-cmp v0.7.0
|
github.com/google/go-cmp v0.7.0
|
||||||
github.com/google/go-containerregistry v0.20.2
|
github.com/google/go-containerregistry v0.20.2
|
||||||
github.com/hashicorp/memberlist v0.5.1
|
|
||||||
github.com/hashicorp/serf v0.10.1
|
|
||||||
github.com/ipfs/boxo v0.21.0
|
|
||||||
github.com/ipfs/go-cid v0.4.1
|
|
||||||
github.com/ipfs/go-datastore v0.6.0
|
|
||||||
github.com/ipfs/go-ds-badger3 v0.0.2
|
|
||||||
github.com/ipfs/go-ds-crdt v0.5.2
|
|
||||||
github.com/ipfs/go-ipld-format v0.6.0
|
|
||||||
github.com/ipfs/go-log/v2 v2.5.1
|
|
||||||
github.com/jmoiron/sqlx v1.4.0
|
github.com/jmoiron/sqlx v1.4.0
|
||||||
github.com/lmittmann/tint v1.0.5
|
|
||||||
github.com/miekg/dns v1.1.65
|
github.com/miekg/dns v1.1.65
|
||||||
github.com/mitchellh/mapstructure v1.5.0
|
github.com/mitchellh/mapstructure v1.5.0
|
||||||
github.com/moby/term v0.5.2
|
github.com/moby/term v0.5.2
|
||||||
github.com/opencontainers/go-digest v1.0.0
|
github.com/opencontainers/go-digest v1.0.0
|
||||||
github.com/opencontainers/image-spec v1.1.1
|
github.com/opencontainers/image-spec v1.1.1
|
||||||
github.com/psviderski/unregistry v0.4.1
|
github.com/psviderski/unregistry v0.4.1
|
||||||
github.com/siderolabs/discovery-api v0.1.4
|
|
||||||
github.com/siderolabs/discovery-client v0.1.9
|
|
||||||
github.com/siderolabs/grpc-proxy v0.5.1
|
github.com/siderolabs/grpc-proxy v0.5.1
|
||||||
github.com/spf13/cobra v1.10.1
|
github.com/spf13/cobra v1.10.1
|
||||||
|
github.com/spf13/pflag v1.0.10
|
||||||
github.com/stretchr/testify v1.11.1
|
github.com/stretchr/testify v1.11.1
|
||||||
github.com/vishvananda/netlink v1.3.1
|
github.com/vishvananda/netlink v1.3.1
|
||||||
go.uber.org/zap v1.27.0
|
|
||||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba
|
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba
|
||||||
golang.org/x/crypto v0.41.0
|
golang.org/x/crypto v0.41.0
|
||||||
golang.org/x/net v0.43.0
|
golang.org/x/net v0.43.0
|
||||||
@@ -84,9 +72,6 @@ require (
|
|||||||
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect
|
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect
|
||||||
github.com/antlr4-go/antlr/v4 v4.13.0 // indirect
|
github.com/antlr4-go/antlr/v4 v4.13.0 // indirect
|
||||||
github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect
|
github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect
|
||||||
github.com/armon/circbuf v0.0.0-20190214190532-5111143e8da2 // indirect
|
|
||||||
github.com/armon/go-metrics v0.4.1 // indirect
|
|
||||||
github.com/armon/go-radix v1.0.0 // indirect
|
|
||||||
github.com/aryann/difflib v0.0.0-20210328193216-ff5ff6dc229b // indirect
|
github.com/aryann/difflib v0.0.0-20210328193216-ff5ff6dc229b // indirect
|
||||||
github.com/atotto/clipboard v0.1.4 // indirect
|
github.com/atotto/clipboard v0.1.4 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2 v1.30.3 // indirect
|
github.com/aws/aws-sdk-go-v2 v1.30.3 // indirect
|
||||||
@@ -103,7 +88,6 @@ require (
|
|||||||
github.com/aws/aws-sdk-go-v2/service/sts v1.30.3 // indirect
|
github.com/aws/aws-sdk-go-v2/service/sts v1.30.3 // indirect
|
||||||
github.com/aws/smithy-go v1.20.3 // indirect
|
github.com/aws/smithy-go v1.20.3 // indirect
|
||||||
github.com/beorn7/perks v1.0.1 // indirect
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
github.com/bgentry/speakeasy v0.2.0 // indirect
|
|
||||||
github.com/buger/goterm v1.0.4 // indirect
|
github.com/buger/goterm v1.0.4 // indirect
|
||||||
github.com/caddyserver/certmagic v0.21.4 // indirect
|
github.com/caddyserver/certmagic v0.21.4 // indirect
|
||||||
github.com/caddyserver/zerossl v0.1.3 // indirect
|
github.com/caddyserver/zerossl v0.1.3 // indirect
|
||||||
@@ -111,7 +95,6 @@ require (
|
|||||||
github.com/cespare/xxhash v1.1.0 // indirect
|
github.com/cespare/xxhash v1.1.0 // indirect
|
||||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||||
github.com/charmbracelet/ultraviolet v0.0.0-20260205113103-524a6607adb8 // indirect
|
github.com/charmbracelet/ultraviolet v0.0.0-20260205113103-524a6607adb8 // indirect
|
||||||
github.com/charmbracelet/x/ansi v0.11.6 // indirect
|
|
||||||
github.com/charmbracelet/x/exp/ordered v0.1.0 // indirect
|
github.com/charmbracelet/x/exp/ordered v0.1.0 // indirect
|
||||||
github.com/charmbracelet/x/exp/strings v0.0.0-20240919170804-a4978c8e603a // indirect
|
github.com/charmbracelet/x/exp/strings v0.0.0-20240919170804-a4978c8e603a // indirect
|
||||||
github.com/charmbracelet/x/term v0.2.2 // indirect
|
github.com/charmbracelet/x/term v0.2.2 // indirect
|
||||||
@@ -134,7 +117,6 @@ require (
|
|||||||
github.com/containerd/typeurl/v2 v2.2.3 // indirect
|
github.com/containerd/typeurl/v2 v2.2.3 // indirect
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect
|
github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect
|
||||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect
|
|
||||||
github.com/dgraph-io/badger v1.6.2 // indirect
|
github.com/dgraph-io/badger v1.6.2 // indirect
|
||||||
github.com/dgraph-io/badger/v2 v2.2007.4 // indirect
|
github.com/dgraph-io/badger/v2 v2.2007.4 // indirect
|
||||||
github.com/dgraph-io/ristretto v0.1.1 // indirect
|
github.com/dgraph-io/ristretto v0.1.1 // indirect
|
||||||
@@ -152,7 +134,6 @@ require (
|
|||||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||||
github.com/eiannone/keyboard v0.0.0-20220611211555-0d226195f203 // indirect
|
github.com/eiannone/keyboard v0.0.0-20220611211555-0d226195f203 // indirect
|
||||||
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
|
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
|
||||||
github.com/fatih/color v1.18.0 // indirect
|
|
||||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||||
github.com/fsnotify/fsevents v0.2.0 // indirect
|
github.com/fsnotify/fsevents v0.2.0 // indirect
|
||||||
github.com/fvbommel/sortorder v1.1.0 // indirect
|
github.com/fvbommel/sortorder v1.1.0 // indirect
|
||||||
@@ -179,7 +160,6 @@ require (
|
|||||||
github.com/golang/snappy v0.0.4 // indirect
|
github.com/golang/snappy v0.0.4 // indirect
|
||||||
github.com/google/btree v1.1.2 // indirect
|
github.com/google/btree v1.1.2 // indirect
|
||||||
github.com/google/cel-go v0.20.1 // indirect
|
github.com/google/cel-go v0.20.1 // indirect
|
||||||
github.com/google/flatbuffers v24.3.25+incompatible // indirect
|
|
||||||
github.com/google/gnostic-models v0.6.8 // indirect
|
github.com/google/gnostic-models v0.6.8 // indirect
|
||||||
github.com/google/gofuzz v1.2.0 // indirect
|
github.com/google/gofuzz v1.2.0 // indirect
|
||||||
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 // indirect
|
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 // indirect
|
||||||
@@ -191,31 +171,15 @@ require (
|
|||||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 // indirect
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 // indirect
|
||||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||||
github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
|
|
||||||
github.com/hashicorp/go-immutable-radix/v2 v2.1.0 // indirect
|
|
||||||
github.com/hashicorp/go-msgpack v0.5.5 // indirect
|
|
||||||
github.com/hashicorp/go-msgpack/v2 v2.1.2 // indirect
|
|
||||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||||
github.com/hashicorp/go-sockaddr v1.0.6 // indirect
|
|
||||||
github.com/hashicorp/go-syslog v1.0.0 // indirect
|
|
||||||
github.com/hashicorp/go-version v1.7.0 // indirect
|
github.com/hashicorp/go-version v1.7.0 // indirect
|
||||||
github.com/hashicorp/golang-lru v1.0.2 // indirect
|
|
||||||
github.com/hashicorp/golang-lru/arc/v2 v2.0.7 // indirect
|
github.com/hashicorp/golang-lru/arc/v2 v2.0.7 // indirect
|
||||||
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
|
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
|
||||||
github.com/hashicorp/logutils v1.0.0 // indirect
|
|
||||||
github.com/hashicorp/mdns v1.0.5 // indirect
|
|
||||||
github.com/huandu/xstrings v1.5.0 // indirect
|
github.com/huandu/xstrings v1.5.0 // indirect
|
||||||
github.com/imdario/mergo v0.3.16 // indirect
|
github.com/imdario/mergo v0.3.16 // indirect
|
||||||
github.com/in-toto/in-toto-golang v0.9.0 // indirect
|
github.com/in-toto/in-toto-golang v0.9.0 // indirect
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||||
github.com/inhies/go-bytesize v0.0.0-20220417184213-4913239db9cf // indirect
|
github.com/inhies/go-bytesize v0.0.0-20220417184213-4913239db9cf // indirect
|
||||||
github.com/ipfs/bbloom v0.0.4 // indirect
|
|
||||||
github.com/ipfs/go-block-format v0.2.0 // indirect
|
|
||||||
github.com/ipfs/go-ipfs-util v0.0.3 // indirect
|
|
||||||
github.com/ipfs/go-ipld-legacy v0.2.1 // indirect
|
|
||||||
github.com/ipfs/go-metrics-interface v0.0.1 // indirect
|
|
||||||
github.com/ipld/go-codec-dagpb v1.6.0 // indirect
|
|
||||||
github.com/ipld/go-ipld-prime v0.21.0 // indirect
|
|
||||||
github.com/ishidawataru/sctp v0.0.0-20230406120618-7ff4192f6ff2 // indirect
|
github.com/ishidawataru/sctp v0.0.0-20230406120618-7ff4192f6ff2 // indirect
|
||||||
github.com/jackc/chunkreader/v2 v2.0.1 // indirect
|
github.com/jackc/chunkreader/v2 v2.0.1 // indirect
|
||||||
github.com/jackc/pgconn v1.14.3 // indirect
|
github.com/jackc/pgconn v1.14.3 // indirect
|
||||||
@@ -225,7 +189,6 @@ require (
|
|||||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
|
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
|
||||||
github.com/jackc/pgtype v1.14.0 // indirect
|
github.com/jackc/pgtype v1.14.0 // indirect
|
||||||
github.com/jackc/pgx/v4 v4.18.3 // indirect
|
github.com/jackc/pgx/v4 v4.18.3 // indirect
|
||||||
github.com/jbenet/goprocess v0.1.4 // indirect
|
|
||||||
github.com/jonboulle/clockwork v0.5.0 // indirect
|
github.com/jonboulle/clockwork v0.5.0 // indirect
|
||||||
github.com/josharian/intern v1.0.0 // indirect
|
github.com/josharian/intern v1.0.0 // indirect
|
||||||
github.com/josharian/native v1.1.0 // indirect
|
github.com/josharian/native v1.1.0 // indirect
|
||||||
@@ -236,10 +199,6 @@ require (
|
|||||||
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect
|
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect
|
||||||
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect
|
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect
|
||||||
github.com/libdns/libdns v0.2.2 // indirect
|
github.com/libdns/libdns v0.2.2 // indirect
|
||||||
github.com/libp2p/go-buffer-pool v0.1.0 // indirect
|
|
||||||
github.com/libp2p/go-libp2p v0.35.4 // indirect
|
|
||||||
github.com/libp2p/go-libp2p-pubsub v0.11.0 // indirect
|
|
||||||
github.com/libp2p/go-msgio v0.3.0 // indirect
|
|
||||||
github.com/lucasb-eyer/go-colorful v1.3.0 // indirect
|
github.com/lucasb-eyer/go-colorful v1.3.0 // indirect
|
||||||
github.com/mailru/easyjson v0.7.7 // indirect
|
github.com/mailru/easyjson v0.7.7 // indirect
|
||||||
github.com/manifoldco/promptui v0.9.0 // indirect
|
github.com/manifoldco/promptui v0.9.0 // indirect
|
||||||
@@ -253,8 +212,6 @@ require (
|
|||||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
|
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
|
||||||
github.com/mholt/acmez/v2 v2.0.3 // indirect
|
github.com/mholt/acmez/v2 v2.0.3 // indirect
|
||||||
github.com/miekg/pkcs11 v1.1.1 // indirect
|
github.com/miekg/pkcs11 v1.1.1 // indirect
|
||||||
github.com/minio/sha256-simd v1.0.1 // indirect
|
|
||||||
github.com/mitchellh/cli v1.1.5 // indirect
|
|
||||||
github.com/mitchellh/copystructure v1.2.0 // indirect
|
github.com/mitchellh/copystructure v1.2.0 // indirect
|
||||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||||
github.com/mitchellh/go-ps v1.0.0 // indirect
|
github.com/mitchellh/go-ps v1.0.0 // indirect
|
||||||
@@ -277,17 +234,7 @@ require (
|
|||||||
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/mr-tron/base58 v1.2.0 // indirect
|
|
||||||
github.com/muesli/cancelreader v0.2.2 // indirect
|
github.com/muesli/cancelreader v0.2.2 // indirect
|
||||||
github.com/multiformats/go-base32 v0.1.0 // indirect
|
|
||||||
github.com/multiformats/go-base36 v0.2.0 // indirect
|
|
||||||
github.com/multiformats/go-multiaddr v0.13.0 // indirect
|
|
||||||
github.com/multiformats/go-multiaddr-fmt v0.1.0 // indirect
|
|
||||||
github.com/multiformats/go-multibase v0.2.0 // indirect
|
|
||||||
github.com/multiformats/go-multicodec v0.9.0 // indirect
|
|
||||||
github.com/multiformats/go-multihash v0.2.3 // indirect
|
|
||||||
github.com/multiformats/go-multistream v0.5.0 // indirect
|
|
||||||
github.com/multiformats/go-varint v0.0.7 // indirect
|
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||||
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
|
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
|
||||||
github.com/ncruces/go-strftime v0.1.9 // indirect
|
github.com/ncruces/go-strftime v0.1.9 // indirect
|
||||||
@@ -299,8 +246,6 @@ require (
|
|||||||
github.com/pkg/errors v0.9.1 // indirect
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect
|
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||||
github.com/polydawn/refmt v0.89.0 // indirect
|
|
||||||
github.com/posener/complete v1.2.3 // indirect
|
|
||||||
github.com/prometheus/client_golang v1.22.0 // indirect
|
github.com/prometheus/client_golang v1.22.0 // indirect
|
||||||
github.com/prometheus/client_model v0.6.1 // indirect
|
github.com/prometheus/client_model v0.6.1 // indirect
|
||||||
github.com/prometheus/common v0.62.0 // indirect
|
github.com/prometheus/common v0.62.0 // indirect
|
||||||
@@ -315,13 +260,11 @@ require (
|
|||||||
github.com/rs/xid v1.5.0 // indirect
|
github.com/rs/xid v1.5.0 // indirect
|
||||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||||
github.com/santhosh-tekuri/jsonschema/v6 v6.0.1 // indirect
|
github.com/santhosh-tekuri/jsonschema/v6 v6.0.1 // indirect
|
||||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 // indirect
|
|
||||||
github.com/secure-systems-lab/go-securesystemslib v0.6.0 // indirect
|
github.com/secure-systems-lab/go-securesystemslib v0.6.0 // indirect
|
||||||
github.com/serialx/hashring v0.0.0-20200727003509-22c0c7ab6b1b // indirect
|
github.com/serialx/hashring v0.0.0-20200727003509-22c0c7ab6b1b // indirect
|
||||||
github.com/shibumi/go-pathspec v1.3.0 // indirect
|
github.com/shibumi/go-pathspec v1.3.0 // indirect
|
||||||
github.com/shopspring/decimal v1.4.0 // indirect
|
github.com/shopspring/decimal v1.4.0 // indirect
|
||||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
|
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
|
||||||
github.com/siderolabs/gen v0.4.8 // indirect
|
|
||||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||||
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 // indirect
|
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 // indirect
|
||||||
github.com/slackhq/nebula v1.6.1 // indirect
|
github.com/slackhq/nebula v1.6.1 // indirect
|
||||||
@@ -330,9 +273,7 @@ require (
|
|||||||
github.com/smallstep/pkcs7 v0.1.1 // indirect
|
github.com/smallstep/pkcs7 v0.1.1 // indirect
|
||||||
github.com/smallstep/scep v0.0.0-20231024192529-aee96d7ad34d // indirect
|
github.com/smallstep/scep v0.0.0-20231024192529-aee96d7ad34d // indirect
|
||||||
github.com/smallstep/truststore v0.13.0 // indirect
|
github.com/smallstep/truststore v0.13.0 // indirect
|
||||||
github.com/spaolacci/murmur3 v1.1.0 // indirect
|
|
||||||
github.com/spf13/cast v1.7.0 // indirect
|
github.com/spf13/cast v1.7.0 // indirect
|
||||||
github.com/spf13/pflag v1.0.10 // indirect
|
|
||||||
github.com/stoewer/go-strcase v1.2.0 // indirect
|
github.com/stoewer/go-strcase v1.2.0 // indirect
|
||||||
github.com/stretchr/objx v0.5.2 // indirect
|
github.com/stretchr/objx v0.5.2 // indirect
|
||||||
github.com/tailscale/tscert v0.0.0-20240517230440-bbccfbf48933 // indirect
|
github.com/tailscale/tscert v0.0.0-20240517230440-bbccfbf48933 // indirect
|
||||||
@@ -384,6 +325,7 @@ require (
|
|||||||
go.uber.org/automaxprocs v1.5.3 // indirect
|
go.uber.org/automaxprocs v1.5.3 // indirect
|
||||||
go.uber.org/mock v0.6.0 // indirect
|
go.uber.org/mock v0.6.0 // indirect
|
||||||
go.uber.org/multierr v1.11.0 // indirect
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
|
go.uber.org/zap v1.27.0 // indirect
|
||||||
go.uber.org/zap/exp v0.2.0 // indirect
|
go.uber.org/zap/exp v0.2.0 // indirect
|
||||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||||
golang.org/x/crypto/x509roots/fallback v0.0.0-20240507223354-67b13616a595 // indirect
|
golang.org/x/crypto/x509roots/fallback v0.0.0-20240507223354-67b13616a595 // indirect
|
||||||
@@ -408,7 +350,6 @@ require (
|
|||||||
k8s.io/klog/v2 v2.130.1 // indirect
|
k8s.io/klog/v2 v2.130.1 // indirect
|
||||||
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f // indirect
|
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f // indirect
|
||||||
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect
|
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect
|
||||||
lukechampine.com/blake3 v1.3.0 // indirect
|
|
||||||
modernc.org/libc v1.61.13 // indirect
|
modernc.org/libc v1.61.13 // indirect
|
||||||
modernc.org/mathutil v1.7.1 // indirect
|
modernc.org/mathutil v1.7.1 // indirect
|
||||||
modernc.org/memory v1.8.2 // indirect
|
modernc.org/memory v1.8.2 // indirect
|
||||||
|
|||||||
@@ -35,7 +35,6 @@ github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg6
|
|||||||
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/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg=
|
github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg=
|
||||||
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||||
github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
|
|
||||||
github.com/DefangLabs/secret-detector v0.0.0-20250403165618-22662109213e h1:rd4bOvKmDIx0WeTv9Qz+hghsgyjikFiPrseXHlKepO0=
|
github.com/DefangLabs/secret-detector v0.0.0-20250403165618-22662109213e h1:rd4bOvKmDIx0WeTv9Qz+hghsgyjikFiPrseXHlKepO0=
|
||||||
github.com/DefangLabs/secret-detector v0.0.0-20250403165618-22662109213e/go.mod h1:blbwPQh4DTlCZEfk1BLU4oMIhLda2U+A840Uag9DsZw=
|
github.com/DefangLabs/secret-detector v0.0.0-20250403165618-22662109213e/go.mod h1:blbwPQh4DTlCZEfk1BLU4oMIhLda2U+A840Uag9DsZw=
|
||||||
github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ=
|
github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ=
|
||||||
@@ -48,7 +47,6 @@ github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0
|
|||||||
github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
|
github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
|
||||||
github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
|
github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
|
||||||
github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
|
github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
|
||||||
github.com/Masterminds/sprig/v3 v3.2.1/go.mod h1:UoaO7Yp8KlPnJIYWTFkMaqPUYKTfGFPhxNuwnnxkKlk=
|
|
||||||
github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA=
|
github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA=
|
||||||
github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM=
|
github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM=
|
||||||
github.com/Masterminds/squirrel v1.5.4 h1:uUcX/aBc8O7Fg9kaISIUsHXdKuqehiXAMQTYX8afzqM=
|
github.com/Masterminds/squirrel v1.5.4 h1:uUcX/aBc8O7Fg9kaISIUsHXdKuqehiXAMQTYX8afzqM=
|
||||||
@@ -74,25 +72,14 @@ github.com/alecthomas/chroma/v2 v2.20.0/go.mod h1:e7tViK0xh/Nf4BYHl00ycY6rV7b8iX
|
|||||||
github.com/alecthomas/repr v0.5.1 h1:E3G4t2QbHTSNpPKBgMTln5KLkZHLOcU7r37J4pXBuIg=
|
github.com/alecthomas/repr v0.5.1 h1:E3G4t2QbHTSNpPKBgMTln5KLkZHLOcU7r37J4pXBuIg=
|
||||||
github.com/alecthomas/repr v0.5.1/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
|
github.com/alecthomas/repr v0.5.1/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
|
||||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
|
||||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||||
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
|
||||||
github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092 h1:aM1rlcoLz8y5B2r4tTLMiVTrMtpfY0O8EScKJxaSaEc=
|
github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092 h1:aM1rlcoLz8y5B2r4tTLMiVTrMtpfY0O8EScKJxaSaEc=
|
||||||
github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092/go.mod h1:rYqSE9HbjzpHTI74vwPvae4ZVYZd1lue2ta6xHPdblA=
|
github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092/go.mod h1:rYqSE9HbjzpHTI74vwPvae4ZVYZd1lue2ta6xHPdblA=
|
||||||
github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI=
|
github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI=
|
||||||
github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g=
|
github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g=
|
||||||
github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew1u1fNQOlOtuGxQY=
|
github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew1u1fNQOlOtuGxQY=
|
||||||
github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4=
|
github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4=
|
||||||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
|
||||||
github.com/armon/circbuf v0.0.0-20190214190532-5111143e8da2 h1:7Ip0wMmLHLRJdrloDxZfhMm0xrLXZS8+COSu2bXmEQs=
|
|
||||||
github.com/armon/circbuf v0.0.0-20190214190532-5111143e8da2/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
|
||||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
|
||||||
github.com/armon/go-metrics v0.4.1 h1:hR91U9KYmb6bLBYLQjyM+3j+rcd/UhE+G78SFnF8gJA=
|
|
||||||
github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4=
|
|
||||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
|
||||||
github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI=
|
|
||||||
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
|
||||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
|
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
|
||||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
||||||
github.com/aryann/difflib v0.0.0-20210328193216-ff5ff6dc229b h1:uUXgbcPDK3KpW29o4iy7GtuappbWT0l5NaMo9H9pJDw=
|
github.com/aryann/difflib v0.0.0-20210328193216-ff5ff6dc229b h1:uUXgbcPDK3KpW29o4iy7GtuappbWT0l5NaMo9H9pJDw=
|
||||||
@@ -129,17 +116,11 @@ github.com/aws/smithy-go v1.20.3 h1:ryHwveWzPV5BIof6fyDvor6V3iUL7nTfiTKXHiW05nE=
|
|||||||
github.com/aws/smithy-go v1.20.3/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E=
|
github.com/aws/smithy-go v1.20.3/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E=
|
||||||
github.com/aymanbagabas/go-udiff v0.4.1 h1:OEIrQ8maEeDBXQDoGCbbTTXYJMYRCRO1fnodZ12Gv5o=
|
github.com/aymanbagabas/go-udiff v0.4.1 h1:OEIrQ8maEeDBXQDoGCbbTTXYJMYRCRO1fnodZ12Gv5o=
|
||||||
github.com/aymanbagabas/go-udiff v0.4.1/go.mod h1:0L9PGwj20lrtmEMeyw4WKJ/TMyDtvAoK9bf2u/mNo3w=
|
github.com/aymanbagabas/go-udiff v0.4.1/go.mod h1:0L9PGwj20lrtmEMeyw4WKJ/TMyDtvAoK9bf2u/mNo3w=
|
||||||
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
|
||||||
github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o=
|
|
||||||
github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
|
||||||
github.com/beorn7/perks v0.0.0-20150223135152-b965b613227f/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
github.com/beorn7/perks v0.0.0-20150223135152-b965b613227f/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
|
||||||
github.com/bgentry/speakeasy v0.2.0 h1:tgObeVOf8WAvtuAX6DhJ4xks4CFNwPDZiqzGqIHE51E=
|
|
||||||
github.com/bgentry/speakeasy v0.2.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
|
||||||
github.com/bitly/go-hostpool v0.1.0/go.mod h1:4gOCgp6+NZnVqlKyZ/iBZFTAJKembaVENUpMkpg42fw=
|
github.com/bitly/go-hostpool v0.1.0/go.mod h1:4gOCgp6+NZnVqlKyZ/iBZFTAJKembaVENUpMkpg42fw=
|
||||||
github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA=
|
github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA=
|
||||||
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
|
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
|
||||||
@@ -207,8 +188,6 @@ github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObk
|
|||||||
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/chzyer/test v1.0.0 h1:p3BQDXSxOhOG0P9z6/hGnII4LGiEPOYBhs8asl/fC04=
|
github.com/chzyer/test v1.0.0 h1:p3BQDXSxOhOG0P9z6/hGnII4LGiEPOYBhs8asl/fC04=
|
||||||
github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8=
|
github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8=
|
||||||
github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
|
|
||||||
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
|
|
||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
github.com/clipperhouse/displaywidth v0.11.0 h1:lBc6kY44VFw+TDx4I8opi/EtL9m20WSEFgwIwO+UVM8=
|
github.com/clipperhouse/displaywidth v0.11.0 h1:lBc6kY44VFw+TDx4I8opi/EtL9m20WSEFgwIwO+UVM8=
|
||||||
github.com/clipperhouse/displaywidth v0.11.0/go.mod h1:bkrFNkf81G8HyVqmKGxsPufD3JhNl3dSqnGhOoSD/o0=
|
github.com/clipperhouse/displaywidth v0.11.0/go.mod h1:bkrFNkf81G8HyVqmKGxsPufD3JhNl3dSqnGhOoSD/o0=
|
||||||
@@ -223,8 +202,6 @@ github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb h1:EDmT6Q9Zs+SbUo
|
|||||||
github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb/go.mod h1:ZjrT6AXHbDs86ZSdt/osfBi5qfexBrKUdONk989Wnk4=
|
github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb/go.mod h1:ZjrT6AXHbDs86ZSdt/osfBi5qfexBrKUdONk989Wnk4=
|
||||||
github.com/compose-spec/compose-go/v2 v2.9.0 h1:UHSv/QHlo6QJtrT4igF1rdORgIUhDo1gWuyJUoiNNIM=
|
github.com/compose-spec/compose-go/v2 v2.9.0 h1:UHSv/QHlo6QJtrT4igF1rdORgIUhDo1gWuyJUoiNNIM=
|
||||||
github.com/compose-spec/compose-go/v2 v2.9.0/go.mod h1:Oky9AZGTRB4E+0VbTPZTUu4Kp+oEMMuwZXZtPPVT1iE=
|
github.com/compose-spec/compose-go/v2 v2.9.0/go.mod h1:Oky9AZGTRB4E+0VbTPZTUu4Kp+oEMMuwZXZtPPVT1iE=
|
||||||
github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM=
|
|
||||||
github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw=
|
|
||||||
github.com/containerd/cgroups/v3 v3.0.5 h1:44na7Ud+VwyE7LIoJ8JTNQOa549a8543BmzaJHo6Bzo=
|
github.com/containerd/cgroups/v3 v3.0.5 h1:44na7Ud+VwyE7LIoJ8JTNQOa549a8543BmzaJHo6Bzo=
|
||||||
github.com/containerd/cgroups/v3 v3.0.5/go.mod h1:SA5DLYnXO8pTGYiAHXz94qvLQTKfVM5GEVisn4jpins=
|
github.com/containerd/cgroups/v3 v3.0.5/go.mod h1:SA5DLYnXO8pTGYiAHXz94qvLQTKfVM5GEVisn4jpins=
|
||||||
github.com/containerd/console v1.0.5 h1:R0ymNeydRqH2DmakFNdmjR2k0t7UPuiOV/N/27/qqsc=
|
github.com/containerd/console v1.0.5 h1:R0ymNeydRqH2DmakFNdmjR2k0t7UPuiOV/N/27/qqsc=
|
||||||
@@ -262,10 +239,7 @@ github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7
|
|||||||
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||||
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf h1:iW4rZ826su+pqaw19uhpSCzhj44qo35pNgKFGqzDKkU=
|
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf h1:iW4rZ826su+pqaw19uhpSCzhj44qo35pNgKFGqzDKkU=
|
||||||
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||||
github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
|
|
||||||
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
|
||||||
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3sHPnBo=
|
github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3sHPnBo=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||||
@@ -274,27 +248,17 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3
|
|||||||
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/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s=
|
github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s=
|
||||||
github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE=
|
github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE=
|
||||||
github.com/cskr/pubsub v1.0.2 h1:vlOzMhl6PFn60gRlTQQsIfVwaPB/B/8MziK8FhEPt/0=
|
|
||||||
github.com/cskr/pubsub v1.0.2/go.mod h1:/8MzYXk/NJAz782G8RPkFzXTZVu63VotefPnR9TIRis=
|
|
||||||
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/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c h1:pFUpOrbxDR6AkioZ1ySsx5yxlDQZ8stG2b88gTPxgJU=
|
|
||||||
github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c/go.mod h1:6UhI8N9EjYm1c2odKpFpAYeR8dsBeM7PtzQhRgxRr9U=
|
|
||||||
github.com/deckarep/golang-set/v2 v2.8.0 h1:swm0rlPCmdWn9mESxKOjWk8hXSqoxOp+ZlfuyaAdFlQ=
|
github.com/deckarep/golang-set/v2 v2.8.0 h1:swm0rlPCmdWn9mESxKOjWk8hXSqoxOp+ZlfuyaAdFlQ=
|
||||||
github.com/deckarep/golang-set/v2 v2.8.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4=
|
github.com/deckarep/golang-set/v2 v2.8.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4=
|
||||||
github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y=
|
|
||||||
github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo=
|
|
||||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 h1:rpfIENRNNilwHwZeG5+P150SMrnNEcHYvcCuK6dPZSg=
|
|
||||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0=
|
|
||||||
github.com/denisenkom/go-mssqldb v0.0.0-20191128021309-1d7a30a10f73/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
|
github.com/denisenkom/go-mssqldb v0.0.0-20191128021309-1d7a30a10f73/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
|
||||||
github.com/dgraph-io/badger v1.6.2 h1:mNw0qs90GVgGGWylh0umH5iag1j6n/PeJtNvL6KY/x8=
|
github.com/dgraph-io/badger v1.6.2 h1:mNw0qs90GVgGGWylh0umH5iag1j6n/PeJtNvL6KY/x8=
|
||||||
github.com/dgraph-io/badger v1.6.2/go.mod h1:JW2yswe3V058sS0kZ2h/AXeDSqFjxnZcRrVH//y2UQE=
|
github.com/dgraph-io/badger v1.6.2/go.mod h1:JW2yswe3V058sS0kZ2h/AXeDSqFjxnZcRrVH//y2UQE=
|
||||||
github.com/dgraph-io/badger/v2 v2.2007.4 h1:TRWBQg8UrlUhaFdco01nO2uXwzKS7zd+HVdwV/GHc4o=
|
github.com/dgraph-io/badger/v2 v2.2007.4 h1:TRWBQg8UrlUhaFdco01nO2uXwzKS7zd+HVdwV/GHc4o=
|
||||||
github.com/dgraph-io/badger/v2 v2.2007.4/go.mod h1:vSw/ax2qojzbN6eXHIx6KPKtCSHJN/Uz0X0VPruTIhk=
|
github.com/dgraph-io/badger/v2 v2.2007.4/go.mod h1:vSw/ax2qojzbN6eXHIx6KPKtCSHJN/Uz0X0VPruTIhk=
|
||||||
github.com/dgraph-io/badger/v3 v3.2103.5 h1:ylPa6qzbjYRQMU6jokoj4wzcaweHylt//CH0AKt0akg=
|
|
||||||
github.com/dgraph-io/badger/v3 v3.2103.5/go.mod h1:4MPiseMeDQ3FNCYwRbbcBOGJLf5jsE0PPFzRiKjtcdw=
|
|
||||||
github.com/dgraph-io/ristretto v0.0.2/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E=
|
github.com/dgraph-io/ristretto v0.0.2/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E=
|
||||||
github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E=
|
github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E=
|
||||||
github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8=
|
github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8=
|
||||||
@@ -345,8 +309,6 @@ github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+m
|
|||||||
github.com/dvsekhvalnov/jose2go v0.0.0-20170216131308-f21a8cedbbae/go.mod h1:7BvyPhdbLxMXIYTFPLsyJRFMsKmOZnQmzh6Gb+uquuM=
|
github.com/dvsekhvalnov/jose2go v0.0.0-20170216131308-f21a8cedbbae/go.mod h1:7BvyPhdbLxMXIYTFPLsyJRFMsKmOZnQmzh6Gb+uquuM=
|
||||||
github.com/eiannone/keyboard v0.0.0-20220611211555-0d226195f203 h1:XBBHcIb256gUJtLmY22n99HaZTz+r2Z51xUPi01m3wg=
|
github.com/eiannone/keyboard v0.0.0-20220611211555-0d226195f203 h1:XBBHcIb256gUJtLmY22n99HaZTz+r2Z51xUPi01m3wg=
|
||||||
github.com/eiannone/keyboard v0.0.0-20220611211555-0d226195f203/go.mod h1:E1jcSv8FaEny+OP/5k9UxZVw9YFWGj7eI4KR/iOBqCg=
|
github.com/eiannone/keyboard v0.0.0-20220611211555-0d226195f203/go.mod h1:E1jcSv8FaEny+OP/5k9UxZVw9YFWGj7eI4KR/iOBqCg=
|
||||||
github.com/elastic/gosigar v0.14.3 h1:xwkKwPia+hSfg9GqrCUKYdId102m9qTJIIr7egmK/uo=
|
|
||||||
github.com/elastic/gosigar v0.14.3/go.mod h1:iXRIGg2tLnu7LBdpqzyQfGDEidKCfWcCMS0WKyPWoMs=
|
|
||||||
github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
|
github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
|
||||||
github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
@@ -354,16 +316,8 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m
|
|||||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||||
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/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0=
|
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0=
|
||||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
|
||||||
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
|
|
||||||
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
|
|
||||||
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/flynn/noise v1.1.0 h1:KjPQoQCEFdZDiP03phOvGi11+SVVhBG2wOWAorLsstg=
|
|
||||||
github.com/flynn/noise v1.1.0/go.mod h1:xbMo+0i6+IGbYdJhF31t2eR1BIU0CYc12+BNAKwUTag=
|
|
||||||
github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk=
|
|
||||||
github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY=
|
|
||||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||||
github.com/fsnotify/fsevents v0.2.0 h1:BRlvlqjvNTfogHfeBOFvSC9N0Ddy+wzQCQukyoD7o/c=
|
github.com/fsnotify/fsevents v0.2.0 h1:BRlvlqjvNTfogHfeBOFvSC9N0Ddy+wzQCQukyoD7o/c=
|
||||||
@@ -379,7 +333,6 @@ github.com/go-jose/go-jose/v3 v3.0.3 h1:fFKWeig/irsp7XD2zBxvnmA/XaRWp5V3CBsZXJF7
|
|||||||
github.com/go-jose/go-jose/v3 v3.0.3/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ=
|
github.com/go-jose/go-jose/v3 v3.0.3/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ=
|
||||||
github.com/go-kit/kit v0.4.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
github.com/go-kit/kit v0.4.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
|
||||||
github.com/go-kit/kit v0.13.0 h1:OoneCcHKHQ03LfBpoQCUfCluwd2Vt3ohz+kvbJneZAU=
|
github.com/go-kit/kit v0.13.0 h1:OoneCcHKHQ03LfBpoQCUfCluwd2Vt3ohz+kvbJneZAU=
|
||||||
github.com/go-kit/kit v0.13.0/go.mod h1:phqEHMMUbyrCFCTgH48JueqrM3md2HcAZ8N3XE4FKDg=
|
github.com/go-kit/kit v0.13.0/go.mod h1:phqEHMMUbyrCFCTgH48JueqrM3md2HcAZ8N3XE4FKDg=
|
||||||
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
|
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
|
||||||
@@ -413,7 +366,6 @@ github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1v
|
|||||||
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
||||||
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
|
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
|
||||||
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||||
github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0=
|
|
||||||
github.com/goccy/go-yaml v1.17.1 h1:LI34wktB2xEE3ONG/2Ar54+/HJVBriAGJ55PHls4YuY=
|
github.com/goccy/go-yaml v1.17.1 h1:LI34wktB2xEE3ONG/2Ar54+/HJVBriAGJ55PHls4YuY=
|
||||||
github.com/goccy/go-yaml v1.17.1/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
|
github.com/goccy/go-yaml v1.17.1/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
|
||||||
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
|
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
|
||||||
@@ -432,7 +384,6 @@ github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2V
|
|||||||
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=
|
||||||
github.com/golang/glog v1.2.5 h1:DrW6hGnjIhtvhOIiAKT6Psh/Kd/ldepEa81DKeiRJ5I=
|
github.com/golang/glog v1.2.5 h1:DrW6hGnjIhtvhOIiAKT6Psh/Kd/ldepEa81DKeiRJ5I=
|
||||||
github.com/golang/glog v1.2.5/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=
|
github.com/golang/glog v1.2.5/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=
|
||||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
|
||||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
|
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
|
||||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
@@ -453,7 +404,6 @@ github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6
|
|||||||
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||||
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
|
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
|
||||||
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
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 v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU=
|
github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU=
|
||||||
github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
|
github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
|
||||||
github.com/google/cel-go v0.20.1 h1:nDx9r8S3L4pE61eDdt8igGj8rf5kjYR3ILxWIpWNi84=
|
github.com/google/cel-go v0.20.1 h1:nDx9r8S3L4pE61eDdt8igGj8rf5kjYR3ILxWIpWNi84=
|
||||||
@@ -461,9 +411,6 @@ github.com/google/cel-go v0.20.1/go.mod h1:kWcIzTsPX0zmQ+H3TirHstLLf9ep5QTsZBN9u
|
|||||||
github.com/google/certificate-transparency-go v1.0.10-0.20180222191210-5ab67e519c93/go.mod h1:QeJfpSbVSfYc7RgB3gJFj9cbuQMMchQxrWXz8Ruopmg=
|
github.com/google/certificate-transparency-go v1.0.10-0.20180222191210-5ab67e519c93/go.mod h1:QeJfpSbVSfYc7RgB3gJFj9cbuQMMchQxrWXz8Ruopmg=
|
||||||
github.com/google/certificate-transparency-go v1.1.8-0.20240110162603-74a5dd331745 h1:heyoXNxkRT155x4jTAiSv5BVSVkueifPUm+Q8LUXMRo=
|
github.com/google/certificate-transparency-go v1.1.8-0.20240110162603-74a5dd331745 h1:heyoXNxkRT155x4jTAiSv5BVSVkueifPUm+Q8LUXMRo=
|
||||||
github.com/google/certificate-transparency-go v1.1.8-0.20240110162603-74a5dd331745/go.mod h1:zN0wUQgV9LjwLZeFHnrAbQi8hzMVvEWePyk+MhPOk7k=
|
github.com/google/certificate-transparency-go v1.1.8-0.20240110162603-74a5dd331745/go.mod h1:zN0wUQgV9LjwLZeFHnrAbQi8hzMVvEWePyk+MhPOk7k=
|
||||||
github.com/google/flatbuffers v1.12.1/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
|
|
||||||
github.com/google/flatbuffers v24.3.25+incompatible h1:CX395cjN9Kke9mmalRoL3d81AtFUxJM+yDthflgJGkI=
|
|
||||||
github.com/google/flatbuffers v24.3.25+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
|
|
||||||
github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
|
github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
|
||||||
github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
|
github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
|
||||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||||
@@ -472,7 +419,6 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
|
|||||||
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.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
|
||||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
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 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||||
@@ -488,8 +434,6 @@ github.com/google/go-tspi v0.3.0/go.mod h1:xfMGI3G0PhxCdNVcYr1C4C+EizojDg/TXuX5b
|
|||||||
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/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||||
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8=
|
|
||||||
github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=
|
|
||||||
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8=
|
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8=
|
||||||
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
|
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
|
||||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||||
@@ -505,9 +449,6 @@ github.com/googleapis/enterprise-certificate-proxy v0.3.4 h1:XYIDZApgAnrN1c855gT
|
|||||||
github.com/googleapis/enterprise-certificate-proxy v0.3.4/go.mod h1:YKe7cfqYXjKGpGvmSg28/fFvhNzinZQm8DGnaburhGA=
|
github.com/googleapis/enterprise-certificate-proxy v0.3.4/go.mod h1:YKe7cfqYXjKGpGvmSg28/fFvhNzinZQm8DGnaburhGA=
|
||||||
github.com/googleapis/gax-go/v2 v2.13.0 h1:yitjD5f7jQHhyDsnhKEBU52NdvvdSeGzlAnDPT0hH1s=
|
github.com/googleapis/gax-go/v2 v2.13.0 h1:yitjD5f7jQHhyDsnhKEBU52NdvvdSeGzlAnDPT0hH1s=
|
||||||
github.com/googleapis/gax-go/v2 v2.13.0/go.mod h1:Z/fvTZXF8/uw7Xu5GuslPw+bplx6SS338j1Is2S+B7A=
|
github.com/googleapis/gax-go/v2 v2.13.0/go.mod h1:Z/fvTZXF8/uw7Xu5GuslPw+bplx6SS338j1Is2S+B7A=
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c h1:7lF+Vz0LqiRidnzC1Oq86fpX1q/iEv2KJdrCtttYjT4=
|
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
|
||||||
github.com/gorilla/handlers v1.5.2 h1:cLTUSsNkgcwhgRqvCNmdbRWG0A3N4F+M2nWKdScwyEE=
|
github.com/gorilla/handlers v1.5.2 h1:cLTUSsNkgcwhgRqvCNmdbRWG0A3N4F+M2nWKdScwyEE=
|
||||||
github.com/gorilla/handlers v1.5.2/go.mod h1:dX+xVpaxdSw+q0Qek8SSsl3dfMk3jNddUkMzo0GtH0w=
|
github.com/gorilla/handlers v1.5.2/go.mod h1:dX+xVpaxdSw+q0Qek8SSsl3dfMk3jNddUkMzo0GtH0w=
|
||||||
github.com/gorilla/mux v1.7.0/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
github.com/gorilla/mux v1.7.0/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||||
@@ -522,66 +463,26 @@ github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMW
|
|||||||
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=
|
||||||
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
||||||
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||||
github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
|
||||||
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
|
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
|
||||||
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
|
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
|
||||||
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
|
||||||
github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc=
|
|
||||||
github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
|
||||||
github.com/hashicorp/go-immutable-radix/v2 v2.1.0 h1:CUW5RYIcysz+D3B+l1mDeXrQ7fUvGGCwJfdASSzbrfo=
|
|
||||||
github.com/hashicorp/go-immutable-radix/v2 v2.1.0/go.mod h1:hgdqLXA4f6NIjRVisM1TJ9aOJVNRqKZj+xDGF6m7PBw=
|
|
||||||
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
|
||||||
github.com/hashicorp/go-msgpack v0.5.5 h1:i9R9JSrqIz0QVLz3sz+i3YJdT7TTSLcfLLzJi9aZTuI=
|
|
||||||
github.com/hashicorp/go-msgpack v0.5.5/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
|
||||||
github.com/hashicorp/go-msgpack/v2 v2.1.2 h1:4Ee8FTp834e+ewB71RDrQ0VKpyFdrKOjvYtnQ/ltVj0=
|
|
||||||
github.com/hashicorp/go-msgpack/v2 v2.1.2/go.mod h1:upybraOAblm4S7rx0+jeNy+CWWhzywQsSRV5033mMu4=
|
|
||||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
|
||||||
github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=
|
|
||||||
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
||||||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||||
github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
|
|
||||||
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
|
|
||||||
github.com/hashicorp/go-sockaddr v1.0.6 h1:RSG8rKU28VTUTvEKghe5gIhIQpv8evvNpnDEyqO4u9I=
|
|
||||||
github.com/hashicorp/go-sockaddr v1.0.6/go.mod h1:uoUUmtwU7n9Dv3O4SNLeFvg0SxQ3lyjsj6+CCykpaxI=
|
|
||||||
github.com/hashicorp/go-syslog v1.0.0 h1:KaodqZuhUoZereWVIYmpUgZysurB1kBLX2j0MwMrUAE=
|
|
||||||
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
|
|
||||||
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
|
||||||
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
|
||||||
github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
|
|
||||||
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
|
||||||
github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY=
|
github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY=
|
||||||
github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
|
||||||
github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c=
|
|
||||||
github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
|
|
||||||
github.com/hashicorp/golang-lru/arc/v2 v2.0.7 h1:QxkVTxwColcduO+LP7eJO56r2hFiG8zEbfAAzRv52KQ=
|
github.com/hashicorp/golang-lru/arc/v2 v2.0.7 h1:QxkVTxwColcduO+LP7eJO56r2hFiG8zEbfAAzRv52KQ=
|
||||||
github.com/hashicorp/golang-lru/arc/v2 v2.0.7/go.mod h1:Pe7gBlGdc8clY5LJ0LpJXMt5AmgmWNH1g+oFFVUHOEc=
|
github.com/hashicorp/golang-lru/arc/v2 v2.0.7/go.mod h1:Pe7gBlGdc8clY5LJ0LpJXMt5AmgmWNH1g+oFFVUHOEc=
|
||||||
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
|
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
|
||||||
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
|
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
|
||||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||||
github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y=
|
|
||||||
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
|
|
||||||
github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc=
|
|
||||||
github.com/hashicorp/mdns v1.0.5 h1:1M5hW1cunYeoXOqHwEb/GBDDHAFo0Yqb/uz/beC6LbE=
|
|
||||||
github.com/hashicorp/mdns v1.0.5/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc=
|
|
||||||
github.com/hashicorp/memberlist v0.5.0/go.mod h1:yvyXLpo0QaGE59Y7hDTsTzDD25JYBZ4mHgHUZ8lrOI0=
|
|
||||||
github.com/hashicorp/memberlist v0.5.1 h1:mk5dRuzeDNis2bi6LLoQIXfMH7JQvAzt3mQD0vNZZUo=
|
|
||||||
github.com/hashicorp/memberlist v0.5.1/go.mod h1:zGDXV6AqbDTKTM6yxW0I4+JtFzZAJVoIPvss4hV8F24=
|
|
||||||
github.com/hashicorp/serf v0.10.1 h1:Z1H2J60yRKvfDYAOZLd2MU0ND4AH/WDz7xYHDWQsIPY=
|
|
||||||
github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4=
|
|
||||||
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
|
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
|
||||||
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
|
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
|
||||||
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog=
|
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog=
|
||||||
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68=
|
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68=
|
||||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
|
||||||
github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
|
||||||
github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
||||||
github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI=
|
github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI=
|
||||||
github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
||||||
github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc=
|
|
||||||
github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8=
|
|
||||||
github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
||||||
github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4=
|
github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4=
|
||||||
github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
|
github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
|
||||||
@@ -592,46 +493,6 @@ github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2
|
|||||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||||
github.com/inhies/go-bytesize v0.0.0-20220417184213-4913239db9cf h1:FtEj8sfIcaaBfAKrE1Cwb61YDtYq9JxChK1c7AKce7s=
|
github.com/inhies/go-bytesize v0.0.0-20220417184213-4913239db9cf h1:FtEj8sfIcaaBfAKrE1Cwb61YDtYq9JxChK1c7AKce7s=
|
||||||
github.com/inhies/go-bytesize v0.0.0-20220417184213-4913239db9cf/go.mod h1:yrqSXGoD/4EKfF26AOGzscPOgTTJcyAwM2rpixWT+t4=
|
github.com/inhies/go-bytesize v0.0.0-20220417184213-4913239db9cf/go.mod h1:yrqSXGoD/4EKfF26AOGzscPOgTTJcyAwM2rpixWT+t4=
|
||||||
github.com/ipfs/bbloom v0.0.4 h1:Gi+8EGJ2y5qiD5FbsbpX/TMNcJw8gSqr7eyjHa4Fhvs=
|
|
||||||
github.com/ipfs/bbloom v0.0.4/go.mod h1:cS9YprKXpoZ9lT0n/Mw/a6/aFV6DTjTLYHeA+gyqMG0=
|
|
||||||
github.com/ipfs/boxo v0.21.0 h1:XpGXb+TQQ0IUdYaeAxGzWjSs6ow/Lce148A/2IbRDVE=
|
|
||||||
github.com/ipfs/boxo v0.21.0/go.mod h1:NmweAYeY1USOaJJxouy7DLr/Y5M8UBSsCI2KRivO+TY=
|
|
||||||
github.com/ipfs/go-block-format v0.2.0 h1:ZqrkxBA2ICbDRbK8KJs/u0O3dlp6gmAuuXUJNiW1Ycs=
|
|
||||||
github.com/ipfs/go-block-format v0.2.0/go.mod h1:+jpL11nFx5A/SPpsoBn6Bzkra/zaArfSmsknbPMYgzM=
|
|
||||||
github.com/ipfs/go-cid v0.4.1 h1:A/T3qGvxi4kpKWWcPC/PgbvDA2bjVLO7n4UeVwnbs/s=
|
|
||||||
github.com/ipfs/go-cid v0.4.1/go.mod h1:uQHwDeX4c6CtyrFwdqyhpNcxVewur1M7l7fNU7LKwZk=
|
|
||||||
github.com/ipfs/go-datastore v0.6.0 h1:JKyz+Gvz1QEZw0LsX1IBn+JFCJQH4SJVFtM4uWU0Myk=
|
|
||||||
github.com/ipfs/go-datastore v0.6.0/go.mod h1:rt5M3nNbSO/8q1t4LNkLyUwRs8HupMeN/8O4Vn9YAT8=
|
|
||||||
github.com/ipfs/go-detect-race v0.0.1 h1:qX/xay2W3E4Q1U7d9lNs1sU9nvguX0a7319XbyQ6cOk=
|
|
||||||
github.com/ipfs/go-detect-race v0.0.1/go.mod h1:8BNT7shDZPo99Q74BpGMK+4D8Mn4j46UU0LZ723meps=
|
|
||||||
github.com/ipfs/go-ds-badger v0.3.0 h1:xREL3V0EH9S219kFFueOYJJTcjgNSZ2HY1iSvN7U1Ro=
|
|
||||||
github.com/ipfs/go-ds-badger v0.3.0/go.mod h1:1ke6mXNqeV8K3y5Ak2bAA0osoTfmxUdupVCGm4QUIek=
|
|
||||||
github.com/ipfs/go-ds-badger3 v0.0.2 h1:+pME0YfRnbUKhvySnakNMuCMsUUhmGfwIsH/nnHZ7QY=
|
|
||||||
github.com/ipfs/go-ds-badger3 v0.0.2/go.mod h1:6/yjF1KaOU+IpCaqMV43yoWIdxHqOAJlO9EhWLnZSkI=
|
|
||||||
github.com/ipfs/go-ds-crdt v0.5.2 h1:3cTJ79/UocAoukkXr0OvpPHcH7v4YwN8xCb8F9pfngc=
|
|
||||||
github.com/ipfs/go-ds-crdt v0.5.2/go.mod h1:sJdQqdCp2Vwv1azk1sPl+Dxk780CQc11rf7B8aWmomc=
|
|
||||||
github.com/ipfs/go-ipfs-blocksutil v0.0.1 h1:Eh/H4pc1hsvhzsQoMEP3Bke/aW5P5rVM1IWFJMcGIPQ=
|
|
||||||
github.com/ipfs/go-ipfs-blocksutil v0.0.1/go.mod h1:Yq4M86uIOmxmGPUHv/uI7uKqZNtLb449gwKqXjIsnRk=
|
|
||||||
github.com/ipfs/go-ipfs-delay v0.0.1 h1:r/UXYyRcddO6thwOnhiznIAiSvxMECGgtv35Xs1IeRQ=
|
|
||||||
github.com/ipfs/go-ipfs-delay v0.0.1/go.mod h1:8SP1YXK1M1kXuc4KJZINY3TQQ03J2rwBG9QfXmbRPrw=
|
|
||||||
github.com/ipfs/go-ipfs-pq v0.0.3 h1:YpoHVJB+jzK15mr/xsWC574tyDLkezVrDNeaalQBsTE=
|
|
||||||
github.com/ipfs/go-ipfs-pq v0.0.3/go.mod h1:btNw5hsHBpRcSSgZtiNm/SLj5gYIZ18AKtv3kERkRb4=
|
|
||||||
github.com/ipfs/go-ipfs-util v0.0.3 h1:2RFdGez6bu2ZlZdI+rWfIdbQb1KudQp3VGwPtdNCmE0=
|
|
||||||
github.com/ipfs/go-ipfs-util v0.0.3/go.mod h1:LHzG1a0Ig4G+iZ26UUOMjHd+lfM84LZCrn17xAKWBvs=
|
|
||||||
github.com/ipfs/go-ipld-format v0.6.0 h1:VEJlA2kQ3LqFSIm5Vu6eIlSxD/Ze90xtc4Meten1F5U=
|
|
||||||
github.com/ipfs/go-ipld-format v0.6.0/go.mod h1:g4QVMTn3marU3qXchwjpKPKgJv+zF+OlaKMyhJ4LHPg=
|
|
||||||
github.com/ipfs/go-ipld-legacy v0.2.1 h1:mDFtrBpmU7b//LzLSypVrXsD8QxkEWxu5qVxN99/+tk=
|
|
||||||
github.com/ipfs/go-ipld-legacy v0.2.1/go.mod h1:782MOUghNzMO2DER0FlBR94mllfdCJCkTtDtPM51otM=
|
|
||||||
github.com/ipfs/go-log/v2 v2.5.1 h1:1XdUzF7048prq4aBjDQQ4SL5RxftpRGdXhNRwKSAlcY=
|
|
||||||
github.com/ipfs/go-log/v2 v2.5.1/go.mod h1:prSpmC1Gpllc9UYWxDiZDreBYw7zp4Iqp1kOLU9U5UI=
|
|
||||||
github.com/ipfs/go-metrics-interface v0.0.1 h1:j+cpbjYvu4R8zbleSs36gvB7jR+wsL2fGD6n0jO4kdg=
|
|
||||||
github.com/ipfs/go-metrics-interface v0.0.1/go.mod h1:6s6euYU4zowdslK0GKHmqaIZ3j/b/tL7HTWtJ4VPgWY=
|
|
||||||
github.com/ipfs/go-peertaskqueue v0.8.1 h1:YhxAs1+wxb5jk7RvS0LHdyiILpNmRIRnZVztekOF0pg=
|
|
||||||
github.com/ipfs/go-peertaskqueue v0.8.1/go.mod h1:Oxxd3eaK279FxeydSPPVGHzbwVeHjatZ2GA8XD+KbPU=
|
|
||||||
github.com/ipld/go-codec-dagpb v1.6.0 h1:9nYazfyu9B1p3NAgfVdpRco3Fs2nFC72DqVsMj6rOcc=
|
|
||||||
github.com/ipld/go-codec-dagpb v1.6.0/go.mod h1:ANzFhfP2uMJxRBr8CE+WQWs5UsNa0pYtmKZ+agnUw9s=
|
|
||||||
github.com/ipld/go-ipld-prime v0.21.0 h1:n4JmcpOlPDIxBcY037SVfpd1G+Sj1nKZah0m6QH9C2E=
|
|
||||||
github.com/ipld/go-ipld-prime v0.21.0/go.mod h1:3RLqy//ERg/y5oShXXdx5YIp50cFGOanyMctpPjsvxQ=
|
|
||||||
github.com/ishidawataru/sctp v0.0.0-20230406120618-7ff4192f6ff2 h1:i2fYnDurfLlJH8AyyMOnkLHnHeP8Ff/DDpuZA/D3bPo=
|
github.com/ishidawataru/sctp v0.0.0-20230406120618-7ff4192f6ff2 h1:i2fYnDurfLlJH8AyyMOnkLHnHeP8Ff/DDpuZA/D3bPo=
|
||||||
github.com/ishidawataru/sctp v0.0.0-20230406120618-7ff4192f6ff2/go.mod h1:co9pwDoBCm1kGxawmb4sPq0cSIOOWNPT4KnHotMP1Zg=
|
github.com/ishidawataru/sctp v0.0.0-20230406120618-7ff4192f6ff2/go.mod h1:co9pwDoBCm1kGxawmb4sPq0cSIOOWNPT4KnHotMP1Zg=
|
||||||
github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo=
|
github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo=
|
||||||
@@ -681,13 +542,6 @@ github.com/jackc/pgx/v4 v4.18.3/go.mod h1:Ey4Oru5tH5sB6tV7hDmfWFahwF15Eb7DNXlRKx
|
|||||||
github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||||
github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||||
github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||||
github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus=
|
|
||||||
github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc=
|
|
||||||
github.com/jbenet/go-cienv v0.1.0/go.mod h1:TqNnHUmJgXau0nCzC7kXWeotg3J9W34CUv5Djy1+FlA=
|
|
||||||
github.com/jbenet/go-temp-err-catcher v0.1.0 h1:zpb3ZH6wIE8Shj2sKS+khgRvf7T7RABoLk/+KKHggpk=
|
|
||||||
github.com/jbenet/go-temp-err-catcher v0.1.0/go.mod h1:0kJRvmDZXNMIiJirNPEYfhpPwbGVtZVWC34vc5WLsDk=
|
|
||||||
github.com/jbenet/goprocess v0.1.4 h1:DRGOFReOMqqDNXwW70QkacFW0YN9QnwLV0Vqk+3oU0o=
|
|
||||||
github.com/jbenet/goprocess v0.1.4/go.mod h1:5yspPrukOVuOLORacaBi858NqyClJPQxYZlqdZVfqY4=
|
|
||||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||||
github.com/jinzhu/gorm v0.0.0-20170222002820-5409931a1bb8 h1:CZkYfurY6KGhVtlalI4QwQ6T0Cu6iuY3e0x5RLu96WE=
|
github.com/jinzhu/gorm v0.0.0-20170222002820-5409931a1bb8 h1:CZkYfurY6KGhVtlalI4QwQ6T0Cu6iuY3e0x5RLu96WE=
|
||||||
github.com/jinzhu/gorm v0.0.0-20170222002820-5409931a1bb8/go.mod h1:Vla75njaFJ8clLU1W44h34PjIkijhjHIYnZxMqCdxqo=
|
github.com/jinzhu/gorm v0.0.0-20170222002820-5409931a1bb8/go.mod h1:Vla75njaFJ8clLU1W44h34PjIkijhjHIYnZxMqCdxqo=
|
||||||
@@ -704,11 +558,8 @@ github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtL
|
|||||||
github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
||||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||||
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
|
||||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||||
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
|
||||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
|
||||||
github.com/juju/loggo v0.0.0-20190526231331-6e530bcce5d8/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U=
|
github.com/juju/loggo v0.0.0-20190526231331-6e530bcce5d8/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U=
|
||||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
|
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
|
||||||
@@ -722,8 +573,6 @@ github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kK
|
|||||||
github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8=
|
github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
github.com/koron/go-ssdp v0.0.4 h1:1IDwrghSKYM7yLf7XCzbByg2sJ/JcNOZRXS2jczTwz0=
|
|
||||||
github.com/koron/go-ssdp v0.0.4/go.mod h1:oDXq+E5IL5q0U8uSBcoAXzTzInwy5lEgC91HoKtbmZk=
|
|
||||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||||
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=
|
||||||
@@ -750,32 +599,6 @@ github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
|||||||
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||||
github.com/libdns/libdns v0.2.2 h1:O6ws7bAfRPaBsgAYt8MDe2HcNBGC29hkZ9MX2eUSX3s=
|
github.com/libdns/libdns v0.2.2 h1:O6ws7bAfRPaBsgAYt8MDe2HcNBGC29hkZ9MX2eUSX3s=
|
||||||
github.com/libdns/libdns v0.2.2/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ=
|
github.com/libdns/libdns v0.2.2/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ=
|
||||||
github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8=
|
|
||||||
github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg=
|
|
||||||
github.com/libp2p/go-flow-metrics v0.1.0 h1:0iPhMI8PskQwzh57jB9WxIuIOQ0r+15PChFGkx3Q3WM=
|
|
||||||
github.com/libp2p/go-flow-metrics v0.1.0/go.mod h1:4Xi8MX8wj5aWNDAZttg6UPmc0ZrnFNsMtpsYUClFtro=
|
|
||||||
github.com/libp2p/go-libp2p v0.35.4 h1:FDiBUYLkueFwsuNJUZaxKRdpKvBOWU64qQPL768bSeg=
|
|
||||||
github.com/libp2p/go-libp2p v0.35.4/go.mod h1:RKCDNt30IkFipGL0tl8wQW/3zVWEGFUZo8g2gAKxwjU=
|
|
||||||
github.com/libp2p/go-libp2p-asn-util v0.4.1 h1:xqL7++IKD9TBFMgnLPZR6/6iYhawHKHl950SO9L6n94=
|
|
||||||
github.com/libp2p/go-libp2p-asn-util v0.4.1/go.mod h1:d/NI6XZ9qxw67b4e+NgpQexCIiFYJjErASrYW4PFDN8=
|
|
||||||
github.com/libp2p/go-libp2p-pubsub v0.11.0 h1:+JvS8Kty0OiyUiN0i8H5JbaCgjnJTRnTHe4rU88dLFc=
|
|
||||||
github.com/libp2p/go-libp2p-pubsub v0.11.0/go.mod h1:QEb+hEV9WL9wCiUAnpY29FZR6W3zK8qYlaml8R4q6gQ=
|
|
||||||
github.com/libp2p/go-libp2p-record v0.2.0 h1:oiNUOCWno2BFuxt3my4i1frNrt7PerzB3queqa1NkQ0=
|
|
||||||
github.com/libp2p/go-libp2p-record v0.2.0/go.mod h1:I+3zMkvvg5m2OcSdoL0KPljyJyvNDFGKX7QdlpYUcwk=
|
|
||||||
github.com/libp2p/go-libp2p-testing v0.12.0 h1:EPvBb4kKMWO29qP4mZGyhVzUyR25dvfUIK5WDu6iPUA=
|
|
||||||
github.com/libp2p/go-libp2p-testing v0.12.0/go.mod h1:KcGDRXyN7sQCllucn1cOOS+Dmm7ujhfEyXQL5lvkcPg=
|
|
||||||
github.com/libp2p/go-msgio v0.3.0 h1:mf3Z8B1xcFN314sWX+2vOTShIE0Mmn2TXn3YCUQGNj0=
|
|
||||||
github.com/libp2p/go-msgio v0.3.0/go.mod h1:nyRM819GmVaF9LX3l03RMh10QdOroF++NBbxAb0mmDM=
|
|
||||||
github.com/libp2p/go-nat v0.2.0 h1:Tyz+bUFAYqGyJ/ppPPymMGbIgNRH+WqC5QrT5fKrrGk=
|
|
||||||
github.com/libp2p/go-nat v0.2.0/go.mod h1:3MJr+GRpRkyT65EpVPBstXLvOlAPzUVlG6Pwg9ohLJk=
|
|
||||||
github.com/libp2p/go-netroute v0.2.1 h1:V8kVrpD8GK0Riv15/7VN6RbUQ3URNZVosw7H2v9tksU=
|
|
||||||
github.com/libp2p/go-netroute v0.2.1/go.mod h1:hraioZr0fhBjG0ZRXJJ6Zj2IVEVNx6tDTFQfSmcq7mQ=
|
|
||||||
github.com/libp2p/go-reuseport v0.4.0 h1:nR5KU7hD0WxXCJbmw7r2rhRYruNRl2koHw8fQscQm2s=
|
|
||||||
github.com/libp2p/go-reuseport v0.4.0/go.mod h1:ZtI03j/wO5hZVDFo2jKywN6bYKWLOy8Se6DrI2E1cLU=
|
|
||||||
github.com/libp2p/go-yamux/v4 v4.0.1 h1:FfDR4S1wj6Bw2Pqbc8Uz7pCxeRBPbwsBbEdfwiCypkQ=
|
|
||||||
github.com/libp2p/go-yamux/v4 v4.0.1/go.mod h1:NWjl8ZTLOGlozrXSOZ/HlfG++39iKNnM5wwmtQP1YB4=
|
|
||||||
github.com/lmittmann/tint v1.0.5 h1:NQclAutOfYsqs2F1Lenue6OoWCajs5wJcP3DfWVpePw=
|
|
||||||
github.com/lmittmann/tint v1.0.5/go.mod h1:HIS3gSy7qNwGCj+5oRjAutErFBl4BzdQP6cJZ0NfMwE=
|
|
||||||
github.com/lucasb-eyer/go-colorful v1.3.0 h1:2/yBRLdWBZKrf7gB40FoiKfAWYQ0lqNcbuQwVHXptag=
|
github.com/lucasb-eyer/go-colorful v1.3.0 h1:2/yBRLdWBZKrf7gB40FoiKfAWYQ0lqNcbuQwVHXptag=
|
||||||
github.com/lucasb-eyer/go-colorful v1.3.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
github.com/lucasb-eyer/go-colorful v1.3.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||||
github.com/magiconair/properties v1.5.3/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
github.com/magiconair/properties v1.5.3/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||||
@@ -786,22 +609,15 @@ github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0
|
|||||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||||
github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYthEiA=
|
github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYthEiA=
|
||||||
github.com/manifoldco/promptui v0.9.0/go.mod h1:ka04sppxSGFAtxX0qhlYQjISsg9mR4GWtQEhdbn6Pgg=
|
github.com/manifoldco/promptui v0.9.0/go.mod h1:ka04sppxSGFAtxX0qhlYQjISsg9mR4GWtQEhdbn6Pgg=
|
||||||
github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd h1:br0buuQ854V8u83wA0rVZ8ttrq5CpaPZdvrK0LP2lOk=
|
|
||||||
github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd/go.mod h1:QuCEs1Nt24+FYQEqAAncTDPJIuGs+LxK1MCiFL25pMU=
|
|
||||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
|
||||||
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.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
|
||||||
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
|
||||||
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.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
github.com/mattn/go-isatty v0.0.7/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.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
|
|
||||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
|
||||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
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=
|
||||||
@@ -824,8 +640,6 @@ github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQ
|
|||||||
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/acmez/v2 v2.0.3 h1:CgDBlEwg3QBp6s45tPQmFIBrkRIkBT4rW4orMM6p4sw=
|
github.com/mholt/acmez/v2 v2.0.3 h1:CgDBlEwg3QBp6s45tPQmFIBrkRIkBT4rW4orMM6p4sw=
|
||||||
github.com/mholt/acmez/v2 v2.0.3/go.mod h1:pQ1ysaDeGrIMvJ9dfJMk5kJNkn7L2sb3UhyrX6Q91cw=
|
github.com/mholt/acmez/v2 v2.0.3/go.mod h1:pQ1ysaDeGrIMvJ9dfJMk5kJNkn7L2sb3UhyrX6Q91cw=
|
||||||
github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
|
|
||||||
github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI=
|
|
||||||
github.com/miekg/dns v1.1.65 h1:0+tIPHzUW0GCge7IiK3guGP57VAw7hoPDfApjkMD1Fc=
|
github.com/miekg/dns v1.1.65 h1:0+tIPHzUW0GCge7IiK3guGP57VAw7hoPDfApjkMD1Fc=
|
||||||
github.com/miekg/dns v1.1.65/go.mod h1:Dzw9769uoKVaLuODMDZz9M6ynFU6Em65csPuoi8G0ck=
|
github.com/miekg/dns v1.1.65/go.mod h1:Dzw9769uoKVaLuODMDZz9M6ynFU6Em65csPuoi8G0ck=
|
||||||
github.com/miekg/pkcs11 v1.0.2/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
|
github.com/miekg/pkcs11 v1.0.2/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
|
||||||
@@ -833,17 +647,6 @@ github.com/miekg/pkcs11 v1.1.1 h1:Ugu9pdy6vAYku5DEpVWVFPYnzV+bxB+iRdbuFSu7TvU=
|
|||||||
github.com/miekg/pkcs11 v1.1.1/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
|
github.com/miekg/pkcs11 v1.1.1/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
|
||||||
github.com/mikioh/ipaddr v0.0.0-20190404000644-d465c8ab6721 h1:RlZweED6sbSArvlE924+mUcZuXKLBHA35U7LN621Bws=
|
github.com/mikioh/ipaddr v0.0.0-20190404000644-d465c8ab6721 h1:RlZweED6sbSArvlE924+mUcZuXKLBHA35U7LN621Bws=
|
||||||
github.com/mikioh/ipaddr v0.0.0-20190404000644-d465c8ab6721/go.mod h1:Ickgr2WtCLZ2MDGd4Gr0geeCH5HybhRJbonOgQpvSxc=
|
github.com/mikioh/ipaddr v0.0.0-20190404000644-d465c8ab6721/go.mod h1:Ickgr2WtCLZ2MDGd4Gr0geeCH5HybhRJbonOgQpvSxc=
|
||||||
github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b h1:z78hV3sbSMAUoyUMM0I83AUIT6Hu17AWfgjzIbtrYFc=
|
|
||||||
github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b/go.mod h1:lxPUiZwKoFL8DUUmalo2yJJUCxbPKtm8OKfqr2/FTNU=
|
|
||||||
github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc h1:PTfri+PuQmWDqERdnNMiD9ZejrlswWrCpBEZgWOiTrc=
|
|
||||||
github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc/go.mod h1:cGKTAVKx4SxOuR/czcZ/E2RSJ3sfHs8FpHhQ5CWMf9s=
|
|
||||||
github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ=
|
|
||||||
github.com/minio/sha256-simd v0.1.1-0.20190913151208-6de447530771/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM=
|
|
||||||
github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM=
|
|
||||||
github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8=
|
|
||||||
github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI=
|
|
||||||
github.com/mitchellh/cli v1.1.5 h1:OxRIeJXpAMztws/XHlN2vu6imG5Dpq+j61AzAX5fLng=
|
|
||||||
github.com/mitchellh/cli v1.1.5/go.mod h1:v8+iFts2sPIKUV1ltktPXMCC8fumSKFItNcD2cLtRR4=
|
|
||||||
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
|
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
|
||||||
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
|
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
|
||||||
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
|
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
|
||||||
@@ -854,7 +657,6 @@ github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN
|
|||||||
github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4=
|
github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4=
|
||||||
github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE=
|
github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE=
|
||||||
github.com/mitchellh/mapstructure v0.0.0-20150613213606-2caf8efc9366/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
github.com/mitchellh/mapstructure v0.0.0-20150613213606-2caf8efc9366/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||||
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
|
||||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||||
@@ -900,33 +702,8 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G
|
|||||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||||
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
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/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
|
|
||||||
github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o=
|
|
||||||
github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
|
|
||||||
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
|
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
|
||||||
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
|
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
|
||||||
github.com/multiformats/go-base32 v0.1.0 h1:pVx9xoSPqEIQG8o+UbAe7DNi51oej1NtK+aGkbLYxPE=
|
|
||||||
github.com/multiformats/go-base32 v0.1.0/go.mod h1:Kj3tFY6zNr+ABYMqeUNeGvkIC/UYgtWibDcT0rExnbI=
|
|
||||||
github.com/multiformats/go-base36 v0.2.0 h1:lFsAbNOGeKtuKozrtBsAkSVhv1p9D0/qedU9rQyccr0=
|
|
||||||
github.com/multiformats/go-base36 v0.2.0/go.mod h1:qvnKE++v+2MWCfePClUEjE78Z7P2a1UV0xHgWc0hkp4=
|
|
||||||
github.com/multiformats/go-multiaddr v0.1.1/go.mod h1:aMKBKNEYmzmDmxfX88/vz+J5IU55txyt0p4aiWVohjo=
|
|
||||||
github.com/multiformats/go-multiaddr v0.13.0 h1:BCBzs61E3AGHcYYTv8dqRH43ZfyrqM8RXVPT8t13tLQ=
|
|
||||||
github.com/multiformats/go-multiaddr v0.13.0/go.mod h1:sBXrNzucqkFJhvKOiwwLyqamGa/P5EIXNPLovyhQCII=
|
|
||||||
github.com/multiformats/go-multiaddr-dns v0.3.1 h1:QgQgR+LQVt3NPTjbrLLpsaT2ufAA2y0Mkk+QRVJbW3A=
|
|
||||||
github.com/multiformats/go-multiaddr-dns v0.3.1/go.mod h1:G/245BRQ6FJGmryJCrOuTdB37AMA5AMOVuO6NY3JwTk=
|
|
||||||
github.com/multiformats/go-multiaddr-fmt v0.1.0 h1:WLEFClPycPkp4fnIzoFoV9FVd49/eQsuaL3/CWe167E=
|
|
||||||
github.com/multiformats/go-multiaddr-fmt v0.1.0/go.mod h1:hGtDIW4PU4BqJ50gW2quDuPVjyWNZxToGUh/HwTZYJo=
|
|
||||||
github.com/multiformats/go-multibase v0.2.0 h1:isdYCVLvksgWlMW9OZRYJEa9pZETFivncJHmHnnd87g=
|
|
||||||
github.com/multiformats/go-multibase v0.2.0/go.mod h1:bFBZX4lKCA/2lyOFSAoKH5SS6oPyjtnzK/XTFDPkNuk=
|
|
||||||
github.com/multiformats/go-multicodec v0.9.0 h1:pb/dlPnzee/Sxv/j4PmkDRxCOi3hXTz3IbPKOXWJkmg=
|
|
||||||
github.com/multiformats/go-multicodec v0.9.0/go.mod h1:L3QTQvMIaVBkXOXXtVmYE+LI16i14xuaojr/H7Ai54k=
|
|
||||||
github.com/multiformats/go-multihash v0.0.8/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew=
|
|
||||||
github.com/multiformats/go-multihash v0.2.3 h1:7Lyc8XfX/IY2jWb/gI7JP+o7JEq9hOa7BFvVU9RSh+U=
|
|
||||||
github.com/multiformats/go-multihash v0.2.3/go.mod h1:dXgKXCXjBzdscBLk9JkjINiEsCKRVch90MdaGiKsvSM=
|
|
||||||
github.com/multiformats/go-multistream v0.5.0 h1:5htLSLl7lvJk3xx3qT/8Zm9J4K8vEOf/QGkvOGQAyiE=
|
|
||||||
github.com/multiformats/go-multistream v0.5.0/go.mod h1:n6tMZiwiP2wUsR8DgfDWw1dydlEqV3l6N3/GBsX6ILA=
|
|
||||||
github.com/multiformats/go-varint v0.0.7 h1:sWSGR+f/eu5ABZA2ZpYKBILXTTs9JWpdEM/nEGOHFS8=
|
|
||||||
github.com/multiformats/go-varint v0.0.7/go.mod h1:r8PUYw/fD/SjBCiKOoDlGF6QawOELpZAu9eioSos/OU=
|
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||||
@@ -956,49 +733,12 @@ github.com/opencontainers/selinux v1.12.0/go.mod h1:BTPX+bjVbWGXw7ZZWUbdENt8w0ht
|
|||||||
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||||
github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs=
|
github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs=
|
||||||
github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
|
github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
|
||||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
|
||||||
github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=
|
|
||||||
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
|
||||||
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0=
|
|
||||||
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y=
|
|
||||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||||
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
|
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
|
||||||
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
||||||
github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI=
|
github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI=
|
||||||
github.com/peterbourgon/diskv/v3 v3.0.1 h1:x06SQA46+PKIUftmEujdwSEpIx8kR+M9eLYsUxeYveU=
|
github.com/peterbourgon/diskv/v3 v3.0.1 h1:x06SQA46+PKIUftmEujdwSEpIx8kR+M9eLYsUxeYveU=
|
||||||
github.com/peterbourgon/diskv/v3 v3.0.1/go.mod h1:kJ5Ny7vLdARGU3WUuy6uzO6T0nb/2gWcT1JiBvRmb5o=
|
github.com/peterbourgon/diskv/v3 v3.0.1/go.mod h1:kJ5Ny7vLdARGU3WUuy6uzO6T0nb/2gWcT1JiBvRmb5o=
|
||||||
github.com/pion/datachannel v1.5.6 h1:1IxKJntfSlYkpUj8LlYRSWpYiTTC02nUrOE8T3DqGeg=
|
|
||||||
github.com/pion/datachannel v1.5.6/go.mod h1:1eKT6Q85pRnr2mHiWHxJwO50SfZRtWHTsNIVb/NfGW4=
|
|
||||||
github.com/pion/dtls/v2 v2.2.11 h1:9U/dpCYl1ySttROPWJgqWKEylUdT0fXp/xst6JwY5Ks=
|
|
||||||
github.com/pion/dtls/v2 v2.2.11/go.mod h1:d9SYc9fch0CqK90mRk1dC7AkzzpwJj6u2GU3u+9pqFE=
|
|
||||||
github.com/pion/ice/v2 v2.3.25 h1:M5rJA07dqhi3nobJIg+uPtcVjFECTrhcR3n0ns8kDZs=
|
|
||||||
github.com/pion/ice/v2 v2.3.25/go.mod h1:KXJJcZK7E8WzrBEYnV4UtqEZsGeWfHxsNqhVcVvgjxw=
|
|
||||||
github.com/pion/interceptor v0.1.29 h1:39fsnlP1U8gw2JzOFWdfCU82vHvhW9o0rZnZF56wF+M=
|
|
||||||
github.com/pion/interceptor v0.1.29/go.mod h1:ri+LGNjRUc5xUNtDEPzfdkmSqISixVTBF/z/Zms/6T4=
|
|
||||||
github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY=
|
|
||||||
github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms=
|
|
||||||
github.com/pion/mdns v0.0.12 h1:CiMYlY+O0azojWDmxdNr7ADGrnZ+V6Ilfner+6mSVK8=
|
|
||||||
github.com/pion/mdns v0.0.12/go.mod h1:VExJjv8to/6Wqm1FXK+Ii/Z9tsVk/F5sD/N70cnYFbk=
|
|
||||||
github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA=
|
|
||||||
github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8=
|
|
||||||
github.com/pion/rtcp v1.2.14 h1:KCkGV3vJ+4DAJmvP0vaQShsb0xkRfWkO540Gy102KyE=
|
|
||||||
github.com/pion/rtcp v1.2.14/go.mod h1:sn6qjxvnwyAkkPzPULIbVqSKI5Dv54Rv7VG0kNxh9L4=
|
|
||||||
github.com/pion/rtp v1.8.6 h1:MTmn/b0aWWsAzux2AmP8WGllusBVw4NPYPVFFd7jUPw=
|
|
||||||
github.com/pion/rtp v1.8.6/go.mod h1:pBGHaFt/yW7bf1jjWAoUjpSNoDnw98KTMg+jWWvziqU=
|
|
||||||
github.com/pion/sctp v1.8.16 h1:PKrMs+o9EMLRvFfXq59WFsC+V8mN1wnKzqrv+3D/gYY=
|
|
||||||
github.com/pion/sctp v1.8.16/go.mod h1:P6PbDVA++OJMrVNg2AL3XtYHV4uD6dvfyOovCgMs0PE=
|
|
||||||
github.com/pion/sdp/v3 v3.0.9 h1:pX++dCHoHUwq43kuwf3PyJfHlwIj4hXA7Vrifiq0IJY=
|
|
||||||
github.com/pion/sdp/v3 v3.0.9/go.mod h1:B5xmvENq5IXJimIO4zfp6LAe1fD9N+kFv+V/1lOdz8M=
|
|
||||||
github.com/pion/srtp/v2 v2.0.18 h1:vKpAXfawO9RtTRKZJbG4y0v1b11NZxQnxRl85kGuUlo=
|
|
||||||
github.com/pion/srtp/v2 v2.0.18/go.mod h1:0KJQjA99A6/a0DOVTu1PhDSw0CXF2jTkqOoMg3ODqdA=
|
|
||||||
github.com/pion/stun v0.6.1 h1:8lp6YejULeHBF8NmV8e2787BogQhduZugh5PdhDyyN4=
|
|
||||||
github.com/pion/stun v0.6.1/go.mod h1:/hO7APkX4hZKu/D0f2lHzNyvdkTGtIy3NDmLR7kSz/8=
|
|
||||||
github.com/pion/transport/v2 v2.2.5 h1:iyi25i/21gQck4hfRhomF6SktmUQjRsRW4WJdhfc3Kc=
|
|
||||||
github.com/pion/transport/v2 v2.2.5/go.mod h1:q2U/tf9FEfnSBGSW6w5Qp5PFWRLRj3NjLhCCgpRK4p0=
|
|
||||||
github.com/pion/turn/v2 v2.1.6 h1:Xr2niVsiPTB0FPtt+yAWKFUkU1eotQbGgpTIld4x1Gc=
|
|
||||||
github.com/pion/turn/v2 v2.1.6/go.mod h1:huEpByKKHix2/b9kmTAM3YoX6MKP+/D//0ClgUYR2fY=
|
|
||||||
github.com/pion/webrtc/v3 v3.2.42 h1:WN/ZuMjtpQOoGRCZUg/zFG+JHEvYLVyDKOxU6H1qWlE=
|
|
||||||
github.com/pion/webrtc/v3 v3.2.42/go.mod h1:M1RAe3TNTD1tzyvqHrbVODfwdPGSXOUo/OgpoGGJqFY=
|
|
||||||
github.com/pires/go-proxyproto v0.7.0 h1:IukmRewDQFWC7kfnb66CSomk2q/seBuilHBYFwyq0Hs=
|
github.com/pires/go-proxyproto v0.7.0 h1:IukmRewDQFWC7kfnb66CSomk2q/seBuilHBYFwyq0Hs=
|
||||||
github.com/pires/go-proxyproto v0.7.0/go.mod h1:Vz/1JPY/OACxWGQNIRY2BeyDmpoaWmEP40O9LbuiFR4=
|
github.com/pires/go-proxyproto v0.7.0/go.mod h1:Vz/1JPY/OACxWGQNIRY2BeyDmpoaWmEP40O9LbuiFR4=
|
||||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
@@ -1010,38 +750,29 @@ github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1
|
|||||||
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/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/polydawn/refmt v0.89.0 h1:ADJTApkvkeBZsN0tBTx8QjpD9JkmxbKp0cxfr9qszm4=
|
|
||||||
github.com/polydawn/refmt v0.89.0/go.mod h1:/zvteZs/GwLtCgZ4BL6CBsk9IKIlexP43ObX9AxTqTw=
|
|
||||||
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
|
||||||
github.com/posener/complete v1.2.3 h1:NP0eAhjcjImqslEwo/1hq7gpajME0fTLTezBKDqfXqo=
|
|
||||||
github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s=
|
|
||||||
github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
|
github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
|
||||||
github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=
|
github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=
|
||||||
github.com/prometheus/client_golang v0.9.0-pre1.0.20180209125602-c332b6f63c06/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
github.com/prometheus/client_golang v0.9.0-pre1.0.20180209125602-c332b6f63c06/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
||||||
github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g=
|
github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g=
|
||||||
github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
|
|
||||||
github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q=
|
github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q=
|
||||||
github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0=
|
github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0=
|
||||||
github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
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=
|
||||||
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
|
||||||
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
|
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
|
||||||
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
|
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
|
||||||
github.com/prometheus/common v0.0.0-20180110214958-89604d197083/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
github.com/prometheus/common v0.0.0-20180110214958-89604d197083/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||||
github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc=
|
github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc=
|
||||||
github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
|
|
||||||
github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
|
github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
|
||||||
github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
|
github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
|
||||||
github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||||
github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
|
github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
|
||||||
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
|
|
||||||
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
|
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
|
||||||
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
|
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
|
||||||
github.com/psviderski/unregistry v0.4.1 h1:KedQTjyj8hHbzygK9uw5YS10amMZ5cjSYHqSjUUBhoE=
|
github.com/psviderski/unregistry v0.4.1 h1:KedQTjyj8hHbzygK9uw5YS10amMZ5cjSYHqSjUUBhoE=
|
||||||
@@ -1050,10 +781,6 @@ github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
|
|||||||
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
|
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
|
||||||
github.com/quic-go/quic-go v0.48.2 h1:wsKXZPeGWpMpCGSWqOcqpW2wZYic/8T3aqiOID0/KWE=
|
github.com/quic-go/quic-go v0.48.2 h1:wsKXZPeGWpMpCGSWqOcqpW2wZYic/8T3aqiOID0/KWE=
|
||||||
github.com/quic-go/quic-go v0.48.2/go.mod h1:yBgs3rWBOADpga7F+jJsb6Ybg1LSYiQvwWlLX+/6HMs=
|
github.com/quic-go/quic-go v0.48.2/go.mod h1:yBgs3rWBOADpga7F+jJsb6Ybg1LSYiQvwWlLX+/6HMs=
|
||||||
github.com/quic-go/webtransport-go v0.8.0 h1:HxSrwun11U+LlmwpgM1kEqIqH90IT4N8auv/cD7QFJg=
|
|
||||||
github.com/quic-go/webtransport-go v0.8.0/go.mod h1:N99tjprW432Ut5ONql/aUhSLT0YVSlwHohQsuac9WaM=
|
|
||||||
github.com/raulk/go-watchdog v1.3.0 h1:oUmdlHxdkXRJlwfG0O9omj8ukerm8MEQavSiDTEtBsk=
|
|
||||||
github.com/raulk/go-watchdog v1.3.0/go.mod h1:fIvOnLbF0b0ZwkB9YU4mOW9Did//4vPZtDqv66NfsMU=
|
|
||||||
github.com/redis/go-redis/extra/rediscmd/v9 v9.0.5 h1:EaDatTxkdHG+U3Bk4EUr+DZ7fOGwTfezUiUJMaIcaho=
|
github.com/redis/go-redis/extra/rediscmd/v9 v9.0.5 h1:EaDatTxkdHG+U3Bk4EUr+DZ7fOGwTfezUiUJMaIcaho=
|
||||||
github.com/redis/go-redis/extra/rediscmd/v9 v9.0.5/go.mod h1:fyalQWdtzDBECAQFBJuQe5bzQ02jGd5Qcbgb97Flm7U=
|
github.com/redis/go-redis/extra/rediscmd/v9 v9.0.5/go.mod h1:fyalQWdtzDBECAQFBJuQe5bzQ02jGd5Qcbgb97Flm7U=
|
||||||
github.com/redis/go-redis/extra/redisotel/v9 v9.0.5 h1:EfpWLLCyXw8PSM2/XNJLjI3Pb27yVE+gIAfeqp8LUCc=
|
github.com/redis/go-redis/extra/redisotel/v9 v9.0.5 h1:EfpWLLCyXw8PSM2/XNJLjI3Pb27yVE+gIAfeqp8LUCc=
|
||||||
@@ -1074,17 +801,13 @@ github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
|||||||
github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
|
github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
|
||||||
github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
|
github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
|
||||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
|
||||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
|
||||||
github.com/santhosh-tekuri/jsonschema/v6 v6.0.1 h1:PKK9DyHxif4LZo+uQSgXNqs0jj5+xZwwfKHgph2lxBw=
|
github.com/santhosh-tekuri/jsonschema/v6 v6.0.1 h1:PKK9DyHxif4LZo+uQSgXNqs0jj5+xZwwfKHgph2lxBw=
|
||||||
github.com/santhosh-tekuri/jsonschema/v6 v6.0.1/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU=
|
github.com/santhosh-tekuri/jsonschema/v6 v6.0.1/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU=
|
||||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||||
github.com/schollz/jsonstore v1.1.0 h1:WZBDjgezFS34CHI+myb4s8GGpir3UMpy7vWoCeO0n6E=
|
github.com/schollz/jsonstore v1.1.0 h1:WZBDjgezFS34CHI+myb4s8GGpir3UMpy7vWoCeO0n6E=
|
||||||
github.com/schollz/jsonstore v1.1.0/go.mod h1:15c6+9guw8vDRyozGjN3FoILt0wpruJk9Pi66vjaZfg=
|
github.com/schollz/jsonstore v1.1.0/go.mod h1:15c6+9guw8vDRyozGjN3FoILt0wpruJk9Pi66vjaZfg=
|
||||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I=
|
|
||||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
|
||||||
github.com/secure-systems-lab/go-securesystemslib v0.6.0 h1:T65atpAVCJQK14UA57LMdZGpHi4QYSH/9FZyNGqMYIA=
|
github.com/secure-systems-lab/go-securesystemslib v0.6.0 h1:T65atpAVCJQK14UA57LMdZGpHi4QYSH/9FZyNGqMYIA=
|
||||||
github.com/secure-systems-lab/go-securesystemslib v0.6.0/go.mod h1:8Mtpo9JKks/qhPG4HGZ2LGMvrPbzuxwfz/f/zLfEWkk=
|
github.com/secure-systems-lab/go-securesystemslib v0.6.0/go.mod h1:8Mtpo9JKks/qhPG4HGZ2LGMvrPbzuxwfz/f/zLfEWkk=
|
||||||
github.com/serialx/hashring v0.0.0-20200727003509-22c0c7ab6b1b h1:h+3JX2VoWTFuyQEo87pStk/a99dzIO1mM9KxIyLPGTU=
|
github.com/serialx/hashring v0.0.0-20200727003509-22c0c7ab6b1b h1:h+3JX2VoWTFuyQEo87pStk/a99dzIO1mM9KxIyLPGTU=
|
||||||
@@ -1097,12 +820,6 @@ github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp
|
|||||||
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
|
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
|
||||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
|
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
|
||||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||||
github.com/siderolabs/discovery-api v0.1.4 h1:2fMEFSMiWaD1zDiBDY5md8VxItvL1rDQRSOfeXNjYKc=
|
|
||||||
github.com/siderolabs/discovery-api v0.1.4/go.mod h1:kaBy+G42v2xd/uAF/NIe383sjNTBE2AhxPTyi9SZI0s=
|
|
||||||
github.com/siderolabs/discovery-client v0.1.9 h1:yDzvts++Nf/2qczdDUfU5GAibkEIgz/eo9RPG/k/rOc=
|
|
||||||
github.com/siderolabs/discovery-client v0.1.9/go.mod h1:Ew1z07eyJwqNwum84IKYH4S649KEKK5WUmRW49HlXS8=
|
|
||||||
github.com/siderolabs/gen v0.4.8 h1:VNpbmDLhkXp7qcSEkKk1Ee7vU2afs3xvHrWLGR2UuiY=
|
|
||||||
github.com/siderolabs/gen v0.4.8/go.mod h1:7ROKVHHB68R3Amrd4a1ZXz/oMrXWF3Mg3lSEgnkJY5c=
|
|
||||||
github.com/siderolabs/grpc-proxy v0.5.1 h1:WTZYLMPTZPt43BzEJ02LT9kYA9qAfquWwCezc6NPPYE=
|
github.com/siderolabs/grpc-proxy v0.5.1 h1:WTZYLMPTZPt43BzEJ02LT9kYA9qAfquWwCezc6NPPYE=
|
||||||
github.com/siderolabs/grpc-proxy v0.5.1/go.mod h1:EQwE87LiWxhiIUPBeWmpjJb9DIWxWID8R6ARtdTC+8A=
|
github.com/siderolabs/grpc-proxy v0.5.1/go.mod h1:EQwE87LiWxhiIUPBeWmpjJb9DIWxWID8R6ARtdTC+8A=
|
||||||
github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
|
github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
|
||||||
@@ -1131,10 +848,6 @@ github.com/smallstep/scep v0.0.0-20231024192529-aee96d7ad34d h1:06LUHn4Ia2X6syjI
|
|||||||
github.com/smallstep/scep v0.0.0-20231024192529-aee96d7ad34d/go.mod h1:4d0ub42ut1mMtvGyMensjuHYEUpRrASvkzLEJvoRQcU=
|
github.com/smallstep/scep v0.0.0-20231024192529-aee96d7ad34d/go.mod h1:4d0ub42ut1mMtvGyMensjuHYEUpRrASvkzLEJvoRQcU=
|
||||||
github.com/smallstep/truststore v0.13.0 h1:90if9htAOblavbMeWlqNLnO9bsjjgVv2hQeQJCi/py4=
|
github.com/smallstep/truststore v0.13.0 h1:90if9htAOblavbMeWlqNLnO9bsjjgVv2hQeQJCi/py4=
|
||||||
github.com/smallstep/truststore v0.13.0/go.mod h1:3tmMp2aLKZ/OA/jnFUB0cYPcho402UG2knuJoPh4j7A=
|
github.com/smallstep/truststore v0.13.0/go.mod h1:3tmMp2aLKZ/OA/jnFUB0cYPcho402UG2knuJoPh4j7A=
|
||||||
github.com/smartystreets/assertions v1.2.0 h1:42S6lae5dvLc7BrLu/0ugRtcFVjoJNMC/N3yZFZkDFs=
|
|
||||||
github.com/smartystreets/assertions v1.2.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo=
|
|
||||||
github.com/smartystreets/goconvey v1.7.2 h1:9RBaZCeXEQ3UselpuwUQHltGVXvdwm6cv1hgR6gDIPg=
|
|
||||||
github.com/smartystreets/goconvey v1.7.2/go.mod h1:Vw0tHAZW6lzCRk3xgdin6fKYcG+G3Pg9vgXWeJpQFMM=
|
|
||||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||||
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
|
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
|
||||||
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||||
@@ -1200,9 +913,7 @@ github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea h1:SXhTLE6pb6eld/
|
|||||||
github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea/go.mod h1:WPnis/6cRcDZSUvVmezrxJPkiO87ThFYsoUiMwWNDJk=
|
github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea/go.mod h1:WPnis/6cRcDZSUvVmezrxJPkiO87ThFYsoUiMwWNDJk=
|
||||||
github.com/tonistiigi/vt100 v0.0.0-20240514184818-90bafcd6abab h1:H6aJ0yKQ0gF49Qb2z5hI1UHxSQt4JMyxebFR15KnApw=
|
github.com/tonistiigi/vt100 v0.0.0-20240514184818-90bafcd6abab h1:H6aJ0yKQ0gF49Qb2z5hI1UHxSQt4JMyxebFR15KnApw=
|
||||||
github.com/tonistiigi/vt100 v0.0.0-20240514184818-90bafcd6abab/go.mod h1:ulncasL3N9uLrVann0m+CDlJKWsIAP34MPcOJF6VRvc=
|
github.com/tonistiigi/vt100 v0.0.0-20240514184818-90bafcd6abab/go.mod h1:ulncasL3N9uLrVann0m+CDlJKWsIAP34MPcOJF6VRvc=
|
||||||
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
|
|
||||||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||||
github.com/urfave/cli v1.22.10/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
|
||||||
github.com/urfave/cli v1.22.17 h1:SYzXoiPfQjHBbkYxbew5prZHS1TOLT3ierW8SYLqtVQ=
|
github.com/urfave/cli v1.22.17 h1:SYzXoiPfQjHBbkYxbew5prZHS1TOLT3ierW8SYLqtVQ=
|
||||||
github.com/urfave/cli v1.22.17/go.mod h1:b0ht0aqgH/6pBYzzxURyrM4xXNgsoT/n2ZzwQiEhNVo=
|
github.com/urfave/cli v1.22.17/go.mod h1:b0ht0aqgH/6pBYzzxURyrM4xXNgsoT/n2ZzwQiEhNVo=
|
||||||
github.com/vbatts/tar-split v0.12.1 h1:CqKoORW7BUWBe7UL/iqTVvkTBOF8UvOMKOIZykxnnbo=
|
github.com/vbatts/tar-split v0.12.1 h1:CqKoORW7BUWBe7UL/iqTVvkTBOF8UvOMKOIZykxnnbo=
|
||||||
@@ -1211,10 +922,6 @@ github.com/vishvananda/netlink v1.3.1 h1:3AEMt62VKqz90r0tmNhog0r/PpWKmrEShJU0wJW
|
|||||||
github.com/vishvananda/netlink v1.3.1/go.mod h1:ARtKouGSTGchR8aMwmkzC0qiNPrrWO5JS/XMVl45+b4=
|
github.com/vishvananda/netlink v1.3.1/go.mod h1:ARtKouGSTGchR8aMwmkzC0qiNPrrWO5JS/XMVl45+b4=
|
||||||
github.com/vishvananda/netns v0.0.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zdEY=
|
github.com/vishvananda/netns v0.0.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zdEY=
|
||||||
github.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
|
github.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
|
||||||
github.com/warpfork/go-testmark v0.12.1 h1:rMgCpJfwy1sJ50x0M0NgyphxYYPMOODIJHhsXyEHU0s=
|
|
||||||
github.com/warpfork/go-testmark v0.12.1/go.mod h1:kHwy7wfvGSPh1rQJYKayD4AbtNaeyZdcGi9tNJTaa5Y=
|
|
||||||
github.com/warpfork/go-wish v0.0.0-20220906213052-39a1cc7a02d0 h1:GDDkbFiaK8jsSDJfjId/PEGEShv6ugrt4kYsC5UIDaQ=
|
|
||||||
github.com/warpfork/go-wish v0.0.0-20220906213052-39a1cc7a02d0/go.mod h1:x6AKhvSSexNrVSrViXSHUEbICjmGXhtgABaHIySUSGw=
|
|
||||||
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
||||||
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
|
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
|
||||||
github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc=
|
github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc=
|
||||||
@@ -1224,7 +931,6 @@ github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJu
|
|||||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||||
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.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
|
||||||
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=
|
||||||
github.com/zclconf/go-cty v1.17.0 h1:seZvECve6XX4tmnvRzWtJNHdscMtYEx5R7bnnVyd/d0=
|
github.com/zclconf/go-cty v1.17.0 h1:seZvECve6XX4tmnvRzWtJNHdscMtYEx5R7bnnVyd/d0=
|
||||||
github.com/zclconf/go-cty v1.17.0/go.mod h1:wqFzcImaLTI6A5HfsRwB0nj5n0MRZFwmey8YoFPPs3U=
|
github.com/zclconf/go-cty v1.17.0/go.mod h1:wqFzcImaLTI6A5HfsRwB0nj5n0MRZFwmey8YoFPPs3U=
|
||||||
@@ -1237,7 +943,6 @@ github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l
|
|||||||
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
|
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
|
||||||
go.etcd.io/bbolt v1.4.3 h1:dEadXpI6G79deX5prL3QRNP6JB8UxVkqo4UPnHaNXJo=
|
go.etcd.io/bbolt v1.4.3 h1:dEadXpI6G79deX5prL3QRNP6JB8UxVkqo4UPnHaNXJo=
|
||||||
go.etcd.io/bbolt v1.4.3/go.mod h1:tKQlpPaYCVFctUIgFKFnAlvbmB3tpy1vkTnDWohtc0E=
|
go.etcd.io/bbolt v1.4.3/go.mod h1:tKQlpPaYCVFctUIgFKFnAlvbmB3tpy1vkTnDWohtc0E=
|
||||||
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
|
|
||||||
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
|
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
|
||||||
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
||||||
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||||
@@ -1300,14 +1005,8 @@ go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
|||||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||||
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||||
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
|
||||||
go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8=
|
go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8=
|
||||||
go.uber.org/automaxprocs v1.5.3/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0=
|
go.uber.org/automaxprocs v1.5.3/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0=
|
||||||
go.uber.org/dig v1.17.1 h1:Tga8Lz8PcYNsWsyHMZ1Vm0OQOUaJNDyvPImgbAu9YSc=
|
|
||||||
go.uber.org/dig v1.17.1/go.mod h1:Us0rSJiThwCv2GteUN0Q7OKvU7n5J4dxZ9JKUXozFdE=
|
|
||||||
go.uber.org/fx v1.22.1 h1:nvvln7mwyT5s1q201YE29V/BFrGor6vMiDNpU/78Mys=
|
|
||||||
go.uber.org/fx v1.22.1/go.mod h1:HT2M7d7RHo+ebKGh9NRcrsrHHfpZ60nW3QRubMRfv48=
|
|
||||||
go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
|
|
||||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||||
go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y=
|
go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y=
|
||||||
@@ -1315,14 +1014,12 @@ go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU=
|
|||||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||||
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
|
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
|
||||||
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
|
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
|
||||||
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
|
||||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
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=
|
||||||
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
|
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
|
||||||
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||||
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
|
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
|
||||||
go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI=
|
|
||||||
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
|
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
|
||||||
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||||
go.uber.org/zap/exp v0.2.0 h1:FtGenNNeCATRB3CmB/yEUnjEFeJWpB/pMcy7e2bKPYs=
|
go.uber.org/zap/exp v0.2.0 h1:FtGenNNeCATRB3CmB/yEUnjEFeJWpB/pMcy7e2bKPYs=
|
||||||
@@ -1337,14 +1034,10 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
|
|||||||
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
||||||
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=
|
||||||
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
|
||||||
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
|
|
||||||
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-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
|
||||||
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-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
|
||||||
golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||||
golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||||
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
@@ -1370,7 +1063,6 @@ golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKG
|
|||||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||||
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.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
@@ -1388,13 +1080,10 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
|
|||||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
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-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
golang.org/x/net v0.0.0-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-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
|
||||||
golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8=
|
|
||||||
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.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
||||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||||
@@ -1410,11 +1099,9 @@ golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKl
|
|||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/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-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-20210220032951-036812b2e83c/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.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
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.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||||
@@ -1423,7 +1110,6 @@ 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.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||||
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/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-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/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=
|
||||||
@@ -1435,32 +1121,23 @@ golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5h
|
|||||||
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20210331175145-43e1dd70ce54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210331175145-43e1dd70ce54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/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-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-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
@@ -1509,19 +1186,16 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm
|
|||||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
|
||||||
golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||||
golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
|
||||||
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
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.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
|
||||||
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.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||||
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
||||||
@@ -1545,7 +1219,6 @@ google.golang.org/api v0.197.0/go.mod h1:AuOuo20GoQ331nq7DquGHlU6d+2wN2fZ8O0ta60
|
|||||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
|
||||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||||
google.golang.org/genproto v0.0.0-20240903143218-8af14fe29dc1 h1:BulPr26Jqjnd4eYDVe+YvyR7Yc2vJGkO5/0UxD0/jZU=
|
google.golang.org/genproto v0.0.0-20240903143218-8af14fe29dc1 h1:BulPr26Jqjnd4eYDVe+YvyR7Yc2vJGkO5/0UxD0/jZU=
|
||||||
@@ -1556,7 +1229,6 @@ google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a h1:
|
|||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
||||||
google.golang.org/grpc v1.0.5/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
google.golang.org/grpc v1.0.5/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||||
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.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||||
@@ -1603,13 +1275,11 @@ gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa
|
|||||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
||||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 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=
|
||||||
gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q=
|
gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q=
|
||||||
@@ -1633,8 +1303,6 @@ k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f h1:GA7//TjRY9yWGy1poLzYYJ
|
|||||||
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f/go.mod h1:R/HEjbvWI0qdfb8viZUeVZm0X6IZnxAydC7YU42CMw4=
|
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f/go.mod h1:R/HEjbvWI0qdfb8viZUeVZm0X6IZnxAydC7YU42CMw4=
|
||||||
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro=
|
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro=
|
||||||
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||||
lukechampine.com/blake3 v1.3.0 h1:sJ3XhFINmHSrYCgl958hscfIa3bw8x4DqMP3u1YvoYE=
|
|
||||||
lukechampine.com/blake3 v1.3.0/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k=
|
|
||||||
modernc.org/cc/v4 v4.24.4 h1:TFkx1s6dCkQpd6dKurBNmpo+G8Zl4Sq/ztJ+2+DEsh0=
|
modernc.org/cc/v4 v4.24.4 h1:TFkx1s6dCkQpd6dKurBNmpo+G8Zl4Sq/ztJ+2+DEsh0=
|
||||||
modernc.org/cc/v4 v4.24.4/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
|
modernc.org/cc/v4 v4.24.4/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
|
||||||
modernc.org/ccgo/v4 v4.23.16 h1:Z2N+kk38b7SfySC1ZkpGLN2vthNJP1+ZzGZIlH7uBxo=
|
modernc.org/ccgo/v4 v4.23.16 h1:Z2N+kk38b7SfySC1ZkpGLN2vthNJP1+ZzGZIlH7uBxo=
|
||||||
|
|||||||
+61
-50
@@ -263,8 +263,8 @@ func (cli *CLI) initRemoteMachine(ctx context.Context, opts InitClusterOptions)
|
|||||||
SSHKeyFile: opts.RemoteMachine.KeyPath,
|
SSHKeyFile: opts.RemoteMachine.KeyPath,
|
||||||
MachineID: resp.Machine.Id,
|
MachineID: resp.Machine.Id,
|
||||||
}
|
}
|
||||||
if opts.RemoteMachine.UseSSHCLI {
|
if opts.RemoteMachine.UseSSHGo {
|
||||||
connCfg.SSHCLI = config.NewSSHDestination(
|
connCfg.SSHGo = config.NewSSHDestination(
|
||||||
opts.RemoteMachine.User,
|
opts.RemoteMachine.User,
|
||||||
opts.RemoteMachine.Host,
|
opts.RemoteMachine.Host,
|
||||||
opts.RemoteMachine.Port,
|
opts.RemoteMachine.Port,
|
||||||
@@ -467,8 +467,8 @@ func (cli *CLI) AddMachine(ctx context.Context, opts AddMachineOptions) (*client
|
|||||||
SSHKeyFile: opts.RemoteMachine.KeyPath,
|
SSHKeyFile: opts.RemoteMachine.KeyPath,
|
||||||
MachineID: addResp.Machine.Id,
|
MachineID: addResp.Machine.Id,
|
||||||
}
|
}
|
||||||
if opts.RemoteMachine.UseSSHCLI {
|
if opts.RemoteMachine.UseSSHGo {
|
||||||
connCfg.SSHCLI = config.NewSSHDestination(
|
connCfg.SSHGo = config.NewSSHDestination(
|
||||||
opts.RemoteMachine.User,
|
opts.RemoteMachine.User,
|
||||||
opts.RemoteMachine.Host,
|
opts.RemoteMachine.Host,
|
||||||
opts.RemoteMachine.Port,
|
opts.RemoteMachine.Port,
|
||||||
@@ -498,73 +498,84 @@ func (cli *CLI) AddMachine(ctx context.Context, opts AddMachineOptions) (*client
|
|||||||
func provisionOrConnectRemoteMachine(
|
func provisionOrConnectRemoteMachine(
|
||||||
ctx context.Context, remoteMachine *RemoteMachine, skipInstall bool, version string,
|
ctx context.Context, remoteMachine *RemoteMachine, skipInstall bool, version string,
|
||||||
) (*client.Client, error) {
|
) (*client.Client, error) {
|
||||||
// Use SSH CLI
|
// Use Go's built-in SSH library.
|
||||||
if remoteMachine.UseSSHCLI {
|
if remoteMachine.UseSSHGo {
|
||||||
exec := sshexec.NewSSHCLIRemote(
|
sshClient, err := sshexec.Connect(
|
||||||
remoteMachine.User,
|
remoteMachine.User, remoteMachine.Host, remoteMachine.Port, remoteMachine.KeyPath,
|
||||||
remoteMachine.Host,
|
|
||||||
remoteMachine.Port,
|
|
||||||
remoteMachine.KeyPath,
|
|
||||||
)
|
)
|
||||||
|
// If the SSH connection using SSH agent fails and no key path is provided, try to use the default SSH key.
|
||||||
|
if err != nil && remoteMachine.KeyPath == "" {
|
||||||
|
remoteMachine.KeyPath = DefaultSSHKeyPath
|
||||||
|
sshClient, err = sshexec.Connect(
|
||||||
|
remoteMachine.User, remoteMachine.Host, remoteMachine.Port, remoteMachine.KeyPath,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf(
|
||||||
|
"SSH login to remote machine %s: %w",
|
||||||
|
config.NewSSHDestination(remoteMachine.User, remoteMachine.Host, remoteMachine.Port), err,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
if !skipInstall {
|
if !skipInstall {
|
||||||
if err := provisionMachine(ctx, exec, version); err != nil {
|
// Provision the remote machine by installing the Uncloud daemon and dependencies over SSH.
|
||||||
|
exec := sshexec.NewRemote(sshClient)
|
||||||
|
if err = provisionMachine(ctx, exec, version); err != nil {
|
||||||
return nil, fmt.Errorf("provision machine: %w", err)
|
return nil, fmt.Errorf("provision machine: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sshConfig := &connector.SSHConnectorConfig{
|
var machineClient *client.Client
|
||||||
User: remoteMachine.User,
|
if remoteMachine.User == "root" || skipInstall {
|
||||||
Host: remoteMachine.Host,
|
// Create a machine API client over the established SSH connection to the remote machine.
|
||||||
Port: remoteMachine.Port,
|
machineClient, err = client.New(ctx, connector.NewSSHConnectorFromClient(sshClient))
|
||||||
KeyPath: remoteMachine.KeyPath,
|
} else {
|
||||||
|
// Since the user is not root, we need to establish a new SSH connection to make the user's addition
|
||||||
|
// to the uncloud group effective, thus allowing access to the Uncloud daemon Unix socket.
|
||||||
|
sshConfig := &connector.SSHConnectorConfig{
|
||||||
|
User: remoteMachine.User,
|
||||||
|
Host: remoteMachine.Host,
|
||||||
|
Port: remoteMachine.Port,
|
||||||
|
KeyPath: remoteMachine.KeyPath,
|
||||||
|
}
|
||||||
|
machineClient, err = client.New(ctx, connector.NewSSHConnector(sshConfig))
|
||||||
}
|
}
|
||||||
machineClient, err := client.New(ctx, connector.NewSSHCLIConnector(sshConfig))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("connect to remote machine: %w", err)
|
return nil, fmt.Errorf("connect to remote machine: %w", err)
|
||||||
}
|
}
|
||||||
return machineClient, nil
|
return machineClient, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use Go SSH
|
// Use the system 'ssh' command (default).
|
||||||
sshClient, err := sshexec.Connect(remoteMachine.User, remoteMachine.Host, remoteMachine.Port, remoteMachine.KeyPath)
|
sshConfig := &connector.SSHConnectorConfig{
|
||||||
// If the SSH connection using SSH agent fails and no key path is provided, try to use the default SSH key.
|
User: remoteMachine.User,
|
||||||
if err != nil && remoteMachine.KeyPath == "" {
|
Host: remoteMachine.Host,
|
||||||
remoteMachine.KeyPath = DefaultSSHKeyPath
|
Port: remoteMachine.Port,
|
||||||
sshClient, err = sshexec.Connect(
|
KeyPath: remoteMachine.KeyPath,
|
||||||
remoteMachine.User, remoteMachine.Host, remoteMachine.Port, remoteMachine.KeyPath,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf(
|
|
||||||
"SSH login to remote machine %s: %w",
|
|
||||||
config.NewSSHDestination(remoteMachine.User, remoteMachine.Host, remoteMachine.Port), err,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
conn := connector.NewSSHCLIConnector(sshConfig)
|
||||||
|
|
||||||
if !skipInstall {
|
if !skipInstall {
|
||||||
// Provision the remote machine by installing the Uncloud daemon and dependencies over SSH.
|
exec := sshexec.NewSSHCLIRemote(
|
||||||
exec := sshexec.NewRemote(sshClient)
|
remoteMachine.User,
|
||||||
if err = provisionMachine(ctx, exec, version); err != nil {
|
remoteMachine.Host,
|
||||||
|
remoteMachine.Port,
|
||||||
|
remoteMachine.KeyPath,
|
||||||
|
)
|
||||||
|
if err := provisionMachine(ctx, exec, version); err != nil {
|
||||||
return nil, fmt.Errorf("provision machine: %w", err)
|
return nil, fmt.Errorf("provision machine: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if remoteMachine.User != rootUser {
|
||||||
|
// provisionMachine has just added the user to the uncloud group. Any SSH ControlMaster left over from
|
||||||
|
// a previous uc invocation (e.g. a failed uc command against the uninitialised machine) still holds
|
||||||
|
// the old user groups and would deny access to /run/uncloud/uncloud.sock. Close the current session
|
||||||
|
// if it exists so the next session picks up the new groups.
|
||||||
|
conn.CloseControlMaster(ctx)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var machineClient *client.Client
|
machineClient, err := client.New(ctx, conn)
|
||||||
if remoteMachine.User == "root" || skipInstall {
|
|
||||||
// Create a machine API client over the established SSH connection to the remote machine.
|
|
||||||
machineClient, err = client.New(ctx, connector.NewSSHConnectorFromClient(sshClient))
|
|
||||||
} else {
|
|
||||||
// Since the user is not root, we need to establish a new SSH connection to make the user's addition
|
|
||||||
// to the uncloud group effective, thus allowing access to the Uncloud daemon Unix socket.
|
|
||||||
sshConfig := &connector.SSHConnectorConfig{
|
|
||||||
User: remoteMachine.User,
|
|
||||||
Host: remoteMachine.Host,
|
|
||||||
Port: remoteMachine.Port,
|
|
||||||
KeyPath: remoteMachine.KeyPath,
|
|
||||||
}
|
|
||||||
machineClient, err = client.New(ctx, connector.NewSSHConnector(sshConfig))
|
|
||||||
}
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("connect to remote machine: %w", err)
|
return nil, fmt.Errorf("connect to remote machine: %w", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,8 +12,12 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type MachineConnection struct {
|
type MachineConnection struct {
|
||||||
SSH SSHDestination `yaml:"ssh,omitempty"`
|
// SSH uses the system ssh CLI command to connect. This is the default SSH connection method.
|
||||||
SSHCLI SSHDestination `yaml:"ssh_cli,omitempty"`
|
SSH SSHDestination `yaml:"ssh,omitempty"`
|
||||||
|
// SSHCLI is a backward-compatible alias for SSH.
|
||||||
|
SSHCLI SSHDestination `yaml:"ssh_cli,omitempty"`
|
||||||
|
// SSHGo uses Go's built-in SSH library to connect.
|
||||||
|
SSHGo SSHDestination `yaml:"ssh_go,omitempty"`
|
||||||
SSHKeyFile string `yaml:"ssh_key_file,omitempty"`
|
SSHKeyFile string `yaml:"ssh_key_file,omitempty"`
|
||||||
// TCP is the address and port of the machine's API server.
|
// TCP is the address and port of the machine's API server.
|
||||||
// The pointer is used to omit the field when not set. Otherwise, yaml marshalling includes an empty object.
|
// The pointer is used to omit the field when not set. Otherwise, yaml marshalling includes an empty object.
|
||||||
@@ -29,7 +33,9 @@ func (c *MachineConnection) String() string {
|
|||||||
if c.SSH != "" {
|
if c.SSH != "" {
|
||||||
return "ssh://" + string(c.SSH)
|
return "ssh://" + string(c.SSH)
|
||||||
} else if c.SSHCLI != "" {
|
} else if c.SSHCLI != "" {
|
||||||
return "ssh+cli://" + string(c.SSHCLI)
|
return "ssh://" + string(c.SSHCLI)
|
||||||
|
} else if c.SSHGo != "" {
|
||||||
|
return "ssh+go://" + string(c.SSHGo)
|
||||||
} else if c.TCP != nil && c.TCP.IsValid() {
|
} else if c.TCP != nil && c.TCP.IsValid() {
|
||||||
return fmt.Sprintf("tcp://%s", c.TCP)
|
return fmt.Sprintf("tcp://%s", c.TCP)
|
||||||
} else if c.Unix != "" {
|
} else if c.Unix != "" {
|
||||||
@@ -46,6 +52,9 @@ func (c *MachineConnection) Validate() error {
|
|||||||
if c.SSHCLI != "" {
|
if c.SSHCLI != "" {
|
||||||
setCount++
|
setCount++
|
||||||
}
|
}
|
||||||
|
if c.SSHGo != "" {
|
||||||
|
setCount++
|
||||||
|
}
|
||||||
if c.TCP != nil && c.TCP.IsValid() {
|
if c.TCP != nil && c.TCP.IsValid() {
|
||||||
setCount++
|
setCount++
|
||||||
}
|
}
|
||||||
@@ -54,10 +63,10 @@ func (c *MachineConnection) Validate() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if setCount == 0 {
|
if setCount == 0 {
|
||||||
return errors.New("no connection method specified (ssh, ssh_cli, tcp, or unix required)")
|
return errors.New("no connection method specified (ssh, ssh_go, tcp, or unix required)")
|
||||||
}
|
}
|
||||||
if setCount > 1 {
|
if setCount > 1 {
|
||||||
return errors.New("only one connection method allowed per connection (ssh, ssh_cli, tcp, or unix)")
|
return errors.New("only one connection method allowed per connection (ssh, ssh_go, tcp, or unix)")
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -30,18 +30,32 @@ func TestMachineConnection_String(t *testing.T) {
|
|||||||
want: "ssh://user@host.com:2222",
|
want: "ssh://user@host.com:2222",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "ssh_cli connection",
|
name: "ssh_cli connection (backward compat alias for ssh)",
|
||||||
conn: MachineConnection{
|
conn: MachineConnection{
|
||||||
SSHCLI: "user@host.com",
|
SSHCLI: "user@host.com",
|
||||||
},
|
},
|
||||||
want: "ssh+cli://user@host.com",
|
want: "ssh://user@host.com",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "ssh_cli connection with port",
|
name: "ssh_cli connection with port (backward compat alias for ssh)",
|
||||||
conn: MachineConnection{
|
conn: MachineConnection{
|
||||||
SSHCLI: "user@host.com:2222",
|
SSHCLI: "user@host.com:2222",
|
||||||
},
|
},
|
||||||
want: "ssh+cli://user@host.com:2222",
|
want: "ssh://user@host.com:2222",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ssh_go connection",
|
||||||
|
conn: MachineConnection{
|
||||||
|
SSHGo: "user@host.com",
|
||||||
|
},
|
||||||
|
want: "ssh+go://user@host.com",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ssh_go connection with port",
|
||||||
|
conn: MachineConnection{
|
||||||
|
SSHGo: "user@host.com:2222",
|
||||||
|
},
|
||||||
|
want: "ssh+go://user@host.com:2222",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "tcp connection",
|
name: "tcp connection",
|
||||||
@@ -103,12 +117,19 @@ func TestMachineConnection_Validate(t *testing.T) {
|
|||||||
wantErr: false,
|
wantErr: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "ssh_cli only - valid",
|
name: "ssh_cli only - valid (backward compat)",
|
||||||
conn: MachineConnection{
|
conn: MachineConnection{
|
||||||
SSHCLI: "user@host",
|
SSHCLI: "user@host",
|
||||||
},
|
},
|
||||||
wantErr: false,
|
wantErr: false,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "ssh_go only - valid",
|
||||||
|
conn: MachineConnection{
|
||||||
|
SSHGo: "user@host",
|
||||||
|
},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "tcp only - valid",
|
name: "tcp only - valid",
|
||||||
conn: MachineConnection{
|
conn: MachineConnection{
|
||||||
@@ -141,6 +162,15 @@ func TestMachineConnection_Validate(t *testing.T) {
|
|||||||
wantErr: true,
|
wantErr: true,
|
||||||
errMsg: "only one connection method allowed",
|
errMsg: "only one connection method allowed",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "ssh and ssh_go - error",
|
||||||
|
conn: MachineConnection{
|
||||||
|
SSH: "user@host",
|
||||||
|
SSHGo: "user@host",
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
errMsg: "only one connection method allowed",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "ssh and unix - error",
|
name: "ssh and unix - error",
|
||||||
conn: MachineConnection{
|
conn: MachineConnection{
|
||||||
|
|||||||
+12
-8
@@ -57,9 +57,9 @@ func connectClusterWithProgress(ctx context.Context, conn config.MachineConnecti
|
|||||||
}
|
}
|
||||||
|
|
||||||
func connectCluster(ctx context.Context, conn config.MachineConnection) (*client.Client, error) {
|
func connectCluster(ctx context.Context, conn config.MachineConnection) (*client.Client, error) {
|
||||||
// Determine which SSH type is configured
|
// Determine which SSH type is configured.
|
||||||
var sshDest config.SSHDestination
|
var sshDest config.SSHDestination
|
||||||
var useSSHCLI bool
|
var useGoSSH bool
|
||||||
|
|
||||||
// Validate connection configuration early to provide clear error messages.
|
// Validate connection configuration early to provide clear error messages.
|
||||||
if err := conn.Validate(); err != nil {
|
if err := conn.Validate(); err != nil {
|
||||||
@@ -67,11 +67,15 @@ func connectCluster(ctx context.Context, conn config.MachineConnection) (*client
|
|||||||
}
|
}
|
||||||
|
|
||||||
if conn.SSH != "" {
|
if conn.SSH != "" {
|
||||||
|
// SSH uses the system ssh CLI command (default).
|
||||||
sshDest = conn.SSH
|
sshDest = conn.SSH
|
||||||
useSSHCLI = false
|
|
||||||
} else if conn.SSHCLI != "" {
|
} else if conn.SSHCLI != "" {
|
||||||
|
// SSHCLI is a backward-compatible alias for SSH.
|
||||||
sshDest = conn.SSHCLI
|
sshDest = conn.SSHCLI
|
||||||
useSSHCLI = true
|
} else if conn.SSHGo != "" {
|
||||||
|
// SSHGo uses Go's built-in SSH library.
|
||||||
|
sshDest = conn.SSHGo
|
||||||
|
useGoSSH = true
|
||||||
} else if conn.TCP != nil && conn.TCP.IsValid() {
|
} else if conn.TCP != nil && conn.TCP.IsValid() {
|
||||||
return client.New(ctx, connector.NewTCPConnector(*conn.TCP))
|
return client.New(ctx, connector.NewTCPConnector(*conn.TCP))
|
||||||
} else if conn.Unix != "" {
|
} else if conn.Unix != "" {
|
||||||
@@ -95,11 +99,11 @@ func connectCluster(ctx context.Context, conn config.MachineConnection) (*client
|
|||||||
KeyPath: keyPath,
|
KeyPath: keyPath,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create appropriate connector based on type
|
// Create appropriate connector based on type.
|
||||||
if useSSHCLI {
|
if useGoSSH {
|
||||||
return client.New(ctx, connector.NewSSHCLIConnector(sshConfig))
|
return client.New(ctx, connector.NewSSHConnector(sshConfig))
|
||||||
}
|
}
|
||||||
return client.New(ctx, connector.NewSSHConnector(sshConfig))
|
return client.New(ctx, connector.NewSSHCLIConnector(sshConfig))
|
||||||
}
|
}
|
||||||
|
|
||||||
// connectModel is a TUI model for connecting to a cluster with a progress spinner.
|
// connectModel is a TUI model for connecting to a cluster with a progress spinner.
|
||||||
|
|||||||
@@ -0,0 +1,190 @@
|
|||||||
|
package logs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"image/color"
|
||||||
|
"os"
|
||||||
|
"slices"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"charm.land/lipgloss/v2"
|
||||||
|
"github.com/docker/docker/pkg/stringid"
|
||||||
|
"github.com/psviderski/uncloud/internal/cli/tui"
|
||||||
|
"github.com/psviderski/uncloud/pkg/api"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Formatter handles formatting and printing of log entries with dynamic column alignment.
|
||||||
|
type Formatter struct {
|
||||||
|
machineNames []string
|
||||||
|
serviceNames []string
|
||||||
|
|
||||||
|
maxMachineWidth int
|
||||||
|
maxServiceWidth int
|
||||||
|
|
||||||
|
utc bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewFormatter(machineNames, serviceNames []string, utc bool) *Formatter {
|
||||||
|
slices.Sort(machineNames)
|
||||||
|
slices.Sort(serviceNames)
|
||||||
|
|
||||||
|
maxMachineWidth := 0
|
||||||
|
for _, name := range machineNames {
|
||||||
|
if len(name) > maxMachineWidth {
|
||||||
|
maxMachineWidth = len(name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
maxServiceWidth := 0
|
||||||
|
for _, name := range serviceNames {
|
||||||
|
if len(name) > maxServiceWidth {
|
||||||
|
maxServiceWidth = len(name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Formatter{
|
||||||
|
machineNames: machineNames,
|
||||||
|
serviceNames: serviceNames,
|
||||||
|
maxMachineWidth: maxMachineWidth,
|
||||||
|
maxServiceWidth: maxServiceWidth,
|
||||||
|
utc: utc,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// formatTimestamp formats timestamp using local timezone or UTC if configured.
|
||||||
|
func (f *Formatter) formatTimestamp(t time.Time) string {
|
||||||
|
if f.utc {
|
||||||
|
t = t.UTC()
|
||||||
|
} else {
|
||||||
|
t = t.In(time.Local)
|
||||||
|
}
|
||||||
|
dimStyle := lipgloss.NewStyle().Faint(true)
|
||||||
|
|
||||||
|
return dimStyle.Render(t.Format(time.StampMilli))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Formatter) formatMachine(name string) string {
|
||||||
|
style := lipgloss.NewStyle().Bold(true).PaddingRight(f.maxMachineWidth - len(name))
|
||||||
|
|
||||||
|
if len(f.serviceNames) == 1 {
|
||||||
|
// Machine name is coloured for single-service logs.
|
||||||
|
i := slices.Index(f.machineNames, name)
|
||||||
|
if i == -1 {
|
||||||
|
f.machineNames = append(f.machineNames, name)
|
||||||
|
i = len(f.machineNames) - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
style = style.Foreground(palette[i%len(palette)])
|
||||||
|
}
|
||||||
|
|
||||||
|
return style.Render(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Formatter) formatService(serviceName, containerID, hook string) string {
|
||||||
|
styleService := lipgloss.NewStyle().Bold(true)
|
||||||
|
padding := f.maxServiceWidth - len(serviceName)
|
||||||
|
|
||||||
|
if len(f.serviceNames) > 1 {
|
||||||
|
// Service name is coloured for multi-service logs.
|
||||||
|
i := slices.Index(f.serviceNames, serviceName)
|
||||||
|
if i == -1 {
|
||||||
|
f.serviceNames = append(f.serviceNames, serviceName)
|
||||||
|
i = len(f.serviceNames) - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
styleService = styleService.Foreground(palette[i%len(palette)])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Journal logs are unit-scoped and have no container ID.
|
||||||
|
if containerID == "" {
|
||||||
|
return styleService.PaddingRight(padding).Render(serviceName)
|
||||||
|
}
|
||||||
|
|
||||||
|
out := styleService.Render(serviceName) + tui.Faint.PaddingRight(padding).Render("/"+containerID[:5])
|
||||||
|
if hook != "" {
|
||||||
|
out += tui.Faint.Render(" [" + hook + "]")
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrintEntry prints a single log entry with proper formatting.
|
||||||
|
func (f *Formatter) PrintEntry(entry api.ServiceLogEntry) {
|
||||||
|
if entry.Err != nil {
|
||||||
|
f.printError(entry)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if entry.Stream != api.LogStreamStdout && entry.Stream != api.LogStreamStderr {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var output strings.Builder
|
||||||
|
|
||||||
|
// Timestamp
|
||||||
|
output.WriteString(f.formatTimestamp(entry.Timestamp))
|
||||||
|
output.WriteString(" ")
|
||||||
|
|
||||||
|
// Machine name
|
||||||
|
output.WriteString(f.formatMachine(entry.Metadata.MachineName))
|
||||||
|
output.WriteString(" ")
|
||||||
|
|
||||||
|
// Service/container_id or service name for a systemd service.
|
||||||
|
output.WriteString(f.formatService(entry.Metadata.ServiceName, entry.Metadata.ContainerID, entry.Metadata.Hook))
|
||||||
|
output.WriteString(" ")
|
||||||
|
|
||||||
|
// Message
|
||||||
|
output.Write(entry.Message)
|
||||||
|
|
||||||
|
// Print to appropriate stream.
|
||||||
|
if entry.Stream == api.LogStreamStderr {
|
||||||
|
fmt.Fprint(os.Stderr, output.String())
|
||||||
|
} else {
|
||||||
|
fmt.Print(output.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// printError prints an error entry (e.g., stalled stream warning).
|
||||||
|
func (f *Formatter) printError(entry api.ServiceLogEntry) {
|
||||||
|
if entry.Metadata.ServiceName == "" {
|
||||||
|
msg := fmt.Sprintf("ERROR: %v", entry.Err)
|
||||||
|
style := lipgloss.NewStyle().Bold(true).Foreground(lipgloss.BrightRed)
|
||||||
|
fmt.Fprintln(os.Stderr, style.Render(msg))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var msg string
|
||||||
|
if entry.Metadata.ContainerID != "" {
|
||||||
|
msg = fmt.Sprintf("WARNING: log stream from container '%s/%s' on machine '%s'",
|
||||||
|
entry.Metadata.ServiceName,
|
||||||
|
stringid.TruncateID(entry.Metadata.ContainerID),
|
||||||
|
entry.Metadata.MachineName)
|
||||||
|
} else {
|
||||||
|
msg = fmt.Sprintf("WARNING: log stream from systemd service '%s' on machine '%s'",
|
||||||
|
entry.Metadata.ServiceName,
|
||||||
|
entry.Metadata.MachineName)
|
||||||
|
}
|
||||||
|
|
||||||
|
if errors.Is(entry.Err, api.ErrLogStreamStalled) {
|
||||||
|
msg += " stopped responding"
|
||||||
|
} else {
|
||||||
|
msg += fmt.Sprintf(": %v", entry.Err)
|
||||||
|
}
|
||||||
|
|
||||||
|
style := lipgloss.NewStyle().Bold(true).Foreground(lipgloss.BrightYellow)
|
||||||
|
fmt.Fprintln(os.Stderr, style.Render(msg))
|
||||||
|
}
|
||||||
|
|
||||||
|
// palette is available colors for machine/service differentiation.
|
||||||
|
var palette = []color.Color{
|
||||||
|
lipgloss.BrightGreen,
|
||||||
|
lipgloss.BrightYellow,
|
||||||
|
lipgloss.BrightBlue,
|
||||||
|
lipgloss.BrightMagenta,
|
||||||
|
lipgloss.BrightCyan,
|
||||||
|
lipgloss.Green,
|
||||||
|
lipgloss.Yellow,
|
||||||
|
lipgloss.Blue,
|
||||||
|
lipgloss.Magenta,
|
||||||
|
lipgloss.Cyan,
|
||||||
|
}
|
||||||
@@ -0,0 +1,116 @@
|
|||||||
|
package logs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/spf13/pflag"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Options describes how and what logs we are requesting.
|
||||||
|
type Options struct {
|
||||||
|
Files []string
|
||||||
|
Follow bool
|
||||||
|
Tail string
|
||||||
|
Since string
|
||||||
|
Until string
|
||||||
|
UTC bool
|
||||||
|
Machines []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func Flags(options *Options) *pflag.FlagSet {
|
||||||
|
set := &pflag.FlagSet{}
|
||||||
|
|
||||||
|
set.BoolVarP(&options.Follow, "follow", "f", false,
|
||||||
|
"Continually stream new logs.")
|
||||||
|
set.StringSliceVarP(&options.Machines, "machine", "m", nil,
|
||||||
|
"Filter logs by machine name or ID. Can be specified multiple times or as a comma-separated list.")
|
||||||
|
set.StringVar(&options.Since, "since", "",
|
||||||
|
"Show logs generated on or after the given timestamp. Accepts relative duration, RFC 3339 date, or Unix timestamp.\n"+
|
||||||
|
"Examples:\n"+
|
||||||
|
" --since 2m30s Relative duration (2 minutes 30 seconds ago)\n"+
|
||||||
|
" --since 1h Relative duration (1 hour ago)\n"+
|
||||||
|
" --since 2025-11-24 RFC 3339 date only (midnight using local timezone)\n"+
|
||||||
|
" --since 2024-05-14T22:50:00 RFC 3339 date/time using local timezone\n"+
|
||||||
|
" --since 2024-01-31T10:30:00Z RFC 3339 date/time in UTC\n"+
|
||||||
|
" --since 1763953966 Unix timestamp (seconds since January 1, 1970)")
|
||||||
|
set.StringVarP(&options.Tail, "tail", "n", "100",
|
||||||
|
"Show the most recent logs and limit the number of lines shown per replica. Use 'all' to show all logs.")
|
||||||
|
set.StringVar(&options.Until, "until", "",
|
||||||
|
"Show logs generated before the given timestamp. Accepts relative duration, RFC 3339 date, or Unix timestamp.\n"+
|
||||||
|
"See --since for examples.")
|
||||||
|
set.BoolVar(&options.UTC, "utc", false,
|
||||||
|
"Print timestamps in UTC instead of local timezone.")
|
||||||
|
|
||||||
|
return set
|
||||||
|
}
|
||||||
|
|
||||||
|
func Tail(tail string) (int, error) {
|
||||||
|
if tail == "all" {
|
||||||
|
return -1, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
tailInt, err := strconv.Atoi(tail)
|
||||||
|
if err != nil {
|
||||||
|
return 0, fmt.Errorf("invalid --tail value '%s': %w", tail, err)
|
||||||
|
}
|
||||||
|
return tailInt, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServiceArg pairs a service name with the list of container filters parsed from `uc service logs` arguments.
|
||||||
|
// An empty Containers slice means "stream logs from all containers of this service".
|
||||||
|
type ServiceArg struct {
|
||||||
|
Service string
|
||||||
|
Containers []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseServiceArgs groups raw `uc service logs` positional arguments into per-service entries,
|
||||||
|
// preserving first-seen order. Each argument is either a service name (e.g. "web") or a
|
||||||
|
// service/container reference (e.g. "web/61d57fd3428f").
|
||||||
|
// Arguments for the same service are merged: if any argument for a service lacks a container suffix, all containers
|
||||||
|
// of that service are streamed regardless of any other service/container arguments for it.
|
||||||
|
func ParseServiceArgs(args []string) ([]ServiceArg, error) {
|
||||||
|
indexByService := make(map[string]int, len(args))
|
||||||
|
allContainers := make(map[string]bool, len(args))
|
||||||
|
result := make([]ServiceArg, 0, len(args))
|
||||||
|
|
||||||
|
for _, arg := range args {
|
||||||
|
arg = strings.TrimSpace(arg)
|
||||||
|
if arg == "" {
|
||||||
|
return nil, fmt.Errorf("empty service argument")
|
||||||
|
}
|
||||||
|
|
||||||
|
service, container, hasSlash := strings.Cut(arg, "/")
|
||||||
|
if service == "" {
|
||||||
|
return nil, fmt.Errorf("invalid service argument '%s': service name is empty", arg)
|
||||||
|
}
|
||||||
|
if hasSlash && container == "" {
|
||||||
|
return nil, fmt.Errorf("invalid service argument '%s': container name or ID is empty", arg)
|
||||||
|
}
|
||||||
|
|
||||||
|
idx, seen := indexByService[service]
|
||||||
|
if !seen {
|
||||||
|
entry := ServiceArg{Service: service}
|
||||||
|
if hasSlash {
|
||||||
|
entry.Containers = []string{container}
|
||||||
|
} else {
|
||||||
|
allContainers[service] = true
|
||||||
|
}
|
||||||
|
result = append(result, entry)
|
||||||
|
indexByService[service] = len(result) - 1
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if !hasSlash {
|
||||||
|
result[idx].Containers = nil
|
||||||
|
allContainers[service] = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !allContainers[service] {
|
||||||
|
result[idx].Containers = append(result[idx].Containers, container)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
package logs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestParseServiceArgs(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args []string
|
||||||
|
want []ServiceArg
|
||||||
|
wantErr string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "empty input",
|
||||||
|
args: nil,
|
||||||
|
want: []ServiceArg{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "single bareword service",
|
||||||
|
args: []string{"web"},
|
||||||
|
want: []ServiceArg{{Service: "web"}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "single container",
|
||||||
|
args: []string{"web/abc123"},
|
||||||
|
want: []ServiceArg{{Service: "web", Containers: []string{"abc123"}}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "multiple containers same service",
|
||||||
|
args: []string{"web/abc123", "web/def456"},
|
||||||
|
want: []ServiceArg{{Service: "web", Containers: []string{"abc123", "def456"}}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "multiple services interleaved",
|
||||||
|
args: []string{"web/abc123", "api", " web/def456 ", "db/xyz789 "},
|
||||||
|
want: []ServiceArg{
|
||||||
|
{Service: "web", Containers: []string{"abc123", "def456"}},
|
||||||
|
{Service: "api"},
|
||||||
|
{Service: "db", Containers: []string{"xyz789"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "bareword wins when seen first",
|
||||||
|
args: []string{"web", "web/abc123"},
|
||||||
|
want: []ServiceArg{{Service: "web"}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "bareword wins when seen later",
|
||||||
|
args: []string{"web/abc123", "web/def456", "web"},
|
||||||
|
want: []ServiceArg{{Service: "web"}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "bareword wins followed by more container args",
|
||||||
|
args: []string{"web/abc123", "web", "web/def456"},
|
||||||
|
want: []ServiceArg{{Service: "web"}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "preserves first-seen service order",
|
||||||
|
args: []string{"db", "api", "web"},
|
||||||
|
want: []ServiceArg{
|
||||||
|
{Service: "db"},
|
||||||
|
{Service: "api"},
|
||||||
|
{Service: "web"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty arg",
|
||||||
|
args: []string{""},
|
||||||
|
wantErr: "empty service argument",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty spaces arg",
|
||||||
|
args: []string{" "},
|
||||||
|
wantErr: "empty service argument",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "missing service half",
|
||||||
|
args: []string{"/abc123"},
|
||||||
|
wantErr: "service name is empty",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "missing container half",
|
||||||
|
args: []string{"web/"},
|
||||||
|
wantErr: "container name or ID is empty",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
got, err := ParseServiceArgs(tt.args)
|
||||||
|
if tt.wantErr != "" {
|
||||||
|
require.Error(t, err)
|
||||||
|
assert.ErrorContains(t, err, tt.wantErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, tt.want, got)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
+39
-29
@@ -2,6 +2,7 @@ package cli
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/base64"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
@@ -13,43 +14,44 @@ import (
|
|||||||
"github.com/psviderski/uncloud/internal/cli/tui"
|
"github.com/psviderski/uncloud/internal/cli/tui"
|
||||||
"github.com/psviderski/uncloud/internal/machine/api/pb"
|
"github.com/psviderski/uncloud/internal/machine/api/pb"
|
||||||
"github.com/psviderski/uncloud/internal/sshexec"
|
"github.com/psviderski/uncloud/internal/sshexec"
|
||||||
|
"github.com/psviderski/uncloud/scripts"
|
||||||
"google.golang.org/protobuf/types/known/emptypb"
|
"google.golang.org/protobuf/types/known/emptypb"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const rootUser = "root"
|
||||||
// TODO: support pinning the script version to the CLI version.
|
|
||||||
installScriptURL = "https://raw.githubusercontent.com/psviderski/uncloud/refs/heads/main/scripts/install.sh"
|
|
||||||
rootUser = "root"
|
|
||||||
)
|
|
||||||
|
|
||||||
type RemoteMachine struct {
|
type RemoteMachine struct {
|
||||||
User string
|
User string
|
||||||
Host string
|
Host string
|
||||||
Port int
|
Port int
|
||||||
KeyPath string
|
KeyPath string
|
||||||
UseSSHCLI bool // indicates ssh+cli:// should be used
|
UseSSHGo bool // Use Go's built-in SSH library instead of the system ssh CLI command.
|
||||||
}
|
}
|
||||||
|
|
||||||
func installCmd(user string, version string) string {
|
// installCmd returns a shell command that decodes the base64-encoded install script and pipes it
|
||||||
|
// into bash, optionally via sudo and with UNCLOUD_* environment variables set.
|
||||||
|
func installCmd(scriptBase64, user, version string) string {
|
||||||
sudoPrefix := ""
|
sudoPrefix := ""
|
||||||
var env []string
|
var env []string
|
||||||
|
|
||||||
// Add the SSH user (non-root) to the uncloud group to allow access to the Uncloud daemon unix socket.
|
// Add the SSH user (non-root) to the uncloud group to allow access to the Uncloud daemon unix socket.
|
||||||
if user != rootUser {
|
if user != rootUser {
|
||||||
sudoPrefix = "sudo"
|
sudoPrefix = "sudo "
|
||||||
env = append(env, "UNCLOUD_GROUP_ADD_USER="+sshexec.Quote(user))
|
env = append(env, "UNCLOUD_GROUP_ADD_USER="+sshexec.Quote(user))
|
||||||
}
|
}
|
||||||
if version != "" {
|
if version != "" {
|
||||||
env = append(env, "UNCLOUD_VERSION="+sshexec.Quote(version))
|
env = append(env, "UNCLOUD_VERSION="+sshexec.Quote(version))
|
||||||
}
|
}
|
||||||
|
|
||||||
envCmd := strings.Join(env, " ")
|
envPrefix := ""
|
||||||
curlBashCmd := fmt.Sprintf("curl -fsSL %s | %s %s bash", sshexec.Quote(installScriptURL), sudoPrefix, envCmd)
|
if len(env) > 0 {
|
||||||
|
envPrefix = strings.Join(env, " ") + " "
|
||||||
|
}
|
||||||
|
|
||||||
return curlBashCmd
|
return fmt.Sprintf("printf '%%s' %s | base64 -d | %s%sbash", scriptBase64, sudoPrefix, envPrefix)
|
||||||
}
|
}
|
||||||
|
|
||||||
// provisionMachine provisions the remote machine by downloading the Uncloud install script from GitHub and running it.
|
// provisionMachine provisions the remote machine by running the Uncloud install script embedded in the uc CLI.
|
||||||
// If version is specified, it will be passed to the install script as UNCLOUD_VERSION environment variable.
|
// If version is specified, it will be passed to the install script as UNCLOUD_VERSION environment variable.
|
||||||
func provisionMachine(ctx context.Context, exec sshexec.Executor, version string) error {
|
func provisionMachine(ctx context.Context, exec sshexec.Executor, version string) error {
|
||||||
user, err := exec.Run(ctx, "whoami")
|
user, err := exec.Run(ctx, "whoami")
|
||||||
@@ -72,18 +74,26 @@ func provisionMachine(ctx context.Context, exec sshexec.Executor, version string
|
|||||||
" echo '%[1]s ALL=(ALL) NOPASSWD:ALL' | sudo tee /etc/sudoers.d/%[1]s",
|
" echo '%[1]s ALL=(ALL) NOPASSWD:ALL' | sudo tee /etc/sudoers.d/%[1]s",
|
||||||
user)
|
user)
|
||||||
}
|
}
|
||||||
|
if strings.Contains(err.Error(), "not in the sudoers file") {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"user '%[1]s' is not in the sudo group or sudoers file so cannot use sudo, but Uncloud needs "+
|
||||||
|
"passwordless sudo or root access to install and configure the uncloudd daemon on the remote "+
|
||||||
|
"machine.\n\n"+
|
||||||
|
"Possible solutions:\n"+
|
||||||
|
"1. Use root user or a user with passwordless sudo instead.\n"+
|
||||||
|
"2. Grant passwordless sudo to the user '%[1]s' by running on the remote machine as root:\n"+
|
||||||
|
" echo '%[1]s ALL=(ALL) NOPASSWD:ALL' > /etc/sudoers.d/%[1]s",
|
||||||
|
user)
|
||||||
|
}
|
||||||
return fmt.Errorf("sudo command failed for user '%s': %w. "+
|
return fmt.Errorf("sudo command failed for user '%s': %w. "+
|
||||||
"Please ensure the user has sudo privileges or use root user instead", user, err)
|
"Please ensure the user has sudo privileges or use root user instead", user, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd := installCmd(user, version)
|
scriptBase64 := base64.StdEncoding.EncodeToString([]byte(scripts.InstallScript))
|
||||||
|
cmd := sshexec.QuoteCommand("bash", "-c", "set -o pipefail; "+installCmd(scriptBase64, user, version))
|
||||||
fmt.Println("Downloading Uncloud install script:", installScriptURL)
|
|
||||||
|
|
||||||
cmd = sshexec.QuoteCommand("bash", "-c", "set -o pipefail; "+cmd)
|
|
||||||
if err = exec.Stream(ctx, cmd, os.Stdout, os.Stderr); err != nil {
|
if err = exec.Stream(ctx, cmd, os.Stdout, os.Stderr); err != nil {
|
||||||
return fmt.Errorf("download and run install script: %w", err)
|
return fmt.Errorf("run install script: %w", err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -95,21 +105,21 @@ func promptResetMachine() error {
|
|||||||
"use --yes flag or set UNCLOUD_AUTO_CONFIRM=true to auto-confirm")
|
"use --yes flag or set UNCLOUD_AUTO_CONFIRM=true to auto-confirm")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fmt.Println(tui.Red.Render("The remote machine is already initialised as a cluster member. Resetting it will:\n" +
|
||||||
|
"- Remove all service containers from the machine\n" +
|
||||||
|
"- Reset the machine to the uninitialised state"))
|
||||||
|
|
||||||
var confirm bool
|
var confirm bool
|
||||||
form := huh.NewForm(
|
form := huh.NewForm(
|
||||||
huh.NewGroup(
|
huh.NewGroup(
|
||||||
huh.NewConfirm().
|
huh.NewConfirm().
|
||||||
Title(
|
Title("Do you want to reset it first?").
|
||||||
"The remote machine is already initialised as a cluster member. Do you want to reset it first?\n" +
|
|
||||||
"This will:\n" +
|
|
||||||
"- Remove all service containers from the machine\n" +
|
|
||||||
"- Reset the machine to the uninitialised state",
|
|
||||||
).
|
|
||||||
Affirmative("Yes!").
|
Affirmative("Yes!").
|
||||||
Negative("No").
|
Negative("No").
|
||||||
Value(&confirm),
|
Value(&confirm),
|
||||||
),
|
),
|
||||||
).WithAccessible(true)
|
).WithTheme(tui.ThemeConfirmDanger()).
|
||||||
|
WithAccessible(true)
|
||||||
if err := form.Run(); err != nil {
|
if err := form.Run(); err != nil {
|
||||||
return fmt.Errorf("prompt user to confirm: %w", err)
|
return fmt.Errorf("prompt user to confirm: %w", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,30 +7,42 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestInstallCmd(t *testing.T) {
|
func TestInstallCmd(t *testing.T) {
|
||||||
t.Run("root", func(t *testing.T) {
|
const scriptB64 = "SCRIPT_BASE64_PLACEHOLDER"
|
||||||
cmd := installCmd("root", "")
|
|
||||||
assert.NotContains(t, cmd, "sudo")
|
|
||||||
assert.NotContains(t, cmd, "UNCLOUD_GROUP_ADD_USER")
|
|
||||||
})
|
|
||||||
|
|
||||||
// Test with version
|
tests := []struct {
|
||||||
t.Run("root with version", func(t *testing.T) {
|
name string
|
||||||
cmd := installCmd("root", "v1.2.3")
|
user string
|
||||||
assert.NotContains(t, cmd, "sudo")
|
version string
|
||||||
assert.NotContains(t, cmd, "UNCLOUD_GROUP_ADD_USER")
|
want string
|
||||||
assert.Contains(t, cmd, "UNCLOUD_VERSION=v1.2.3")
|
}{
|
||||||
})
|
{
|
||||||
|
name: "root",
|
||||||
|
user: "root",
|
||||||
|
want: "printf '%s' SCRIPT_BASE64_PLACEHOLDER | base64 -d | bash",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "root with version",
|
||||||
|
user: "root",
|
||||||
|
version: "v1.2.3",
|
||||||
|
want: "printf '%s' SCRIPT_BASE64_PLACEHOLDER | base64 -d | UNCLOUD_VERSION=v1.2.3 bash",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "nonroot",
|
||||||
|
user: "nonroot",
|
||||||
|
want: "printf '%s' SCRIPT_BASE64_PLACEHOLDER | base64 -d | sudo UNCLOUD_GROUP_ADD_USER=nonroot bash",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "nonroot with version",
|
||||||
|
user: "nonroot",
|
||||||
|
version: "v1.2.3",
|
||||||
|
want: "printf '%s' SCRIPT_BASE64_PLACEHOLDER | base64 -d | " +
|
||||||
|
"sudo UNCLOUD_GROUP_ADD_USER=nonroot UNCLOUD_VERSION=v1.2.3 bash",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
t.Run("nonroot", func(t *testing.T) {
|
for _, tt := range tests {
|
||||||
cmd := installCmd("nonroot", "")
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
assert.Contains(t, cmd, "sudo")
|
assert.Equal(t, tt.want, installCmd(scriptB64, tt.user, tt.version))
|
||||||
assert.Contains(t, cmd, "UNCLOUD_GROUP_ADD_USER=nonroot")
|
})
|
||||||
})
|
}
|
||||||
|
|
||||||
t.Run("nonroot with version", func(t *testing.T) {
|
|
||||||
cmd := installCmd("nonroot", "v1.2.3")
|
|
||||||
assert.Contains(t, cmd, "sudo")
|
|
||||||
assert.Contains(t, cmd, "UNCLOUD_GROUP_ADD_USER=nonroot")
|
|
||||||
assert.Contains(t, cmd, "UNCLOUD_VERSION=v1.2.3")
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,67 @@
|
|||||||
|
package progress
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/docker/docker/pkg/stringid"
|
||||||
|
"github.com/psviderski/uncloud/internal/cli/tui"
|
||||||
|
)
|
||||||
|
|
||||||
|
type eventIDKey struct{}
|
||||||
|
|
||||||
|
// WithEventID returns a context that overrides the default event ID used by client methods.
|
||||||
|
func WithEventID(ctx context.Context, eventID string) context.Context {
|
||||||
|
return context.WithValue(ctx, eventIDKey{}, eventID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainerEventID returns a progress event ID for operations on existing containers using the canonical
|
||||||
|
// service_name/short_id format. Allows to override it using WithEventID.
|
||||||
|
func ContainerEventID(ctx context.Context, serviceName, containerID, machineName string) string {
|
||||||
|
if id, ok := ctx.Value(eventIDKey{}).(string); ok && id != "" {
|
||||||
|
return id
|
||||||
|
}
|
||||||
|
return tui.Faint.Render("Container ") +
|
||||||
|
serviceName + tui.Faint.Render("/") + stringid.TruncateID(containerID) +
|
||||||
|
tui.Faint.Render(" on ") + machineName
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewContainerEventID returns a progress event ID for new container creation where the Docker container ID
|
||||||
|
// is not yet known. Allows to override it using WithEventID.
|
||||||
|
func NewContainerEventID(ctx context.Context, containerName, machineName string) string {
|
||||||
|
if id, ok := ctx.Value(eventIDKey{}).(string); ok && id != "" {
|
||||||
|
return id
|
||||||
|
}
|
||||||
|
return tui.Faint.Render("Container ") + containerName +
|
||||||
|
tui.Faint.Render(" on ") + machineName
|
||||||
|
}
|
||||||
|
|
||||||
|
// PreDeployHookEventID returns a progress event ID for pre-deploy hook operations.
|
||||||
|
func PreDeployHookEventID(serviceName, machineName string) string {
|
||||||
|
return tui.Faint.Render("Pre-deploy hook ") + serviceName +
|
||||||
|
tui.Faint.Render(" on ") + machineName
|
||||||
|
}
|
||||||
|
|
||||||
|
// OldPreDeployHookEventID returns a progress event ID for old pre-deploy hook container cleanup.
|
||||||
|
func OldPreDeployHookEventID(serviceName, containerID, machineName string) string {
|
||||||
|
return tui.Faint.Render("Old pre-deploy hook ") +
|
||||||
|
serviceName + tui.Faint.Render("/") + stringid.TruncateID(containerID) +
|
||||||
|
tui.Faint.Render(" on ") + machineName
|
||||||
|
}
|
||||||
|
|
||||||
|
// ImageEventID returns a progress event ID for image pull operations.
|
||||||
|
func ImageEventID(image, machineName string) string {
|
||||||
|
return tui.Faint.Render("Image ") + image +
|
||||||
|
tui.Faint.Render(" on ") + machineName
|
||||||
|
}
|
||||||
|
|
||||||
|
// VolumeEventID returns a progress event ID for volume operations.
|
||||||
|
func VolumeEventID(volumeName, machineName string) string {
|
||||||
|
return tui.Faint.Render("Volume ") + volumeName +
|
||||||
|
tui.Faint.Render(" on ") + machineName
|
||||||
|
}
|
||||||
|
|
||||||
|
// MachineEventID returns a progress event ID for machine operations.
|
||||||
|
func MachineEventID(machineName, publicIP string) string {
|
||||||
|
return tui.Faint.Render("Machine ") + machineName +
|
||||||
|
tui.Faint.Render(" (") + publicIP + tui.Faint.Render(")")
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package tui
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FormatRTT formats a round-trip time duration as whole milliseconds, e.g. "140ms".
|
||||||
|
func FormatRTT(d time.Duration) string {
|
||||||
|
return fmt.Sprintf("%dms", int64(math.Round(float64(d)/float64(time.Millisecond))))
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package tui
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFormatRTT(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
in time.Duration
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{"zero", 0, "0ms"},
|
||||||
|
{"rounds down", 39*time.Millisecond + 200*time.Microsecond, "39ms"},
|
||||||
|
{"rounds up at half", 39*time.Millisecond + 500*time.Microsecond, "40ms"},
|
||||||
|
{"rounds up near next ms", 39*time.Millisecond + 600*time.Microsecond, "40ms"},
|
||||||
|
{"exact ms", 140 * time.Millisecond, "140ms"},
|
||||||
|
{"one second", time.Second, "1000ms"},
|
||||||
|
{"1.5s", 1500 * time.Millisecond, "1500ms"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
assert.Equal(t, tt.want, FormatRTT(tt.in))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -42,6 +42,15 @@ func ThemeConfirm() huh.Theme {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ThemeConfirmDanger returns a huh theme with a bold red title style for dangerous confirmation prompts.
|
||||||
|
func ThemeConfirmDanger() huh.Theme {
|
||||||
|
return huh.ThemeFunc(func(isDark bool) *huh.Styles {
|
||||||
|
t := huh.ThemeBase(isDark)
|
||||||
|
t.Focused.Title = t.Focused.Title.Foreground(lipgloss.Red).Bold(true)
|
||||||
|
return t
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// IsStdinTerminal checks if the standard input is a terminal (TTY).
|
// IsStdinTerminal checks if the standard input is a terminal (TTY).
|
||||||
func IsStdinTerminal() bool {
|
func IsStdinTerminal() bool {
|
||||||
return term.IsTerminal(int(os.Stdin.Fd()))
|
return term.IsTerminal(int(os.Stdin.Fd()))
|
||||||
@@ -51,3 +60,13 @@ func IsStdinTerminal() bool {
|
|||||||
func IsStdoutTerminal() bool {
|
func IsStdoutTerminal() bool {
|
||||||
return term.IsTerminal(int(os.Stdout.Fd()))
|
return term.IsTerminal(int(os.Stdout.Fd()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TerminalWidth returns the width of the terminal.
|
||||||
|
// Returns 0 if stdout is not a terminal or the width cannot be determined.
|
||||||
|
func TerminalWidth() int {
|
||||||
|
width, _, err := term.GetSize(int(os.Stdout.Fd()))
|
||||||
|
if err != nil || width <= 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return width
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
NoStyle = lipgloss.NewStyle()
|
||||||
|
|
||||||
Faint = lipgloss.NewStyle().Faint(true)
|
Faint = lipgloss.NewStyle().Faint(true)
|
||||||
Red = lipgloss.NewStyle().Foreground(lipgloss.Red)
|
Red = lipgloss.NewStyle().Foreground(lipgloss.Red)
|
||||||
Green = lipgloss.NewStyle().Foreground(lipgloss.Green)
|
Green = lipgloss.NewStyle().Foreground(lipgloss.Green)
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package tui
|
||||||
|
|
||||||
|
import (
|
||||||
|
"charm.land/lipgloss/v2"
|
||||||
|
"charm.land/lipgloss/v2/table"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewTable creates a borderless table with bold headers and consistent padding for CLI output.
|
||||||
|
func NewTable() *table.Table {
|
||||||
|
return table.New().
|
||||||
|
Border(lipgloss.Border{}).
|
||||||
|
BorderTop(false).
|
||||||
|
BorderBottom(false).
|
||||||
|
BorderLeft(false).
|
||||||
|
BorderRight(false).
|
||||||
|
BorderHeader(false).
|
||||||
|
BorderColumn(false).
|
||||||
|
StyleFunc(func(row, col int) lipgloss.Style {
|
||||||
|
if row == table.HeaderRow {
|
||||||
|
return Bold.PaddingRight(3)
|
||||||
|
}
|
||||||
|
return NoStyle.PaddingRight(3)
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -6,8 +6,10 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"math"
|
||||||
"net"
|
"net"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
|
"sort"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -256,3 +258,116 @@ func (c *AdminClient) ClusterMembershipStates(latest bool) ([]ClusterMembershipS
|
|||||||
}
|
}
|
||||||
return states, parseErr
|
return states, parseErr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type MemberRTTStats struct {
|
||||||
|
Addr netip.AddrPort
|
||||||
|
Median time.Duration
|
||||||
|
StdDev time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClusterMemberRTTs returns the median and standard deviation of round-trip times to each cluster member.
|
||||||
|
func (c *AdminClient) ClusterMemberRTTs() ([]MemberRTTStats, error) {
|
||||||
|
respCh, err := c.SendCommand([]byte("{\"Cluster\":\"Members\"}"))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var stats []MemberRTTStats
|
||||||
|
var parseErr error
|
||||||
|
|
||||||
|
for r := range respCh {
|
||||||
|
if r.Err != nil {
|
||||||
|
return nil, r.Err
|
||||||
|
}
|
||||||
|
|
||||||
|
addr, rtts, err := parseClusterMemberRTT(r.JSON)
|
||||||
|
if err != nil {
|
||||||
|
parseErr = errors.Join(parseErr, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(rtts) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Corrosion reports RTT samples as floating-point milliseconds.
|
||||||
|
medianMs, stdDevMs := computeRTTStatsMs(rtts)
|
||||||
|
stats = append(stats, MemberRTTStats{
|
||||||
|
Addr: addr,
|
||||||
|
Median: time.Duration(medianMs * float64(time.Millisecond)),
|
||||||
|
StdDev: time.Duration(stdDevMs * float64(time.Millisecond)),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return stats, parseErr
|
||||||
|
}
|
||||||
|
|
||||||
|
// computeRTTStatsMs returns the median and population standard deviation (ms) of the given samples.
|
||||||
|
func computeRTTStatsMs(rtts []float64) (median, stdDev float64) {
|
||||||
|
if len(rtts) == 0 {
|
||||||
|
return 0, 0
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Float64s(rtts)
|
||||||
|
n := len(rtts)
|
||||||
|
if n%2 == 0 {
|
||||||
|
median = (rtts[n/2-1] + rtts[n/2]) / 2
|
||||||
|
} else {
|
||||||
|
median = rtts[n/2]
|
||||||
|
}
|
||||||
|
|
||||||
|
var sum float64
|
||||||
|
for _, rtt := range rtts {
|
||||||
|
sum += rtt
|
||||||
|
}
|
||||||
|
avg := sum / float64(n)
|
||||||
|
|
||||||
|
var varianceSum float64
|
||||||
|
for _, rtt := range rtts {
|
||||||
|
diff := rtt - avg
|
||||||
|
varianceSum += diff * diff
|
||||||
|
}
|
||||||
|
stdDev = math.Sqrt(varianceSum / float64(n))
|
||||||
|
|
||||||
|
return median, stdDev
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseClusterMemberRTT(json map[string]any) (netip.AddrPort, []float64, error) {
|
||||||
|
var addr netip.AddrPort
|
||||||
|
var rtts []float64
|
||||||
|
var err error
|
||||||
|
|
||||||
|
// Parse state to get Addr.
|
||||||
|
stateObj, ok := json["state"].(map[string]any)
|
||||||
|
if !ok {
|
||||||
|
return addr, nil, fmt.Errorf("missing or invalid 'state' field")
|
||||||
|
}
|
||||||
|
|
||||||
|
if addrStr, ok := stateObj["addr"].(string); ok {
|
||||||
|
addr, err = netip.ParseAddrPort(addrStr)
|
||||||
|
if err != nil {
|
||||||
|
return addr, nil, fmt.Errorf("parse 'addr' field: %w", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return addr, nil, fmt.Errorf("missing or invalid 'addr' field in 'state'")
|
||||||
|
}
|
||||||
|
|
||||||
|
// The absent 'rtts' key or equal to null are treated as no samples yet.
|
||||||
|
rttsVal, ok := json["rtts"]
|
||||||
|
if !ok || rttsVal == nil {
|
||||||
|
return addr, nil, nil
|
||||||
|
}
|
||||||
|
rttsSlice, ok := rttsVal.([]any)
|
||||||
|
if !ok {
|
||||||
|
return addr, nil, fmt.Errorf("invalid 'rtts' field type: %T", rttsVal)
|
||||||
|
}
|
||||||
|
for _, v := range rttsSlice {
|
||||||
|
f, ok := v.(float64)
|
||||||
|
if !ok {
|
||||||
|
return addr, nil, fmt.Errorf("invalid rtt value type: %T", v)
|
||||||
|
}
|
||||||
|
rtts = append(rtts, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
return addr, rtts, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,174 @@
|
|||||||
|
package corrosion
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
"net/netip"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestComputeRTTStatsMs(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
samples []float64
|
||||||
|
wantMedian float64
|
||||||
|
wantStdDev float64
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "single sample",
|
||||||
|
samples: []float64{42},
|
||||||
|
wantMedian: 42,
|
||||||
|
wantStdDev: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "two samples (even, averaged)",
|
||||||
|
samples: []float64{10, 20},
|
||||||
|
wantMedian: 15,
|
||||||
|
// Population stddev: variance = ((10-15)^2 + (20-15)^2)/2 = 25; sqrt = 5.
|
||||||
|
wantStdDev: 5,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "three samples (odd, middle)",
|
||||||
|
samples: []float64{3, 1, 2},
|
||||||
|
wantMedian: 2,
|
||||||
|
// Mean = 2; variance = (1+0+1)/3 = 0.666...; stddev = sqrt(2/3).
|
||||||
|
wantStdDev: math.Sqrt(2.0 / 3.0),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "four samples (even, averaged)",
|
||||||
|
samples: []float64{1, 2, 3, 4},
|
||||||
|
wantMedian: 2.5,
|
||||||
|
// Mean = 2.5; variance = (2.25+0.25+0.25+2.25)/4 = 1.25; stddev = sqrt(1.25).
|
||||||
|
wantStdDev: math.Sqrt(1.25),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// Verifies that unsorted input is sorted before picking the median.
|
||||||
|
name: "unsorted input",
|
||||||
|
samples: []float64{100, 1, 50},
|
||||||
|
wantMedian: 50,
|
||||||
|
// Mean = 151/3. Variance = ((149^2 + 148^2 + 1^2) / 9) / 3.
|
||||||
|
wantStdDev: math.Sqrt(float64(149*149+148*148+1) / 9.0 / 3.0),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
median, stdDev := computeRTTStatsMs(tt.samples)
|
||||||
|
assert.InDelta(t, tt.wantMedian, median, 1e-9, "median")
|
||||||
|
assert.InDelta(t, tt.wantStdDev, stdDev, 1e-9, "stdDev")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseClusterMemberRTT(t *testing.T) {
|
||||||
|
validState := map[string]any{"addr": "[fdcc:b618:5034:7afa:172a:1452:f2de:3c99]:51001"}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input map[string]any
|
||||||
|
wantAddr string
|
||||||
|
wantRTTs []float64
|
||||||
|
wantErr bool
|
||||||
|
errSubstr string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "valid state and rtts",
|
||||||
|
input: map[string]any{
|
||||||
|
"state": validState,
|
||||||
|
"rtts": []any{float64(10), float64(20), float64(30)},
|
||||||
|
},
|
||||||
|
wantAddr: "[fdcc:b618:5034:7afa:172a:1452:f2de:3c99]:51001",
|
||||||
|
wantRTTs: []float64{10, 20, 30},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "missing rtts key is not an error",
|
||||||
|
input: map[string]any{
|
||||||
|
"state": validState,
|
||||||
|
},
|
||||||
|
wantAddr: "[fdcc:b618:5034:7afa:172a:1452:f2de:3c99]:51001",
|
||||||
|
wantRTTs: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "null rtts value is not an error",
|
||||||
|
input: map[string]any{
|
||||||
|
"state": validState,
|
||||||
|
"rtts": nil,
|
||||||
|
},
|
||||||
|
wantAddr: "[fdcc:b618:5034:7afa:172a:1452:f2de:3c99]:51001",
|
||||||
|
wantRTTs: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty rtts array is not an error",
|
||||||
|
input: map[string]any{
|
||||||
|
"state": validState,
|
||||||
|
"rtts": []any{},
|
||||||
|
},
|
||||||
|
wantAddr: "[fdcc:b618:5034:7afa:172a:1452:f2de:3c99]:51001",
|
||||||
|
wantRTTs: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "non-array rtts",
|
||||||
|
input: map[string]any{
|
||||||
|
"state": validState,
|
||||||
|
"rtts": "not-an-array",
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
errSubstr: "invalid 'rtts' field type",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "non-number element in rtts",
|
||||||
|
input: map[string]any{
|
||||||
|
"state": validState,
|
||||||
|
"rtts": []any{float64(10), "bad"},
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
errSubstr: "invalid rtt value type",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "missing state",
|
||||||
|
input: map[string]any{"rtts": []any{float64(1)}},
|
||||||
|
wantErr: true,
|
||||||
|
errSubstr: "missing or invalid 'state' field",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "missing addr in state",
|
||||||
|
input: map[string]any{
|
||||||
|
"state": map[string]any{},
|
||||||
|
"rtts": []any{float64(1)},
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
errSubstr: "missing or invalid 'addr' field",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "invalid addr format",
|
||||||
|
input: map[string]any{
|
||||||
|
"state": map[string]any{"addr": "not-an-addr"},
|
||||||
|
"rtts": []any{float64(1)},
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
errSubstr: "parse 'addr' field",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
addr, rtts, err := parseClusterMemberRTT(tt.input)
|
||||||
|
if tt.wantErr {
|
||||||
|
require.Error(t, err)
|
||||||
|
assert.Contains(t, err.Error(), tt.errSubstr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, tt.wantAddr, addr.String())
|
||||||
|
assert.Equal(t, tt.wantRTTs, rtts)
|
||||||
|
|
||||||
|
// Sanity: the parsed addr is a valid AddrPort.
|
||||||
|
_, perr := netip.ParseAddrPort(addr.String())
|
||||||
|
assert.NoError(t, perr)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -71,3 +71,8 @@ func Chown(path, username, group string) error {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Exists(path string) bool {
|
||||||
|
_, err := os.Stat(path)
|
||||||
|
return err == nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,194 @@
|
|||||||
|
package grpcversion
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"sync/atomic"
|
||||||
|
|
||||||
|
"github.com/Masterminds/semver"
|
||||||
|
"github.com/psviderski/uncloud/internal/version"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
"google.golang.org/grpc/codes"
|
||||||
|
"google.golang.org/grpc/metadata"
|
||||||
|
"google.golang.org/grpc/status"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
MetadataKeyClientVersion = "uncloud-client-version"
|
||||||
|
MetadataKeyMinServerVersion = "uncloud-min-server-version"
|
||||||
|
MetadataKeyServerVersion = "uncloud-server-version"
|
||||||
|
|
||||||
|
// MinClientVersion is the minimum client version the daemon accepts. The daemon
|
||||||
|
// rejects requests from older clients, forcing them to upgrade. This provides
|
||||||
|
// a clean cut-off for dropping support for old clients.
|
||||||
|
//
|
||||||
|
// MinServerVersion is the minimum daemon version the client requires. The client
|
||||||
|
// sends this with each request so the daemon can immediately reject if it's too old,
|
||||||
|
// avoiding the need for a preflight request. This is useful when a new client feature
|
||||||
|
// requires daemon capabilities that didn't exist in older versions.
|
||||||
|
//
|
||||||
|
// The two minimums are independent: a client might require a newer daemon for new
|
||||||
|
// features, while that same daemon could still handle requests from older clients.
|
||||||
|
MinClientVersion = "0.0.0"
|
||||||
|
MinServerVersion = "0.0.0"
|
||||||
|
|
||||||
|
ReleaseURL = "https://github.com/psviderski/uncloud/releases/latest"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// zeroVersion is used when no version is specified (treated as 0.0.0).
|
||||||
|
zeroVersion = semver.MustParse("0.0.0")
|
||||||
|
// currentVersion is the version of this binary (CLI or daemon). It's injected via an ldflag at build time.
|
||||||
|
// Fall back to zeroVersion if the injected string isn't valid semver.
|
||||||
|
currentVersion = parseVersionOrZero(version.String())
|
||||||
|
// Pre-parsed minimum versions for comparison.
|
||||||
|
minClientVersion = semver.MustParse(MinClientVersion)
|
||||||
|
minServerVersion = semver.MustParse(MinServerVersion)
|
||||||
|
|
||||||
|
// warned tracks if we've already printed the daemon version warning.
|
||||||
|
// TODO: Remove when checkServerVersionInResponse is no longer needed (see below).
|
||||||
|
warned atomic.Bool
|
||||||
|
|
||||||
|
// WarnWriter is the writer used for version mismatch warnings. Defaults to os.Stderr.
|
||||||
|
// Tests can override this to capture warning output.
|
||||||
|
WarnWriter io.Writer = os.Stderr
|
||||||
|
)
|
||||||
|
|
||||||
|
// parseVersionOrZero parses v as a semver, returning zeroVersion if parsing fails.
|
||||||
|
func parseVersionOrZero(v string) *semver.Version {
|
||||||
|
parsed, err := semver.NewVersion(v)
|
||||||
|
if err != nil {
|
||||||
|
return zeroVersion
|
||||||
|
}
|
||||||
|
return parsed
|
||||||
|
}
|
||||||
|
|
||||||
|
func extractVersion(md metadata.MD, key string) *semver.Version {
|
||||||
|
if md == nil {
|
||||||
|
return zeroVersion
|
||||||
|
}
|
||||||
|
values := md.Get(key)
|
||||||
|
if len(values) == 0 || values[0] == "" {
|
||||||
|
return zeroVersion
|
||||||
|
}
|
||||||
|
sv, err := semver.NewVersion(values[0])
|
||||||
|
if err != nil {
|
||||||
|
return zeroVersion
|
||||||
|
}
|
||||||
|
return sv
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkClientVersionHeaders(ctx context.Context) error {
|
||||||
|
md, _ := metadata.FromIncomingContext(ctx)
|
||||||
|
|
||||||
|
actualClientVersion := extractVersion(md, MetadataKeyClientVersion)
|
||||||
|
if actualClientVersion.LessThan(minClientVersion) {
|
||||||
|
return status.Errorf(codes.FailedPrecondition,
|
||||||
|
"version check failed: client version is below minimum %s. Please upgrade: %s",
|
||||||
|
minClientVersion, ReleaseURL)
|
||||||
|
}
|
||||||
|
|
||||||
|
requiredMinServer := extractVersion(md, MetadataKeyMinServerVersion)
|
||||||
|
if currentVersion.LessThan(requiredMinServer) {
|
||||||
|
return status.Errorf(codes.FailedPrecondition,
|
||||||
|
"version check failed: daemon version %s is below client's minimum required version %s. Please upgrade the daemon: %s",
|
||||||
|
currentVersion, requiredMinServer, ReleaseURL)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ServerUnaryInterceptor(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (any, error) {
|
||||||
|
if err := checkClientVersionHeaders(ctx); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := grpc.SetHeader(ctx, metadata.Pairs(MetadataKeyServerVersion, currentVersion.String())); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return handler(ctx, req)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ServerStreamInterceptor(srv any, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
|
||||||
|
if err := checkClientVersionHeaders(ss.Context()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := ss.SetHeader(metadata.Pairs(MetadataKeyServerVersion, currentVersion.String())); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return handler(srv, ss)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ClientUnaryInterceptor(ctx context.Context, method string, req, reply any, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
|
||||||
|
ctx = metadata.AppendToOutgoingContext(ctx,
|
||||||
|
MetadataKeyClientVersion, currentVersion.String(),
|
||||||
|
MetadataKeyMinServerVersion, MinServerVersion,
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO: Remove when checkServerVersionInResponse is no longer needed,
|
||||||
|
// as we'll no longer need to extract headers from the response here.
|
||||||
|
var respMD metadata.MD
|
||||||
|
opts = append(opts, grpc.Header(&respMD))
|
||||||
|
|
||||||
|
err := invoker(ctx, method, req, reply, cc, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Remove eventually (see note on method below).
|
||||||
|
checkServerVersionInResponse(respMD)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkServerVersionInResponse warns the user when they communicated with a daemon that
|
||||||
|
// did not check the version requirements. This is only needed during the transition to
|
||||||
|
// version-checking releases.
|
||||||
|
// TODO: Remove this in some later release, after users have upgraded.
|
||||||
|
func checkServerVersionInResponse(md metadata.MD) {
|
||||||
|
serverVersion := extractVersion(md, MetadataKeyServerVersion)
|
||||||
|
if serverVersion.LessThan(minServerVersion) {
|
||||||
|
if warned.Swap(true) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
msg := fmt.Sprintf("daemon version is below minimum required version %s. The daemon did not verify this CLI's minimum version requirement, so the operation may not have behaved as intended. Please upgrade the daemon: %s",
|
||||||
|
minServerVersion, ReleaseURL)
|
||||||
|
fmt.Fprintf(WarnWriter, "WARNING: %s\n", msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ClientStreamInterceptor(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption) (grpc.ClientStream, error) {
|
||||||
|
ctx = metadata.AppendToOutgoingContext(ctx,
|
||||||
|
MetadataKeyClientVersion, currentVersion.String(),
|
||||||
|
MetadataKeyMinServerVersion, MinServerVersion,
|
||||||
|
)
|
||||||
|
|
||||||
|
stream, err := streamer(ctx, desc, cc, method, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Wrapping the stream in versionedClientStream will no longer
|
||||||
|
// be necessary when we are ready to remove the temporary, transition
|
||||||
|
// safety check checkServerVersionInResponse (see note on method above).
|
||||||
|
return &versionedClientStream{ClientStream: stream}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Remove when checkServerVersionInResponse is no longer needed.
|
||||||
|
type versionedClientStream struct {
|
||||||
|
grpc.ClientStream
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Remove when checkServerVersionInResponse is no longer needed.
|
||||||
|
func (s *versionedClientStream) Header() (metadata.MD, error) {
|
||||||
|
md, err := s.ClientStream.Header()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
checkServerVersionInResponse(md)
|
||||||
|
|
||||||
|
return md, nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,236 @@
|
|||||||
|
package grpcversion
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"google.golang.org/grpc/codes"
|
||||||
|
"google.golang.org/grpc/metadata"
|
||||||
|
"google.golang.org/grpc/status"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestParseVersionOrZero(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "valid semver",
|
||||||
|
input: "1.2.3",
|
||||||
|
expected: "1.2.3",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "valid semver with prerelease",
|
||||||
|
input: "0.19.0-nightly-abc1234",
|
||||||
|
expected: "0.19.0-nightly-abc1234",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "dev version",
|
||||||
|
input: "999.0.0-dev",
|
||||||
|
expected: "999.0.0-dev",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "invalid ldflag-injected string falls back to zero",
|
||||||
|
input: "nightly-SNAPSHOT-abc1234",
|
||||||
|
expected: "0.0.0",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty string falls back to zero",
|
||||||
|
input: "",
|
||||||
|
expected: "0.0.0",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
got := parseVersionOrZero(tt.input)
|
||||||
|
assert.Equal(t, tt.expected, got.String())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestExtractVersion(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
md metadata.MD
|
||||||
|
key string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "nil metadata",
|
||||||
|
md: nil,
|
||||||
|
key: MetadataKeyClientVersion,
|
||||||
|
expected: "0.0.0",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "missing key",
|
||||||
|
md: metadata.MD{},
|
||||||
|
key: MetadataKeyClientVersion,
|
||||||
|
expected: "0.0.0",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty value",
|
||||||
|
md: metadata.Pairs(MetadataKeyClientVersion, ""),
|
||||||
|
key: MetadataKeyClientVersion,
|
||||||
|
expected: "0.0.0",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "invalid version",
|
||||||
|
md: metadata.Pairs(MetadataKeyClientVersion, "not-a-version"),
|
||||||
|
key: MetadataKeyClientVersion,
|
||||||
|
expected: "0.0.0",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "valid version",
|
||||||
|
md: metadata.Pairs(MetadataKeyClientVersion, "1.2.3"),
|
||||||
|
key: MetadataKeyClientVersion,
|
||||||
|
expected: "1.2.3",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "version with prerelease",
|
||||||
|
md: metadata.Pairs(MetadataKeyClientVersion, "0.0.0-dev"),
|
||||||
|
key: MetadataKeyClientVersion,
|
||||||
|
expected: "0.0.0-dev",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
got := extractVersion(tt.md, tt.key)
|
||||||
|
assert.Equal(t, tt.expected, got.String())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCheckClientVersionHeaders(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
md metadata.MD
|
||||||
|
wantErr bool
|
||||||
|
errCode codes.Code
|
||||||
|
errContain string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "cli version below minimum",
|
||||||
|
md: metadata.Pairs(
|
||||||
|
MetadataKeyClientVersion, "0.0.0-dev",
|
||||||
|
),
|
||||||
|
wantErr: true,
|
||||||
|
errCode: codes.FailedPrecondition,
|
||||||
|
errContain: "client version is below minimum",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "cli version above minimum",
|
||||||
|
md: metadata.Pairs(
|
||||||
|
MetadataKeyClientVersion, "999.0.0",
|
||||||
|
),
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "min daemon version above current daemon",
|
||||||
|
md: metadata.Pairs(
|
||||||
|
MetadataKeyClientVersion, "999.0.0",
|
||||||
|
MetadataKeyMinServerVersion, "999.0.0",
|
||||||
|
),
|
||||||
|
wantErr: true,
|
||||||
|
errCode: codes.FailedPrecondition,
|
||||||
|
errContain: "daemon version",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "min daemon version below current daemon",
|
||||||
|
md: metadata.Pairs(
|
||||||
|
MetadataKeyClientVersion, "999.0.0",
|
||||||
|
MetadataKeyMinServerVersion, "0.0.1",
|
||||||
|
),
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
ctx := context.Background()
|
||||||
|
if tt.md != nil {
|
||||||
|
ctx = metadata.NewIncomingContext(ctx, tt.md)
|
||||||
|
}
|
||||||
|
|
||||||
|
err := checkClientVersionHeaders(ctx)
|
||||||
|
|
||||||
|
if tt.wantErr {
|
||||||
|
require.Error(t, err)
|
||||||
|
st, ok := status.FromError(err)
|
||||||
|
require.True(t, ok, "expected gRPC status error, got %T", err)
|
||||||
|
assert.Equal(t, tt.errCode, st.Code())
|
||||||
|
assert.Contains(t, st.Message(), tt.errContain)
|
||||||
|
} else {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func captureWarnings(t *testing.T, fn func()) string {
|
||||||
|
t.Helper()
|
||||||
|
var buf bytes.Buffer
|
||||||
|
old := WarnWriter
|
||||||
|
WarnWriter = &buf
|
||||||
|
t.Cleanup(func() { WarnWriter = old })
|
||||||
|
fn()
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCheckServerVersionInResponse(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
md metadata.MD
|
||||||
|
wantWarning bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "daemon version below minimum",
|
||||||
|
md: metadata.Pairs(MetadataKeyServerVersion, "0.0.0-dev"),
|
||||||
|
wantWarning: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "daemon version above minimum",
|
||||||
|
md: metadata.Pairs(MetadataKeyServerVersion, "999.0.0"),
|
||||||
|
wantWarning: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
warned.Store(false)
|
||||||
|
|
||||||
|
output := captureWarnings(t, func() {
|
||||||
|
checkServerVersionInResponse(tt.md)
|
||||||
|
})
|
||||||
|
|
||||||
|
if tt.wantWarning {
|
||||||
|
assert.Contains(t, output, "WARNING")
|
||||||
|
} else {
|
||||||
|
assert.Empty(t, output)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCheckServerVersionInResponse_WarnOnce(t *testing.T) {
|
||||||
|
warned.Store(false)
|
||||||
|
|
||||||
|
md := metadata.Pairs(MetadataKeyServerVersion, "0.0.0-dev")
|
||||||
|
|
||||||
|
// First call should warn.
|
||||||
|
output1 := captureWarnings(t, func() {
|
||||||
|
checkServerVersionInResponse(md)
|
||||||
|
})
|
||||||
|
assert.Contains(t, output1, "WARNING", "first call should warn")
|
||||||
|
|
||||||
|
// Second call should not warn (warned flag is now true).
|
||||||
|
output2 := captureWarnings(t, func() {
|
||||||
|
checkServerVersionInResponse(md)
|
||||||
|
})
|
||||||
|
assert.Empty(t, output2, "second call should not warn")
|
||||||
|
}
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
package journal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os/exec"
|
||||||
|
|
||||||
|
"github.com/psviderski/uncloud/pkg/api"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
UnitUncloud = "uncloud"
|
||||||
|
UnitDocker = "docker"
|
||||||
|
UnitCorrosion = "uncloud-corrosion"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ValidUnit(unit string) bool {
|
||||||
|
switch unit {
|
||||||
|
case UnitUncloud:
|
||||||
|
case UnitDocker:
|
||||||
|
case UnitCorrosion:
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
const journalctl = "journalctl"
|
||||||
|
|
||||||
|
var commandContext = exec.CommandContext // allow override for test
|
||||||
|
|
||||||
|
func logs(ctx context.Context, unit string, opts api.ServiceLogsOptions) (io.ReadCloser, func() error, error) {
|
||||||
|
if !ValidUnit(unit) {
|
||||||
|
return nil, nil, fmt.Errorf("journal logs: invalid unit: %s", unit)
|
||||||
|
}
|
||||||
|
args := []string{"-u", unit, "--no-hostname"}
|
||||||
|
args = append(args, "-n")
|
||||||
|
if opts.Tail > -1 {
|
||||||
|
args = append(args, fmt.Sprintf("%d", opts.Tail))
|
||||||
|
} else {
|
||||||
|
args = append(args, "all")
|
||||||
|
}
|
||||||
|
if opts.Follow {
|
||||||
|
args = append(args, "-f")
|
||||||
|
}
|
||||||
|
|
||||||
|
args = append(args, "-o")
|
||||||
|
args = append(args, "short-unix")
|
||||||
|
|
||||||
|
if opts.Since != "" {
|
||||||
|
args = append(args, "-S")
|
||||||
|
args = append(args, opts.Since)
|
||||||
|
}
|
||||||
|
if opts.Until != "" {
|
||||||
|
args = append(args, "-U")
|
||||||
|
args = append(args, opts.Until)
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := commandContext(ctx, journalctl, args...)
|
||||||
|
p, err := cmd.StdoutPipe()
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := cmd.Start(); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return p, cmd.Wait, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// follow synchronously follows the io.Reader, writing each new journal entry to channel.
|
||||||
|
// It stops when the reader is exhausted or the context is cancelled.
|
||||||
|
func follow(ctx context.Context, reader io.Reader, outCh chan api.LogEntry) {
|
||||||
|
scanner := bufio.NewScanner(reader)
|
||||||
|
|
||||||
|
for scanner.Scan() {
|
||||||
|
select {
|
||||||
|
case outCh <- entry(scanner.Bytes()):
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := scanner.Err(); err != nil {
|
||||||
|
outCh <- api.LogEntry{Err: fmt.Errorf("journal logs: %w", err)}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
package journal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"slices"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/psviderski/uncloud/pkg/api"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Logs streams logs from a service and returns entries via a channel.
|
||||||
|
func Logs(ctx context.Context, unit string, opts api.ServiceLogsOptions) (<-chan api.LogEntry, error) {
|
||||||
|
if !ValidUnit(unit) {
|
||||||
|
return nil, fmt.Errorf("journal logs: invalid unit: %s", unit)
|
||||||
|
}
|
||||||
|
|
||||||
|
reader, wait, err := logs(ctx, unit, opts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
outCh := make(chan api.LogEntry)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer close(outCh)
|
||||||
|
follow(ctx, reader, outCh)
|
||||||
|
wait()
|
||||||
|
}()
|
||||||
|
|
||||||
|
return outCh, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func entry(data []byte) api.LogEntry {
|
||||||
|
// 1758193407.686964 systemd[1]: ...
|
||||||
|
timestamp := time.Time{}
|
||||||
|
message := data
|
||||||
|
if timestampPart, messagePart, found := bytes.Cut(data, []byte(" ")); found {
|
||||||
|
if t, ok := parseUnixTimestamp(timestampPart); ok {
|
||||||
|
timestamp = t
|
||||||
|
message = messagePart
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return api.LogEntry{
|
||||||
|
Timestamp: timestamp,
|
||||||
|
Message: append(slices.Clone(message), '\n'), // scanner controls the buffer so Clone and re-add newline
|
||||||
|
Stream: api.LogStreamStdout,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseUnixTimestamp parses a journalctl short-unix timestamp "SSSSSSSSSS.UUUUUU" with microsecond precision.
|
||||||
|
func parseUnixTimestamp(b []byte) (time.Time, bool) {
|
||||||
|
secPart, usecPart, found := bytes.Cut(b, []byte("."))
|
||||||
|
if !found {
|
||||||
|
return time.Time{}, false
|
||||||
|
}
|
||||||
|
sec, err := strconv.ParseInt(string(secPart), 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return time.Time{}, false
|
||||||
|
}
|
||||||
|
usec, err := strconv.ParseInt(string(usecPart), 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return time.Time{}, false
|
||||||
|
}
|
||||||
|
return time.Unix(sec, usec*1000), true
|
||||||
|
}
|
||||||
@@ -0,0 +1,152 @@
|
|||||||
|
package journal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"os/exec"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/psviderski/uncloud/pkg/api"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestParseUnixTimestamp(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input string
|
||||||
|
want time.Time
|
||||||
|
ok bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "valid with microseconds",
|
||||||
|
input: "1769188773.687500",
|
||||||
|
want: time.Unix(1769188773, 687500000),
|
||||||
|
ok: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "zero microseconds",
|
||||||
|
input: "1769188773.000000",
|
||||||
|
want: time.Unix(1769188773, 0),
|
||||||
|
ok: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "epoch",
|
||||||
|
input: "0.000000",
|
||||||
|
want: time.Unix(0, 0),
|
||||||
|
ok: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no fractional part",
|
||||||
|
input: "1769188773",
|
||||||
|
want: time.Time{},
|
||||||
|
ok: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "non-numeric seconds",
|
||||||
|
input: "abc.000000",
|
||||||
|
want: time.Time{},
|
||||||
|
ok: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "non-numeric microseconds",
|
||||||
|
input: "1769188773.abc",
|
||||||
|
want: time.Time{},
|
||||||
|
ok: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty",
|
||||||
|
input: "",
|
||||||
|
want: time.Time{},
|
||||||
|
ok: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
got, ok := parseUnixTimestamp([]byte(tt.input))
|
||||||
|
assert.Equal(t, tt.ok, ok)
|
||||||
|
assert.True(t, tt.want.Equal(got), "want %v, got %v", tt.want, got)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEntry(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input string
|
||||||
|
timestamp time.Time
|
||||||
|
message string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "valid entry",
|
||||||
|
input: "1769188773.687500 uncloudd[332455]: INFO Starting daemon.",
|
||||||
|
timestamp: time.Unix(1769188773, 687500000),
|
||||||
|
message: "uncloudd[332455]: INFO Starting daemon.\n",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "unparseable timestamp keeps full line as message",
|
||||||
|
input: "-- Boot 1234567890 --",
|
||||||
|
timestamp: time.Time{},
|
||||||
|
message: "-- Boot 1234567890 --\n",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "line without space keeps full line as message",
|
||||||
|
input: "nospacehere",
|
||||||
|
timestamp: time.Time{},
|
||||||
|
message: "nospacehere\n",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty line",
|
||||||
|
input: "",
|
||||||
|
timestamp: time.Time{},
|
||||||
|
message: "\n",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
got := entry([]byte(tt.input))
|
||||||
|
assert.True(t, tt.timestamp.Equal(got.Timestamp), "timestamp: want %v, got %v", tt.timestamp, got.Timestamp)
|
||||||
|
assert.Equal(t, tt.message, string(got.Message))
|
||||||
|
assert.Equal(t, api.LogStreamStdout, got.Stream)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLogs(t *testing.T) {
|
||||||
|
commandContext = func(ctx context.Context, _ string, _ ...string) *exec.Cmd {
|
||||||
|
return exec.CommandContext(ctx, "/usr/bin/tail", "testdata/logs")
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
ch, err := Logs(ctx, "uncloud", api.ServiceLogsOptions{})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
i := 0
|
||||||
|
for range ch {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
assert.Equal(t, 6, i)
|
||||||
|
|
||||||
|
commandContext = func(ctx context.Context, _ string, _ ...string) *exec.Cmd {
|
||||||
|
return exec.CommandContext(ctx, "/usr/bin/tail", "-f", "testdata/logs")
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx = context.Background()
|
||||||
|
ctx, cancel = context.WithCancel(ctx)
|
||||||
|
go func() { time.Sleep(1 * time.Second); cancel() }()
|
||||||
|
|
||||||
|
ch, err = Logs(ctx, "uncloud", api.ServiceLogsOptions{Tail: 3})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
i = 0
|
||||||
|
for range ch {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
// Still six because heartbeats are not written here and Tail is ignored as the command is overridden.
|
||||||
|
assert.Equal(t, 6, i)
|
||||||
|
}
|
||||||
Vendored
+6
@@ -0,0 +1,6 @@
|
|||||||
|
1769188773.686964 fedora kernel: apple-dcp 271c00000.dcp: DCP index:1 dptx target phy: 5 dptx die: 0
|
||||||
|
1769188773.687155 fedora kernel: platform 271c00000.dcp:piodma: Adding to iommu group 9
|
||||||
|
1769188773.687343 fedora kernel: apple-dcp 271c00000.dcp: RTKit: Initializing (protocol version 12)
|
||||||
|
1769188773.687500 fedora kernel: apple-dcp 271c00000.dcp: shmem_setup: iova: 880255000 -> pa: be4f29000 -> iomem: ffff800082>
|
||||||
|
1769188773.687657 fedora kernel: apple-dcp 271c00000.dcp: shmem_setup: iova: ffffec000, buffer: ffff8000817cc000
|
||||||
|
1769188773.687826 fedora kernel: apple-dcp 271c00000.dcp: shmem_setup: iova: ffffe8000, buffer: ffff8000817d4000
|
||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
status "google.golang.org/genproto/googleapis/rpc/status"
|
status "google.golang.org/genproto/googleapis/rpc/status"
|
||||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||||
|
timestamppb "google.golang.org/protobuf/types/known/timestamppb"
|
||||||
reflect "reflect"
|
reflect "reflect"
|
||||||
sync "sync"
|
sync "sync"
|
||||||
)
|
)
|
||||||
@@ -21,6 +22,58 @@ const (
|
|||||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type LogEntry_StreamType int32
|
||||||
|
|
||||||
|
const (
|
||||||
|
LogEntry_UNKNOWN LogEntry_StreamType = 0
|
||||||
|
LogEntry_STDOUT LogEntry_StreamType = 1
|
||||||
|
LogEntry_STDERR LogEntry_StreamType = 2
|
||||||
|
LogEntry_HEARTBEAT LogEntry_StreamType = 3
|
||||||
|
)
|
||||||
|
|
||||||
|
// Enum value maps for LogEntry_StreamType.
|
||||||
|
var (
|
||||||
|
LogEntry_StreamType_name = map[int32]string{
|
||||||
|
0: "UNKNOWN",
|
||||||
|
1: "STDOUT",
|
||||||
|
2: "STDERR",
|
||||||
|
3: "HEARTBEAT",
|
||||||
|
}
|
||||||
|
LogEntry_StreamType_value = map[string]int32{
|
||||||
|
"UNKNOWN": 0,
|
||||||
|
"STDOUT": 1,
|
||||||
|
"STDERR": 2,
|
||||||
|
"HEARTBEAT": 3,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func (x LogEntry_StreamType) Enum() *LogEntry_StreamType {
|
||||||
|
p := new(LogEntry_StreamType)
|
||||||
|
*p = x
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x LogEntry_StreamType) String() string {
|
||||||
|
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (LogEntry_StreamType) Descriptor() protoreflect.EnumDescriptor {
|
||||||
|
return file_internal_machine_api_pb_common_proto_enumTypes[0].Descriptor()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (LogEntry_StreamType) Type() protoreflect.EnumType {
|
||||||
|
return &file_internal_machine_api_pb_common_proto_enumTypes[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x LogEntry_StreamType) Number() protoreflect.EnumNumber {
|
||||||
|
return protoreflect.EnumNumber(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use LogEntry_StreamType.Descriptor instead.
|
||||||
|
func (LogEntry_StreamType) EnumDescriptor() ([]byte, []int) {
|
||||||
|
return file_internal_machine_api_pb_common_proto_rawDescGZIP(), []int{7, 0}
|
||||||
|
}
|
||||||
|
|
||||||
// Common metadata message nested in all reply message types, injected by the gRPC proxy to provide information
|
// Common metadata message nested in all reply message types, injected by the gRPC proxy to provide information
|
||||||
// about the machine that responded to the request.
|
// about the machine that responded to the request.
|
||||||
type Metadata struct {
|
type Metadata struct {
|
||||||
@@ -343,6 +396,150 @@ func (x *IPPrefix) GetBits() uint32 {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type LogsRequest struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
|
||||||
|
// Options for logs retrieval.
|
||||||
|
Follow bool `protobuf:"varint,2,opt,name=follow,proto3" json:"follow,omitempty"`
|
||||||
|
Tail int32 `protobuf:"varint,3,opt,name=tail,proto3" json:"tail,omitempty"` // -1 means all
|
||||||
|
Since string `protobuf:"bytes,4,opt,name=since,proto3" json:"since,omitempty"` // https://www.rfc-editor.org/rfc/rfc3339.html timestamp or Go duration string
|
||||||
|
Until string `protobuf:"bytes,5,opt,name=until,proto3" json:"until,omitempty"` // https://www.rfc-editor.org/rfc/rfc3339.html timestamp or Go duration string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *LogsRequest) Reset() {
|
||||||
|
*x = LogsRequest{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_internal_machine_api_pb_common_proto_msgTypes[6]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *LogsRequest) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*LogsRequest) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *LogsRequest) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_internal_machine_api_pb_common_proto_msgTypes[6]
|
||||||
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use LogsRequest.ProtoReflect.Descriptor instead.
|
||||||
|
func (*LogsRequest) Descriptor() ([]byte, []int) {
|
||||||
|
return file_internal_machine_api_pb_common_proto_rawDescGZIP(), []int{6}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *LogsRequest) GetId() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Id
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *LogsRequest) GetFollow() bool {
|
||||||
|
if x != nil {
|
||||||
|
return x.Follow
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *LogsRequest) GetTail() int32 {
|
||||||
|
if x != nil {
|
||||||
|
return x.Tail
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *LogsRequest) GetSince() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Since
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *LogsRequest) GetUntil() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Until
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
type LogEntry struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
Stream LogEntry_StreamType `protobuf:"varint,1,opt,name=stream,proto3,enum=api.LogEntry_StreamType" json:"stream,omitempty"`
|
||||||
|
Timestamp *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
|
||||||
|
// Log line content. Empty for heartbeat entries.
|
||||||
|
Message []byte `protobuf:"bytes,3,opt,name=message,proto3" json:"message,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *LogEntry) Reset() {
|
||||||
|
*x = LogEntry{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_internal_machine_api_pb_common_proto_msgTypes[7]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *LogEntry) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*LogEntry) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *LogEntry) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_internal_machine_api_pb_common_proto_msgTypes[7]
|
||||||
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use LogEntry.ProtoReflect.Descriptor instead.
|
||||||
|
func (*LogEntry) Descriptor() ([]byte, []int) {
|
||||||
|
return file_internal_machine_api_pb_common_proto_rawDescGZIP(), []int{7}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *LogEntry) GetStream() LogEntry_StreamType {
|
||||||
|
if x != nil {
|
||||||
|
return x.Stream
|
||||||
|
}
|
||||||
|
return LogEntry_UNKNOWN
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *LogEntry) GetTimestamp() *timestamppb.Timestamp {
|
||||||
|
if x != nil {
|
||||||
|
return x.Timestamp
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *LogEntry) GetMessage() []byte {
|
||||||
|
if x != nil {
|
||||||
|
return x.Message
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
var File_internal_machine_api_pb_common_proto protoreflect.FileDescriptor
|
var File_internal_machine_api_pb_common_proto protoreflect.FileDescriptor
|
||||||
|
|
||||||
var file_internal_machine_api_pb_common_proto_rawDesc = []byte{
|
var file_internal_machine_api_pb_common_proto_rawDesc = []byte{
|
||||||
@@ -350,32 +547,55 @@ var file_internal_machine_api_pb_common_proto_rawDesc = []byte{
|
|||||||
0x6e, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x62, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e,
|
0x6e, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x62, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e,
|
||||||
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x03, 0x61, 0x70, 0x69, 0x1a, 0x17, 0x67, 0x6f, 0x6f,
|
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x03, 0x61, 0x70, 0x69, 0x1a, 0x17, 0x67, 0x6f, 0x6f,
|
||||||
0x67, 0x6c, 0x65, 0x2f, 0x72, 0x70, 0x63, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2e, 0x70,
|
0x67, 0x6c, 0x65, 0x2f, 0x72, 0x70, 0x63, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2e, 0x70,
|
||||||
0x72, 0x6f, 0x74, 0x6f, 0x22, 0x66, 0x0a, 0x08, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61,
|
0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f,
|
||||||
0x12, 0x18, 0x0a, 0x07, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
|
0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e,
|
||||||
0x09, 0x52, 0x07, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72,
|
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x66, 0x0a, 0x08, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74,
|
||||||
0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72,
|
0x61, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x18, 0x01, 0x20, 0x01,
|
||||||
0x12, 0x2a, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b,
|
0x28, 0x09, 0x52, 0x07, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x65,
|
||||||
0x32, 0x12, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x74,
|
0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f,
|
||||||
0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x32, 0x0a, 0x05,
|
0x72, 0x12, 0x2a, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28,
|
||||||
0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x29, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74,
|
0x0b, 0x32, 0x12, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x53,
|
||||||
0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4d, 0x65,
|
0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x32, 0x0a,
|
||||||
0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61,
|
0x05, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x29, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61,
|
||||||
0x22, 0x37, 0x0a, 0x0d, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
|
0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4d,
|
||||||
0x65, 0x12, 0x26, 0x0a, 0x08, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x18, 0x01, 0x20,
|
0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74,
|
||||||
0x03, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x52,
|
0x61, 0x22, 0x37, 0x0a, 0x0d, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
|
||||||
0x08, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x22, 0x14, 0x0a, 0x02, 0x49, 0x50, 0x12,
|
0x73, 0x65, 0x12, 0x26, 0x0a, 0x08, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x18, 0x01,
|
||||||
0x0e, 0x0a, 0x02, 0x69, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x70, 0x22,
|
0x20, 0x03, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79,
|
||||||
0x35, 0x0a, 0x06, 0x49, 0x50, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x17, 0x0a, 0x02, 0x69, 0x70, 0x18,
|
0x52, 0x08, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x22, 0x14, 0x0a, 0x02, 0x49, 0x50,
|
||||||
0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x07, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x49, 0x50, 0x52, 0x02,
|
0x12, 0x0e, 0x0a, 0x02, 0x69, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x70,
|
||||||
0x69, 0x70, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d,
|
0x22, 0x35, 0x0a, 0x06, 0x49, 0x50, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x17, 0x0a, 0x02, 0x69, 0x70,
|
||||||
0x52, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x22, 0x37, 0x0a, 0x08, 0x49, 0x50, 0x50, 0x72, 0x65, 0x66,
|
0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x07, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x49, 0x50, 0x52,
|
||||||
0x69, 0x78, 0x12, 0x17, 0x0a, 0x02, 0x69, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x07,
|
0x02, 0x69, 0x70, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28,
|
||||||
0x2e, 0x61, 0x70, 0x69, 0x2e, 0x49, 0x50, 0x52, 0x02, 0x69, 0x70, 0x12, 0x12, 0x0a, 0x04, 0x62,
|
0x0d, 0x52, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x22, 0x37, 0x0a, 0x08, 0x49, 0x50, 0x50, 0x72, 0x65,
|
||||||
0x69, 0x74, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x62, 0x69, 0x74, 0x73, 0x42,
|
0x66, 0x69, 0x78, 0x12, 0x17, 0x0a, 0x02, 0x69, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32,
|
||||||
0x37, 0x5a, 0x35, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x73,
|
0x07, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x49, 0x50, 0x52, 0x02, 0x69, 0x70, 0x12, 0x12, 0x0a, 0x04,
|
||||||
0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x6b, 0x69, 0x2f, 0x75, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64,
|
0x62, 0x69, 0x74, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x62, 0x69, 0x74, 0x73,
|
||||||
0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e,
|
0x22, 0x75, 0x0a, 0x0b, 0x4c, 0x6f, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12,
|
||||||
0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12,
|
||||||
|
0x16, 0x0a, 0x06, 0x66, 0x6f, 0x6c, 0x6c, 0x6f, 0x77, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52,
|
||||||
|
0x06, 0x66, 0x6f, 0x6c, 0x6c, 0x6f, 0x77, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x61, 0x69, 0x6c, 0x18,
|
||||||
|
0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x74, 0x61, 0x69, 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x73,
|
||||||
|
0x69, 0x6e, 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x69, 0x6e, 0x63,
|
||||||
|
0x65, 0x12, 0x14, 0x0a, 0x05, 0x75, 0x6e, 0x74, 0x69, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09,
|
||||||
|
0x52, 0x05, 0x75, 0x6e, 0x74, 0x69, 0x6c, 0x22, 0xd2, 0x01, 0x0a, 0x08, 0x4c, 0x6f, 0x67, 0x45,
|
||||||
|
0x6e, 0x74, 0x72, 0x79, 0x12, 0x30, 0x0a, 0x06, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x18, 0x01,
|
||||||
|
0x20, 0x01, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x6f, 0x67, 0x45, 0x6e,
|
||||||
|
0x74, 0x72, 0x79, 0x2e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x54, 0x79, 0x70, 0x65, 0x52, 0x06,
|
||||||
|
0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x38, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74,
|
||||||
|
0x61, 0x6d, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
|
||||||
|
0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65,
|
||||||
|
0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70,
|
||||||
|
0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28,
|
||||||
|
0x0c, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x40, 0x0a, 0x0a, 0x53, 0x74,
|
||||||
|
0x72, 0x65, 0x61, 0x6d, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e,
|
||||||
|
0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x54, 0x44, 0x4f, 0x55, 0x54, 0x10,
|
||||||
|
0x01, 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x54, 0x44, 0x45, 0x52, 0x52, 0x10, 0x02, 0x12, 0x0d, 0x0a,
|
||||||
|
0x09, 0x48, 0x45, 0x41, 0x52, 0x54, 0x42, 0x45, 0x41, 0x54, 0x10, 0x03, 0x42, 0x37, 0x5a, 0x35,
|
||||||
|
0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x73, 0x76, 0x69, 0x64,
|
||||||
|
0x65, 0x72, 0x73, 0x6b, 0x69, 0x2f, 0x75, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2f, 0x69, 0x6e,
|
||||||
|
0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x2f, 0x61,
|
||||||
|
0x70, 0x69, 0x2f, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -390,27 +610,34 @@ func file_internal_machine_api_pb_common_proto_rawDescGZIP() []byte {
|
|||||||
return file_internal_machine_api_pb_common_proto_rawDescData
|
return file_internal_machine_api_pb_common_proto_rawDescData
|
||||||
}
|
}
|
||||||
|
|
||||||
var file_internal_machine_api_pb_common_proto_msgTypes = make([]protoimpl.MessageInfo, 6)
|
var file_internal_machine_api_pb_common_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
|
||||||
|
var file_internal_machine_api_pb_common_proto_msgTypes = make([]protoimpl.MessageInfo, 8)
|
||||||
var file_internal_machine_api_pb_common_proto_goTypes = []any{
|
var file_internal_machine_api_pb_common_proto_goTypes = []any{
|
||||||
(*Metadata)(nil), // 0: api.Metadata
|
(LogEntry_StreamType)(0), // 0: api.LogEntry.StreamType
|
||||||
(*Empty)(nil), // 1: api.Empty
|
(*Metadata)(nil), // 1: api.Metadata
|
||||||
(*EmptyResponse)(nil), // 2: api.EmptyResponse
|
(*Empty)(nil), // 2: api.Empty
|
||||||
(*IP)(nil), // 3: api.IP
|
(*EmptyResponse)(nil), // 3: api.EmptyResponse
|
||||||
(*IPPort)(nil), // 4: api.IPPort
|
(*IP)(nil), // 4: api.IP
|
||||||
(*IPPrefix)(nil), // 5: api.IPPrefix
|
(*IPPort)(nil), // 5: api.IPPort
|
||||||
(*status.Status)(nil), // 6: google.rpc.Status
|
(*IPPrefix)(nil), // 6: api.IPPrefix
|
||||||
|
(*LogsRequest)(nil), // 7: api.LogsRequest
|
||||||
|
(*LogEntry)(nil), // 8: api.LogEntry
|
||||||
|
(*status.Status)(nil), // 9: google.rpc.Status
|
||||||
|
(*timestamppb.Timestamp)(nil), // 10: google.protobuf.Timestamp
|
||||||
}
|
}
|
||||||
var file_internal_machine_api_pb_common_proto_depIdxs = []int32{
|
var file_internal_machine_api_pb_common_proto_depIdxs = []int32{
|
||||||
6, // 0: api.Metadata.status:type_name -> google.rpc.Status
|
9, // 0: api.Metadata.status:type_name -> google.rpc.Status
|
||||||
0, // 1: api.Empty.metadata:type_name -> api.Metadata
|
1, // 1: api.Empty.metadata:type_name -> api.Metadata
|
||||||
1, // 2: api.EmptyResponse.messages:type_name -> api.Empty
|
2, // 2: api.EmptyResponse.messages:type_name -> api.Empty
|
||||||
3, // 3: api.IPPort.ip:type_name -> api.IP
|
4, // 3: api.IPPort.ip:type_name -> api.IP
|
||||||
3, // 4: api.IPPrefix.ip:type_name -> api.IP
|
4, // 4: api.IPPrefix.ip:type_name -> api.IP
|
||||||
5, // [5:5] is the sub-list for method output_type
|
0, // 5: api.LogEntry.stream:type_name -> api.LogEntry.StreamType
|
||||||
5, // [5:5] is the sub-list for method input_type
|
10, // 6: api.LogEntry.timestamp:type_name -> google.protobuf.Timestamp
|
||||||
5, // [5:5] is the sub-list for extension type_name
|
7, // [7:7] is the sub-list for method output_type
|
||||||
5, // [5:5] is the sub-list for extension extendee
|
7, // [7:7] is the sub-list for method input_type
|
||||||
0, // [0:5] is the sub-list for field type_name
|
7, // [7:7] is the sub-list for extension type_name
|
||||||
|
7, // [7:7] is the sub-list for extension extendee
|
||||||
|
0, // [0:7] is the sub-list for field type_name
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() { file_internal_machine_api_pb_common_proto_init() }
|
func init() { file_internal_machine_api_pb_common_proto_init() }
|
||||||
@@ -491,19 +718,44 @@ func file_internal_machine_api_pb_common_proto_init() {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
file_internal_machine_api_pb_common_proto_msgTypes[6].Exporter = func(v any, i int) any {
|
||||||
|
switch v := v.(*LogsRequest); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file_internal_machine_api_pb_common_proto_msgTypes[7].Exporter = func(v any, i int) any {
|
||||||
|
switch v := v.(*LogEntry); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
type x struct{}
|
type x struct{}
|
||||||
out := protoimpl.TypeBuilder{
|
out := protoimpl.TypeBuilder{
|
||||||
File: protoimpl.DescBuilder{
|
File: protoimpl.DescBuilder{
|
||||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||||
RawDescriptor: file_internal_machine_api_pb_common_proto_rawDesc,
|
RawDescriptor: file_internal_machine_api_pb_common_proto_rawDesc,
|
||||||
NumEnums: 0,
|
NumEnums: 1,
|
||||||
NumMessages: 6,
|
NumMessages: 8,
|
||||||
NumExtensions: 0,
|
NumExtensions: 0,
|
||||||
NumServices: 0,
|
NumServices: 0,
|
||||||
},
|
},
|
||||||
GoTypes: file_internal_machine_api_pb_common_proto_goTypes,
|
GoTypes: file_internal_machine_api_pb_common_proto_goTypes,
|
||||||
DependencyIndexes: file_internal_machine_api_pb_common_proto_depIdxs,
|
DependencyIndexes: file_internal_machine_api_pb_common_proto_depIdxs,
|
||||||
|
EnumInfos: file_internal_machine_api_pb_common_proto_enumTypes,
|
||||||
MessageInfos: file_internal_machine_api_pb_common_proto_msgTypes,
|
MessageInfos: file_internal_machine_api_pb_common_proto_msgTypes,
|
||||||
}.Build()
|
}.Build()
|
||||||
File_internal_machine_api_pb_common_proto = out.File
|
File_internal_machine_api_pb_common_proto = out.File
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ option go_package = "github.com/psviderski/uncloud/internal/machine/api/pb";
|
|||||||
|
|
||||||
// Vendored at internal/machine/api/vendor/google/rpc/status.proto.
|
// Vendored at internal/machine/api/vendor/google/rpc/status.proto.
|
||||||
import "google/rpc/status.proto";
|
import "google/rpc/status.proto";
|
||||||
|
import "google/protobuf/timestamp.proto";
|
||||||
|
|
||||||
// Common metadata message nested in all reply message types, injected by the gRPC proxy to provide information
|
// Common metadata message nested in all reply message types, injected by the gRPC proxy to provide information
|
||||||
// about the machine that responded to the request.
|
// about the machine that responded to the request.
|
||||||
@@ -42,3 +43,25 @@ message IPPrefix {
|
|||||||
IP ip = 1;
|
IP ip = 1;
|
||||||
uint32 bits = 2;
|
uint32 bits = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message LogsRequest {
|
||||||
|
string id = 1;
|
||||||
|
// Options for logs retrieval.
|
||||||
|
bool follow = 2;
|
||||||
|
int32 tail = 3; // -1 means all
|
||||||
|
string since = 4; // https://www.rfc-editor.org/rfc/rfc3339.html timestamp or Go duration string
|
||||||
|
string until = 5; // https://www.rfc-editor.org/rfc/rfc3339.html timestamp or Go duration string
|
||||||
|
}
|
||||||
|
|
||||||
|
message LogEntry {
|
||||||
|
enum StreamType {
|
||||||
|
UNKNOWN = 0;
|
||||||
|
STDOUT = 1;
|
||||||
|
STDERR = 2;
|
||||||
|
HEARTBEAT = 3;
|
||||||
|
}
|
||||||
|
StreamType stream = 1;
|
||||||
|
google.protobuf.Timestamp timestamp = 2;
|
||||||
|
// Log line content. Empty for heartbeat entries.
|
||||||
|
bytes message = 3;
|
||||||
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -5,7 +5,6 @@ package api;
|
|||||||
option go_package = "github.com/psviderski/uncloud/internal/machine/api/pb";
|
option go_package = "github.com/psviderski/uncloud/internal/machine/api/pb";
|
||||||
|
|
||||||
import "google/protobuf/empty.proto";
|
import "google/protobuf/empty.proto";
|
||||||
import "google/protobuf/timestamp.proto";
|
|
||||||
import "internal/machine/api/pb/common.proto";
|
import "internal/machine/api/pb/common.proto";
|
||||||
|
|
||||||
service Docker {
|
service Docker {
|
||||||
@@ -17,7 +16,7 @@ service Docker {
|
|||||||
rpc RemoveContainer(RemoveContainerRequest) returns (google.protobuf.Empty);
|
rpc RemoveContainer(RemoveContainerRequest) returns (google.protobuf.Empty);
|
||||||
|
|
||||||
rpc ExecContainer(stream ExecContainerRequest) returns (stream ExecContainerResponse);
|
rpc ExecContainer(stream ExecContainerRequest) returns (stream ExecContainerResponse);
|
||||||
rpc ContainerLogs(ContainerLogsRequest) returns (stream ContainerLogEntry);
|
rpc ContainerLogs(LogsRequest) returns (stream LogEntry);
|
||||||
|
|
||||||
rpc PullImage(PullImageRequest) returns (stream JSONMessage);
|
rpc PullImage(PullImageRequest) returns (stream JSONMessage);
|
||||||
rpc InspectImage(InspectImageRequest) returns (InspectImageResponse);
|
rpc InspectImage(InspectImageRequest) returns (InspectImageResponse);
|
||||||
@@ -132,28 +131,6 @@ message ExecContainerResponse {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
message ContainerLogsRequest {
|
|
||||||
string container_id = 1;
|
|
||||||
// Options for logs retrieval.
|
|
||||||
bool follow = 2;
|
|
||||||
int32 tail = 3; // -1 means all
|
|
||||||
string since = 4; // https://www.rfc-editor.org/rfc/rfc3339.html timestamp or Go duration string
|
|
||||||
string until = 5; // https://www.rfc-editor.org/rfc/rfc3339.html timestamp or Go duration string
|
|
||||||
}
|
|
||||||
|
|
||||||
message ContainerLogEntry {
|
|
||||||
enum StreamType {
|
|
||||||
UNKNOWN = 0;
|
|
||||||
STDOUT = 1;
|
|
||||||
STDERR = 2;
|
|
||||||
HEARTBEAT = 3;
|
|
||||||
}
|
|
||||||
StreamType stream = 1;
|
|
||||||
google.protobuf.Timestamp timestamp = 2;
|
|
||||||
// Log line content. Empty for heartbeat entries.
|
|
||||||
bytes message = 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
message PullImageRequest {
|
message PullImageRequest {
|
||||||
string image = 1;
|
string image = 1;
|
||||||
// JSON serialised image.PullOptions.
|
// JSON serialised image.PullOptions.
|
||||||
@@ -251,6 +228,15 @@ message CreateServiceContainerRequest {
|
|||||||
// JSON serialised api.ServiceSpec.
|
// JSON serialised api.ServiceSpec.
|
||||||
bytes service_spec = 2;
|
bytes service_spec = 2;
|
||||||
string container_name = 3;
|
string container_name = 3;
|
||||||
|
|
||||||
|
// ContainerType specifies the purpose of the container being created.
|
||||||
|
enum ContainerType {
|
||||||
|
// SERVICE is the default type for long-running service containers.
|
||||||
|
SERVICE = 0;
|
||||||
|
// PRE_DEPLOY is a one-shot container for a pre-deploy hook to run before service deployment.
|
||||||
|
PRE_DEPLOY = 1;
|
||||||
|
}
|
||||||
|
ContainerType container_type = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
message ServiceContainer {
|
message ServiceContainer {
|
||||||
@@ -275,4 +261,6 @@ message ListServiceContainersResponse {
|
|||||||
message MachineServiceContainers {
|
message MachineServiceContainers {
|
||||||
Metadata metadata = 1;
|
Metadata metadata = 1;
|
||||||
repeated ServiceContainer containers = 2;
|
repeated ServiceContainer containers = 2;
|
||||||
|
// One-shot containers for deployment hooks (e.g. pre-deploy).
|
||||||
|
repeated ServiceContainer hook_containers = 3;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ type DockerClient interface {
|
|||||||
ListContainers(ctx context.Context, in *ListContainersRequest, opts ...grpc.CallOption) (*ListContainersResponse, error)
|
ListContainers(ctx context.Context, in *ListContainersRequest, opts ...grpc.CallOption) (*ListContainersResponse, error)
|
||||||
RemoveContainer(ctx context.Context, in *RemoveContainerRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)
|
RemoveContainer(ctx context.Context, in *RemoveContainerRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)
|
||||||
ExecContainer(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[ExecContainerRequest, ExecContainerResponse], error)
|
ExecContainer(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[ExecContainerRequest, ExecContainerResponse], error)
|
||||||
ContainerLogs(ctx context.Context, in *ContainerLogsRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[ContainerLogEntry], error)
|
ContainerLogs(ctx context.Context, in *LogsRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[LogEntry], error)
|
||||||
PullImage(ctx context.Context, in *PullImageRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[JSONMessage], error)
|
PullImage(ctx context.Context, in *PullImageRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[JSONMessage], error)
|
||||||
InspectImage(ctx context.Context, in *InspectImageRequest, opts ...grpc.CallOption) (*InspectImageResponse, error)
|
InspectImage(ctx context.Context, in *InspectImageRequest, opts ...grpc.CallOption) (*InspectImageResponse, error)
|
||||||
// InspectRemoteImage returns the image metadata for an image in a remote registry using the machine's
|
// InspectRemoteImage returns the image metadata for an image in a remote registry using the machine's
|
||||||
@@ -149,13 +149,13 @@ func (c *dockerClient) ExecContainer(ctx context.Context, opts ...grpc.CallOptio
|
|||||||
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||||
type Docker_ExecContainerClient = grpc.BidiStreamingClient[ExecContainerRequest, ExecContainerResponse]
|
type Docker_ExecContainerClient = grpc.BidiStreamingClient[ExecContainerRequest, ExecContainerResponse]
|
||||||
|
|
||||||
func (c *dockerClient) ContainerLogs(ctx context.Context, in *ContainerLogsRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[ContainerLogEntry], error) {
|
func (c *dockerClient) ContainerLogs(ctx context.Context, in *LogsRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[LogEntry], error) {
|
||||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
stream, err := c.cc.NewStream(ctx, &Docker_ServiceDesc.Streams[1], Docker_ContainerLogs_FullMethodName, cOpts...)
|
stream, err := c.cc.NewStream(ctx, &Docker_ServiceDesc.Streams[1], Docker_ContainerLogs_FullMethodName, cOpts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
x := &grpc.GenericClientStream[ContainerLogsRequest, ContainerLogEntry]{ClientStream: stream}
|
x := &grpc.GenericClientStream[LogsRequest, LogEntry]{ClientStream: stream}
|
||||||
if err := x.ClientStream.SendMsg(in); err != nil {
|
if err := x.ClientStream.SendMsg(in); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -166,7 +166,7 @@ func (c *dockerClient) ContainerLogs(ctx context.Context, in *ContainerLogsReque
|
|||||||
}
|
}
|
||||||
|
|
||||||
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||||
type Docker_ContainerLogsClient = grpc.ServerStreamingClient[ContainerLogEntry]
|
type Docker_ContainerLogsClient = grpc.ServerStreamingClient[LogEntry]
|
||||||
|
|
||||||
func (c *dockerClient) PullImage(ctx context.Context, in *PullImageRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[JSONMessage], error) {
|
func (c *dockerClient) PullImage(ctx context.Context, in *PullImageRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[JSONMessage], error) {
|
||||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
@@ -298,7 +298,7 @@ type DockerServer interface {
|
|||||||
ListContainers(context.Context, *ListContainersRequest) (*ListContainersResponse, error)
|
ListContainers(context.Context, *ListContainersRequest) (*ListContainersResponse, error)
|
||||||
RemoveContainer(context.Context, *RemoveContainerRequest) (*emptypb.Empty, error)
|
RemoveContainer(context.Context, *RemoveContainerRequest) (*emptypb.Empty, error)
|
||||||
ExecContainer(grpc.BidiStreamingServer[ExecContainerRequest, ExecContainerResponse]) error
|
ExecContainer(grpc.BidiStreamingServer[ExecContainerRequest, ExecContainerResponse]) error
|
||||||
ContainerLogs(*ContainerLogsRequest, grpc.ServerStreamingServer[ContainerLogEntry]) error
|
ContainerLogs(*LogsRequest, grpc.ServerStreamingServer[LogEntry]) error
|
||||||
PullImage(*PullImageRequest, grpc.ServerStreamingServer[JSONMessage]) error
|
PullImage(*PullImageRequest, grpc.ServerStreamingServer[JSONMessage]) error
|
||||||
InspectImage(context.Context, *InspectImageRequest) (*InspectImageResponse, error)
|
InspectImage(context.Context, *InspectImageRequest) (*InspectImageResponse, error)
|
||||||
// InspectRemoteImage returns the image metadata for an image in a remote registry using the machine's
|
// InspectRemoteImage returns the image metadata for an image in a remote registry using the machine's
|
||||||
@@ -343,7 +343,7 @@ func (UnimplementedDockerServer) RemoveContainer(context.Context, *RemoveContain
|
|||||||
func (UnimplementedDockerServer) ExecContainer(grpc.BidiStreamingServer[ExecContainerRequest, ExecContainerResponse]) error {
|
func (UnimplementedDockerServer) ExecContainer(grpc.BidiStreamingServer[ExecContainerRequest, ExecContainerResponse]) error {
|
||||||
return status.Errorf(codes.Unimplemented, "method ExecContainer not implemented")
|
return status.Errorf(codes.Unimplemented, "method ExecContainer not implemented")
|
||||||
}
|
}
|
||||||
func (UnimplementedDockerServer) ContainerLogs(*ContainerLogsRequest, grpc.ServerStreamingServer[ContainerLogEntry]) error {
|
func (UnimplementedDockerServer) ContainerLogs(*LogsRequest, grpc.ServerStreamingServer[LogEntry]) error {
|
||||||
return status.Errorf(codes.Unimplemented, "method ContainerLogs not implemented")
|
return status.Errorf(codes.Unimplemented, "method ContainerLogs not implemented")
|
||||||
}
|
}
|
||||||
func (UnimplementedDockerServer) PullImage(*PullImageRequest, grpc.ServerStreamingServer[JSONMessage]) error {
|
func (UnimplementedDockerServer) PullImage(*PullImageRequest, grpc.ServerStreamingServer[JSONMessage]) error {
|
||||||
@@ -516,15 +516,15 @@ func _Docker_ExecContainer_Handler(srv interface{}, stream grpc.ServerStream) er
|
|||||||
type Docker_ExecContainerServer = grpc.BidiStreamingServer[ExecContainerRequest, ExecContainerResponse]
|
type Docker_ExecContainerServer = grpc.BidiStreamingServer[ExecContainerRequest, ExecContainerResponse]
|
||||||
|
|
||||||
func _Docker_ContainerLogs_Handler(srv interface{}, stream grpc.ServerStream) error {
|
func _Docker_ContainerLogs_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||||
m := new(ContainerLogsRequest)
|
m := new(LogsRequest)
|
||||||
if err := stream.RecvMsg(m); err != nil {
|
if err := stream.RecvMsg(m); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return srv.(DockerServer).ContainerLogs(m, &grpc.GenericServerStream[ContainerLogsRequest, ContainerLogEntry]{ServerStream: stream})
|
return srv.(DockerServer).ContainerLogs(m, &grpc.GenericServerStream[LogsRequest, LogEntry]{ServerStream: stream})
|
||||||
}
|
}
|
||||||
|
|
||||||
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||||
type Docker_ContainerLogsServer = grpc.ServerStreamingServer[ContainerLogEntry]
|
type Docker_ContainerLogsServer = grpc.ServerStreamingServer[LogEntry]
|
||||||
|
|
||||||
func _Docker_PullImage_Handler(srv interface{}, stream grpc.ServerStream) error {
|
func _Docker_PullImage_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||||
m := new(PullImageRequest)
|
m := new(PullImageRequest)
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ package pb
|
|||||||
import (
|
import (
|
||||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||||
|
durationpb "google.golang.org/protobuf/types/known/durationpb"
|
||||||
emptypb "google.golang.org/protobuf/types/known/emptypb"
|
emptypb "google.golang.org/protobuf/types/known/emptypb"
|
||||||
timestamppb "google.golang.org/protobuf/types/known/timestamppb"
|
timestamppb "google.golang.org/protobuf/types/known/timestamppb"
|
||||||
reflect "reflect"
|
reflect "reflect"
|
||||||
@@ -495,6 +496,8 @@ type MachineDetails struct {
|
|||||||
Machine *MachineInfo `protobuf:"bytes,2,opt,name=machine,proto3" json:"machine,omitempty"`
|
Machine *MachineInfo `protobuf:"bytes,2,opt,name=machine,proto3" json:"machine,omitempty"`
|
||||||
// Current Corrosion cr-sqlite database version (Lamport timestamp) of the cluster store.
|
// Current Corrosion cr-sqlite database version (Lamport timestamp) of the cluster store.
|
||||||
StoreDbVersion int64 `protobuf:"varint,3,opt,name=store_db_version,json=storeDbVersion,proto3" json:"store_db_version,omitempty"`
|
StoreDbVersion int64 `protobuf:"varint,3,opt,name=store_db_version,json=storeDbVersion,proto3" json:"store_db_version,omitempty"`
|
||||||
|
// Round-trip times to other machines in the cluster, keyed by peer machine ID.
|
||||||
|
Rtts map[string]*RTTStats `protobuf:"bytes,4,rep,name=rtts,proto3" json:"rtts,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *MachineDetails) Reset() {
|
func (x *MachineDetails) Reset() {
|
||||||
@@ -550,6 +553,13 @@ func (x *MachineDetails) GetStoreDbVersion() int64 {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *MachineDetails) GetRtts() map[string]*RTTStats {
|
||||||
|
if x != nil {
|
||||||
|
return x.Rtts
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
type TokenResponse struct {
|
type TokenResponse struct {
|
||||||
state protoimpl.MessageState
|
state protoimpl.MessageState
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
@@ -958,6 +968,61 @@ func (x *WireGuardPeer) GetAllowedIps() []string {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type RTTStats struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
Median *durationpb.Duration `protobuf:"bytes,1,opt,name=median,proto3" json:"median,omitempty"`
|
||||||
|
StdDev *durationpb.Duration `protobuf:"bytes,2,opt,name=std_dev,json=stdDev,proto3" json:"std_dev,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *RTTStats) Reset() {
|
||||||
|
*x = RTTStats{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_internal_machine_api_pb_machine_proto_msgTypes[15]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *RTTStats) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*RTTStats) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *RTTStats) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_internal_machine_api_pb_machine_proto_msgTypes[15]
|
||||||
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use RTTStats.ProtoReflect.Descriptor instead.
|
||||||
|
func (*RTTStats) Descriptor() ([]byte, []int) {
|
||||||
|
return file_internal_machine_api_pb_machine_proto_rawDescGZIP(), []int{15}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *RTTStats) GetMedian() *durationpb.Duration {
|
||||||
|
if x != nil {
|
||||||
|
return x.Median
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *RTTStats) GetStdDev() *durationpb.Duration {
|
||||||
|
if x != nil {
|
||||||
|
return x.StdDev
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
type Service_Container struct {
|
type Service_Container struct {
|
||||||
state protoimpl.MessageState
|
state protoimpl.MessageState
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
@@ -971,7 +1036,7 @@ type Service_Container struct {
|
|||||||
func (x *Service_Container) Reset() {
|
func (x *Service_Container) Reset() {
|
||||||
*x = Service_Container{}
|
*x = Service_Container{}
|
||||||
if protoimpl.UnsafeEnabled {
|
if protoimpl.UnsafeEnabled {
|
||||||
mi := &file_internal_machine_api_pb_machine_proto_msgTypes[15]
|
mi := &file_internal_machine_api_pb_machine_proto_msgTypes[17]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
@@ -984,7 +1049,7 @@ func (x *Service_Container) String() string {
|
|||||||
func (*Service_Container) ProtoMessage() {}
|
func (*Service_Container) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *Service_Container) ProtoReflect() protoreflect.Message {
|
func (x *Service_Container) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_internal_machine_api_pb_machine_proto_msgTypes[15]
|
mi := &file_internal_machine_api_pb_machine_proto_msgTypes[17]
|
||||||
if protoimpl.UnsafeEnabled && x != nil {
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
@@ -1019,7 +1084,9 @@ var File_internal_machine_api_pb_machine_proto protoreflect.FileDescriptor
|
|||||||
var file_internal_machine_api_pb_machine_proto_rawDesc = []byte{
|
var file_internal_machine_api_pb_machine_proto_rawDesc = []byte{
|
||||||
0x0a, 0x25, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x6d, 0x61, 0x63, 0x68, 0x69,
|
0x0a, 0x25, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x6d, 0x61, 0x63, 0x68, 0x69,
|
||||||
0x6e, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x62, 0x2f, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e,
|
0x6e, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x62, 0x2f, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e,
|
||||||
0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x03, 0x61, 0x70, 0x69, 0x1a, 0x1b, 0x67, 0x6f,
|
0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x03, 0x61, 0x70, 0x69, 0x1a, 0x1e, 0x67, 0x6f,
|
||||||
|
0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x75,
|
||||||
|
0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1b, 0x67, 0x6f,
|
||||||
0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x65, 0x6d,
|
0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x65, 0x6d,
|
||||||
0x70, 0x74, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
|
0x70, 0x74, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
|
||||||
0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73,
|
0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73,
|
||||||
@@ -1086,7 +1153,7 @@ var file_internal_machine_api_pb_machine_proto_rawDesc = []byte{
|
|||||||
0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x08, 0x6d, 0x61, 0x63,
|
0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x08, 0x6d, 0x61, 0x63,
|
||||||
0x68, 0x69, 0x6e, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x61, 0x70,
|
0x68, 0x69, 0x6e, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x61, 0x70,
|
||||||
0x69, 0x2e, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73,
|
0x69, 0x2e, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73,
|
||||||
0x52, 0x08, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x73, 0x22, 0x91, 0x01, 0x0a, 0x0e, 0x4d,
|
0x52, 0x08, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x73, 0x22, 0x8c, 0x02, 0x0a, 0x0e, 0x4d,
|
||||||
0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x29, 0x0a,
|
0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x29, 0x0a,
|
||||||
0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32,
|
0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32,
|
||||||
0x0d, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x08,
|
0x0d, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x08,
|
||||||
@@ -1095,96 +1162,114 @@ var file_internal_machine_api_pb_machine_proto_rawDesc = []byte{
|
|||||||
0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x07, 0x6d, 0x61, 0x63,
|
0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x07, 0x6d, 0x61, 0x63,
|
||||||
0x68, 0x69, 0x6e, 0x65, 0x12, 0x28, 0x0a, 0x10, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x64, 0x62,
|
0x68, 0x69, 0x6e, 0x65, 0x12, 0x28, 0x0a, 0x10, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x64, 0x62,
|
||||||
0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0e,
|
0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0e,
|
||||||
0x73, 0x74, 0x6f, 0x72, 0x65, 0x44, 0x62, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x25,
|
0x73, 0x74, 0x6f, 0x72, 0x65, 0x44, 0x62, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x31,
|
||||||
0x0a, 0x0d, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
|
0x0a, 0x04, 0x72, 0x74, 0x74, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x61,
|
||||||
0x14, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05,
|
0x70, 0x69, 0x2e, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c,
|
||||||
0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x0e, 0x0a, 0x0c, 0x52, 0x65, 0x73, 0x65, 0x74, 0x52, 0x65,
|
0x73, 0x2e, 0x52, 0x74, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x04, 0x72, 0x74, 0x74,
|
||||||
0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xc3, 0x01, 0x0a, 0x07, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63,
|
0x73, 0x1a, 0x46, 0x0a, 0x09, 0x52, 0x74, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10,
|
||||||
0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69,
|
0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79,
|
||||||
0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
|
0x12, 0x23, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32,
|
||||||
0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x03, 0x20,
|
0x0d, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x52, 0x54, 0x54, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x05,
|
||||||
0x01, 0x28, 0x09, 0x52, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x12, 0x36, 0x0a, 0x0a, 0x63, 0x6f, 0x6e,
|
0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x25, 0x0a, 0x0d, 0x54, 0x6f, 0x6b,
|
||||||
0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e,
|
0x65, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f,
|
||||||
0x61, 0x70, 0x69, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x43, 0x6f, 0x6e, 0x74,
|
0x6b, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e,
|
||||||
0x61, 0x69, 0x6e, 0x65, 0x72, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72,
|
0x22, 0x0e, 0x0a, 0x0c, 0x52, 0x65, 0x73, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
|
||||||
0x73, 0x1a, 0x48, 0x0a, 0x09, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x12, 0x1d,
|
0x22, 0xc3, 0x01, 0x0a, 0x07, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x0e, 0x0a, 0x02,
|
||||||
0x0a, 0x0a, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01,
|
0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04,
|
||||||
0x28, 0x09, 0x52, 0x09, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x49, 0x64, 0x12, 0x1c, 0x0a,
|
0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65,
|
||||||
0x09, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c,
|
0x12, 0x12, 0x0a, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04,
|
||||||
0x52, 0x09, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x22, 0x27, 0x0a, 0x15, 0x49,
|
0x6d, 0x6f, 0x64, 0x65, 0x12, 0x36, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65,
|
||||||
0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71,
|
0x72, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x53,
|
||||||
0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
|
0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72,
|
||||||
0x52, 0x02, 0x69, 0x64, 0x22, 0x40, 0x0a, 0x16, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x53,
|
0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x1a, 0x48, 0x0a, 0x09,
|
||||||
0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x26,
|
0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x61, 0x63,
|
||||||
0x0a, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32,
|
0x68, 0x69, 0x6e, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6d,
|
||||||
0x0c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x07, 0x73,
|
0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x49, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x74,
|
||||||
0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x22, 0xb2, 0x01, 0x0a, 0x1f, 0x49, 0x6e, 0x73, 0x70, 0x65,
|
0x61, 0x69, 0x6e, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x63, 0x6f, 0x6e,
|
||||||
0x63, 0x74, 0x57, 0x69, 0x72, 0x65, 0x47, 0x75, 0x61, 0x72, 0x64, 0x4e, 0x65, 0x74, 0x77, 0x6f,
|
0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x22, 0x27, 0x0a, 0x15, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63,
|
||||||
0x72, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x69, 0x6e,
|
0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12,
|
||||||
0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01,
|
0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22,
|
||||||
0x28, 0x09, 0x52, 0x0d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x4e, 0x61, 0x6d,
|
0x40, 0x0a, 0x16, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63,
|
||||||
0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18,
|
0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x26, 0x0a, 0x07, 0x73, 0x65, 0x72,
|
||||||
0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79,
|
0x76, 0x69, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x61, 0x70, 0x69,
|
||||||
0x12, 0x1f, 0x0a, 0x0b, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18,
|
0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63,
|
||||||
0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x50, 0x6f, 0x72,
|
0x65, 0x22, 0xb2, 0x01, 0x0a, 0x1f, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x57, 0x69, 0x72,
|
||||||
0x74, 0x12, 0x28, 0x0a, 0x05, 0x70, 0x65, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b,
|
0x65, 0x47, 0x75, 0x61, 0x72, 0x64, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x65, 0x73,
|
||||||
0x32, 0x12, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x57, 0x69, 0x72, 0x65, 0x47, 0x75, 0x61, 0x72, 0x64,
|
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61,
|
||||||
0x50, 0x65, 0x65, 0x72, 0x52, 0x05, 0x70, 0x65, 0x65, 0x72, 0x73, 0x22, 0x83, 0x02, 0x0a, 0x0d,
|
0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x69,
|
||||||
0x57, 0x69, 0x72, 0x65, 0x47, 0x75, 0x61, 0x72, 0x64, 0x50, 0x65, 0x65, 0x72, 0x12, 0x1d, 0x0a,
|
0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1d, 0x0a, 0x0a,
|
||||||
0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28,
|
0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c,
|
||||||
0x0c, 0x52, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x1a, 0x0a, 0x08,
|
0x52, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x1f, 0x0a, 0x0b, 0x6c,
|
||||||
0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08,
|
0x69, 0x73, 0x74, 0x65, 0x6e, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05,
|
||||||
0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x4a, 0x0a, 0x13, 0x6c, 0x61, 0x73, 0x74,
|
0x52, 0x0a, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x28, 0x0a, 0x05,
|
||||||
0x5f, 0x68, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18,
|
0x70, 0x65, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x61, 0x70,
|
||||||
0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70,
|
0x69, 0x2e, 0x57, 0x69, 0x72, 0x65, 0x47, 0x75, 0x61, 0x72, 0x64, 0x50, 0x65, 0x65, 0x72, 0x52,
|
||||||
0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d,
|
0x05, 0x70, 0x65, 0x65, 0x72, 0x73, 0x22, 0x83, 0x02, 0x0a, 0x0d, 0x57, 0x69, 0x72, 0x65, 0x47,
|
||||||
0x70, 0x52, 0x11, 0x6c, 0x61, 0x73, 0x74, 0x48, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65,
|
0x75, 0x61, 0x72, 0x64, 0x50, 0x65, 0x65, 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x75, 0x62, 0x6c,
|
||||||
0x54, 0x69, 0x6d, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x5f,
|
0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x70, 0x75,
|
||||||
0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x72, 0x65, 0x63,
|
0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f,
|
||||||
0x65, 0x69, 0x76, 0x65, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x74, 0x72, 0x61,
|
0x69, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f,
|
||||||
0x6e, 0x73, 0x6d, 0x69, 0x74, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28,
|
0x69, 0x6e, 0x74, 0x12, 0x4a, 0x0a, 0x13, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x68, 0x61, 0x6e, 0x64,
|
||||||
0x03, 0x52, 0x0d, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x6d, 0x69, 0x74, 0x42, 0x79, 0x74, 0x65, 0x73,
|
0x73, 0x68, 0x61, 0x6b, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b,
|
||||||
0x12, 0x1f, 0x0a, 0x0b, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x5f, 0x69, 0x70, 0x73, 0x18,
|
0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
|
||||||
0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x49, 0x70,
|
0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x11, 0x6c, 0x61,
|
||||||
0x73, 0x32, 0xe3, 0x04, 0x0a, 0x07, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x12, 0x4d, 0x0a,
|
0x73, 0x74, 0x48, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12,
|
||||||
0x12, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x50, 0x72, 0x65, 0x72, 0x65, 0x71, 0x75, 0x69, 0x73, 0x69,
|
0x23, 0x0a, 0x0d, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73,
|
||||||
0x74, 0x65, 0x73, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,
|
0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x42,
|
||||||
0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1f, 0x2e, 0x61, 0x70,
|
0x79, 0x74, 0x65, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x6d, 0x69, 0x74,
|
||||||
0x69, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x50, 0x72, 0x65, 0x72, 0x65, 0x71, 0x75, 0x69, 0x73,
|
0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x74, 0x72,
|
||||||
0x69, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x40, 0x0a, 0x0b,
|
0x61, 0x6e, 0x73, 0x6d, 0x69, 0x74, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x61,
|
||||||
0x49, 0x6e, 0x69, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x12, 0x17, 0x2e, 0x61, 0x70,
|
0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x5f, 0x69, 0x70, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09,
|
||||||
0x69, 0x2e, 0x49, 0x6e, 0x69, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x71,
|
0x52, 0x0a, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x49, 0x70, 0x73, 0x22, 0x71, 0x0a, 0x08,
|
||||||
0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x49, 0x6e, 0x69, 0x74, 0x43,
|
0x52, 0x54, 0x54, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x31, 0x0a, 0x06, 0x6d, 0x65, 0x64, 0x69,
|
||||||
0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3e,
|
0x61, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
|
||||||
0x0a, 0x0b, 0x4a, 0x6f, 0x69, 0x6e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x12, 0x17, 0x2e,
|
0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74,
|
||||||
0x61, 0x70, 0x69, 0x2e, 0x4a, 0x6f, 0x69, 0x6e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52,
|
0x69, 0x6f, 0x6e, 0x52, 0x06, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x6e, 0x12, 0x32, 0x0a, 0x07, 0x73,
|
||||||
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
|
0x74, 0x64, 0x5f, 0x64, 0x65, 0x76, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67,
|
||||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x33,
|
0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44,
|
||||||
0x0a, 0x05, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
|
0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x73, 0x74, 0x64, 0x44, 0x65, 0x76, 0x32,
|
||||||
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a,
|
0x95, 0x05, 0x0a, 0x07, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x12, 0x4d, 0x0a, 0x12, 0x43,
|
||||||
0x12, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f,
|
0x68, 0x65, 0x63, 0x6b, 0x50, 0x72, 0x65, 0x72, 0x65, 0x71, 0x75, 0x69, 0x73, 0x69, 0x74, 0x65,
|
||||||
0x6e, 0x73, 0x65, 0x12, 0x33, 0x0a, 0x07, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x12, 0x16,
|
0x73, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
||||||
0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
|
0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1f, 0x2e, 0x61, 0x70, 0x69, 0x2e,
|
||||||
0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x10, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4d, 0x61, 0x63,
|
0x43, 0x68, 0x65, 0x63, 0x6b, 0x50, 0x72, 0x65, 0x72, 0x65, 0x71, 0x75, 0x69, 0x73, 0x69, 0x74,
|
||||||
0x68, 0x69, 0x6e, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x45, 0x0a, 0x0e, 0x49, 0x6e, 0x73, 0x70,
|
0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x40, 0x0a, 0x0b, 0x49, 0x6e,
|
||||||
0x65, 0x63, 0x74, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f,
|
0x69, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x12, 0x17, 0x2e, 0x61, 0x70, 0x69, 0x2e,
|
||||||
0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70,
|
0x49, 0x6e, 0x69, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65,
|
||||||
0x74, 0x79, 0x1a, 0x1b, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74,
|
0x73, 0x74, 0x1a, 0x18, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x49, 0x6e, 0x69, 0x74, 0x43, 0x6c, 0x75,
|
||||||
0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
|
0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3e, 0x0a, 0x0b,
|
||||||
0x57, 0x0a, 0x17, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x57, 0x69, 0x72, 0x65, 0x47, 0x75,
|
0x4a, 0x6f, 0x69, 0x6e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x12, 0x17, 0x2e, 0x61, 0x70,
|
||||||
0x61, 0x72, 0x64, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f,
|
0x69, 0x2e, 0x4a, 0x6f, 0x69, 0x6e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x71,
|
||||||
0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70,
|
|
||||||
0x74, 0x79, 0x1a, 0x24, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74,
|
|
||||||
0x57, 0x69, 0x72, 0x65, 0x47, 0x75, 0x61, 0x72, 0x64, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b,
|
|
||||||
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x32, 0x0a, 0x05, 0x52, 0x65, 0x73, 0x65,
|
|
||||||
0x74, 0x12, 0x11, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x52, 0x65, 0x73, 0x65, 0x74, 0x52, 0x65, 0x71,
|
|
||||||
0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72,
|
0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72,
|
||||||
0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x49, 0x0a, 0x0e,
|
0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x33, 0x0a, 0x05,
|
||||||
0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x1a,
|
0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70,
|
||||||
0x2e, 0x61, 0x70, 0x69, 0x2e, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x53, 0x65, 0x72, 0x76,
|
0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x12, 0x2e,
|
||||||
0x69, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x61, 0x70, 0x69,
|
0x61, 0x70, 0x69, 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
|
||||||
0x2e, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52,
|
0x65, 0x12, 0x33, 0x0a, 0x07, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x12, 0x16, 0x2e, 0x67,
|
||||||
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x37, 0x5a, 0x35, 0x67, 0x69, 0x74, 0x68, 0x75,
|
0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45,
|
||||||
|
0x6d, 0x70, 0x74, 0x79, 0x1a, 0x10, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4d, 0x61, 0x63, 0x68, 0x69,
|
||||||
|
0x6e, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x45, 0x0a, 0x0e, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63,
|
||||||
|
0x74, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
|
||||||
|
0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79,
|
||||||
|
0x1a, 0x1b, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x4d, 0x61,
|
||||||
|
0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x57, 0x0a,
|
||||||
|
0x17, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x57, 0x69, 0x72, 0x65, 0x47, 0x75, 0x61, 0x72,
|
||||||
|
0x64, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
|
||||||
|
0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79,
|
||||||
|
0x1a, 0x24, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x57, 0x69,
|
||||||
|
0x72, 0x65, 0x47, 0x75, 0x61, 0x72, 0x64, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x65,
|
||||||
|
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x32, 0x0a, 0x05, 0x52, 0x65, 0x73, 0x65, 0x74, 0x12,
|
||||||
|
0x11, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x52, 0x65, 0x73, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65,
|
||||||
|
0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,
|
||||||
|
0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x49, 0x0a, 0x0e, 0x49, 0x6e,
|
||||||
|
0x73, 0x70, 0x65, 0x63, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x1a, 0x2e, 0x61,
|
||||||
|
0x70, 0x69, 0x2e, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63,
|
||||||
|
0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x49,
|
||||||
|
0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x73,
|
||||||
|
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x30, 0x0a, 0x0b, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65,
|
||||||
|
0x4c, 0x6f, 0x67, 0x73, 0x12, 0x10, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x6f, 0x67, 0x73, 0x52,
|
||||||
|
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0d, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x6f, 0x67,
|
||||||
|
0x45, 0x6e, 0x74, 0x72, 0x79, 0x30, 0x01, 0x42, 0x37, 0x5a, 0x35, 0x67, 0x69, 0x74, 0x68, 0x75,
|
||||||
0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x73, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x6b, 0x69,
|
0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x73, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x6b, 0x69,
|
||||||
0x2f, 0x75, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61,
|
0x2f, 0x75, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61,
|
||||||
0x6c, 0x2f, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x62,
|
0x6c, 0x2f, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x62,
|
||||||
@@ -1203,7 +1288,7 @@ func file_internal_machine_api_pb_machine_proto_rawDescGZIP() []byte {
|
|||||||
return file_internal_machine_api_pb_machine_proto_rawDescData
|
return file_internal_machine_api_pb_machine_proto_rawDescData
|
||||||
}
|
}
|
||||||
|
|
||||||
var file_internal_machine_api_pb_machine_proto_msgTypes = make([]protoimpl.MessageInfo, 16)
|
var file_internal_machine_api_pb_machine_proto_msgTypes = make([]protoimpl.MessageInfo, 18)
|
||||||
var file_internal_machine_api_pb_machine_proto_goTypes = []any{
|
var file_internal_machine_api_pb_machine_proto_goTypes = []any{
|
||||||
(*MachineInfo)(nil), // 0: api.MachineInfo
|
(*MachineInfo)(nil), // 0: api.MachineInfo
|
||||||
(*NetworkConfig)(nil), // 1: api.NetworkConfig
|
(*NetworkConfig)(nil), // 1: api.NetworkConfig
|
||||||
@@ -1220,56 +1305,67 @@ var file_internal_machine_api_pb_machine_proto_goTypes = []any{
|
|||||||
(*InspectServiceResponse)(nil), // 12: api.InspectServiceResponse
|
(*InspectServiceResponse)(nil), // 12: api.InspectServiceResponse
|
||||||
(*InspectWireGuardNetworkResponse)(nil), // 13: api.InspectWireGuardNetworkResponse
|
(*InspectWireGuardNetworkResponse)(nil), // 13: api.InspectWireGuardNetworkResponse
|
||||||
(*WireGuardPeer)(nil), // 14: api.WireGuardPeer
|
(*WireGuardPeer)(nil), // 14: api.WireGuardPeer
|
||||||
(*Service_Container)(nil), // 15: api.Service.Container
|
(*RTTStats)(nil), // 15: api.RTTStats
|
||||||
(*IP)(nil), // 16: api.IP
|
nil, // 16: api.MachineDetails.RttsEntry
|
||||||
(*IPPrefix)(nil), // 17: api.IPPrefix
|
(*Service_Container)(nil), // 17: api.Service.Container
|
||||||
(*IPPort)(nil), // 18: api.IPPort
|
(*IP)(nil), // 18: api.IP
|
||||||
(*Metadata)(nil), // 19: api.Metadata
|
(*IPPrefix)(nil), // 19: api.IPPrefix
|
||||||
(*timestamppb.Timestamp)(nil), // 20: google.protobuf.Timestamp
|
(*IPPort)(nil), // 20: api.IPPort
|
||||||
(*emptypb.Empty)(nil), // 21: google.protobuf.Empty
|
(*Metadata)(nil), // 21: api.Metadata
|
||||||
|
(*timestamppb.Timestamp)(nil), // 22: google.protobuf.Timestamp
|
||||||
|
(*durationpb.Duration)(nil), // 23: google.protobuf.Duration
|
||||||
|
(*emptypb.Empty)(nil), // 24: google.protobuf.Empty
|
||||||
|
(*LogsRequest)(nil), // 25: api.LogsRequest
|
||||||
|
(*LogEntry)(nil), // 26: api.LogEntry
|
||||||
}
|
}
|
||||||
var file_internal_machine_api_pb_machine_proto_depIdxs = []int32{
|
var file_internal_machine_api_pb_machine_proto_depIdxs = []int32{
|
||||||
1, // 0: api.MachineInfo.network:type_name -> api.NetworkConfig
|
1, // 0: api.MachineInfo.network:type_name -> api.NetworkConfig
|
||||||
16, // 1: api.MachineInfo.public_ip:type_name -> api.IP
|
18, // 1: api.MachineInfo.public_ip:type_name -> api.IP
|
||||||
17, // 2: api.NetworkConfig.subnet:type_name -> api.IPPrefix
|
19, // 2: api.NetworkConfig.subnet:type_name -> api.IPPrefix
|
||||||
16, // 3: api.NetworkConfig.management_ip:type_name -> api.IP
|
18, // 3: api.NetworkConfig.management_ip:type_name -> api.IP
|
||||||
18, // 4: api.NetworkConfig.endpoints:type_name -> api.IPPort
|
20, // 4: api.NetworkConfig.endpoints:type_name -> api.IPPort
|
||||||
17, // 5: api.InitClusterRequest.network:type_name -> api.IPPrefix
|
19, // 5: api.InitClusterRequest.network:type_name -> api.IPPrefix
|
||||||
16, // 6: api.InitClusterRequest.public_ip:type_name -> api.IP
|
18, // 6: api.InitClusterRequest.public_ip:type_name -> api.IP
|
||||||
18, // 7: api.InitClusterRequest.wireguard_endpoints:type_name -> api.IPPort
|
20, // 7: api.InitClusterRequest.wireguard_endpoints:type_name -> api.IPPort
|
||||||
0, // 8: api.InitClusterResponse.machine:type_name -> api.MachineInfo
|
0, // 8: api.InitClusterResponse.machine:type_name -> api.MachineInfo
|
||||||
0, // 9: api.JoinClusterRequest.machine:type_name -> api.MachineInfo
|
0, // 9: api.JoinClusterRequest.machine:type_name -> api.MachineInfo
|
||||||
0, // 10: api.JoinClusterRequest.other_machines:type_name -> api.MachineInfo
|
0, // 10: api.JoinClusterRequest.other_machines:type_name -> api.MachineInfo
|
||||||
7, // 11: api.InspectMachineResponse.machines:type_name -> api.MachineDetails
|
7, // 11: api.InspectMachineResponse.machines:type_name -> api.MachineDetails
|
||||||
19, // 12: api.MachineDetails.metadata:type_name -> api.Metadata
|
21, // 12: api.MachineDetails.metadata:type_name -> api.Metadata
|
||||||
0, // 13: api.MachineDetails.machine:type_name -> api.MachineInfo
|
0, // 13: api.MachineDetails.machine:type_name -> api.MachineInfo
|
||||||
15, // 14: api.Service.containers:type_name -> api.Service.Container
|
16, // 14: api.MachineDetails.rtts:type_name -> api.MachineDetails.RttsEntry
|
||||||
10, // 15: api.InspectServiceResponse.service:type_name -> api.Service
|
17, // 15: api.Service.containers:type_name -> api.Service.Container
|
||||||
14, // 16: api.InspectWireGuardNetworkResponse.peers:type_name -> api.WireGuardPeer
|
10, // 16: api.InspectServiceResponse.service:type_name -> api.Service
|
||||||
20, // 17: api.WireGuardPeer.last_handshake_time:type_name -> google.protobuf.Timestamp
|
14, // 17: api.InspectWireGuardNetworkResponse.peers:type_name -> api.WireGuardPeer
|
||||||
21, // 18: api.Machine.CheckPrerequisites:input_type -> google.protobuf.Empty
|
22, // 18: api.WireGuardPeer.last_handshake_time:type_name -> google.protobuf.Timestamp
|
||||||
3, // 19: api.Machine.InitCluster:input_type -> api.InitClusterRequest
|
23, // 19: api.RTTStats.median:type_name -> google.protobuf.Duration
|
||||||
5, // 20: api.Machine.JoinCluster:input_type -> api.JoinClusterRequest
|
23, // 20: api.RTTStats.std_dev:type_name -> google.protobuf.Duration
|
||||||
21, // 21: api.Machine.Token:input_type -> google.protobuf.Empty
|
15, // 21: api.MachineDetails.RttsEntry.value:type_name -> api.RTTStats
|
||||||
21, // 22: api.Machine.Inspect:input_type -> google.protobuf.Empty
|
24, // 22: api.Machine.CheckPrerequisites:input_type -> google.protobuf.Empty
|
||||||
21, // 23: api.Machine.InspectMachine:input_type -> google.protobuf.Empty
|
3, // 23: api.Machine.InitCluster:input_type -> api.InitClusterRequest
|
||||||
21, // 24: api.Machine.InspectWireGuardNetwork:input_type -> google.protobuf.Empty
|
5, // 24: api.Machine.JoinCluster:input_type -> api.JoinClusterRequest
|
||||||
9, // 25: api.Machine.Reset:input_type -> api.ResetRequest
|
24, // 25: api.Machine.Token:input_type -> google.protobuf.Empty
|
||||||
11, // 26: api.Machine.InspectService:input_type -> api.InspectServiceRequest
|
24, // 26: api.Machine.Inspect:input_type -> google.protobuf.Empty
|
||||||
2, // 27: api.Machine.CheckPrerequisites:output_type -> api.CheckPrerequisitesResponse
|
24, // 27: api.Machine.InspectMachine:input_type -> google.protobuf.Empty
|
||||||
4, // 28: api.Machine.InitCluster:output_type -> api.InitClusterResponse
|
24, // 28: api.Machine.InspectWireGuardNetwork:input_type -> google.protobuf.Empty
|
||||||
21, // 29: api.Machine.JoinCluster:output_type -> google.protobuf.Empty
|
9, // 29: api.Machine.Reset:input_type -> api.ResetRequest
|
||||||
8, // 30: api.Machine.Token:output_type -> api.TokenResponse
|
11, // 30: api.Machine.InspectService:input_type -> api.InspectServiceRequest
|
||||||
0, // 31: api.Machine.Inspect:output_type -> api.MachineInfo
|
25, // 31: api.Machine.MachineLogs:input_type -> api.LogsRequest
|
||||||
6, // 32: api.Machine.InspectMachine:output_type -> api.InspectMachineResponse
|
2, // 32: api.Machine.CheckPrerequisites:output_type -> api.CheckPrerequisitesResponse
|
||||||
13, // 33: api.Machine.InspectWireGuardNetwork:output_type -> api.InspectWireGuardNetworkResponse
|
4, // 33: api.Machine.InitCluster:output_type -> api.InitClusterResponse
|
||||||
21, // 34: api.Machine.Reset:output_type -> google.protobuf.Empty
|
24, // 34: api.Machine.JoinCluster:output_type -> google.protobuf.Empty
|
||||||
12, // 35: api.Machine.InspectService:output_type -> api.InspectServiceResponse
|
8, // 35: api.Machine.Token:output_type -> api.TokenResponse
|
||||||
27, // [27:36] is the sub-list for method output_type
|
0, // 36: api.Machine.Inspect:output_type -> api.MachineInfo
|
||||||
18, // [18:27] is the sub-list for method input_type
|
6, // 37: api.Machine.InspectMachine:output_type -> api.InspectMachineResponse
|
||||||
18, // [18:18] is the sub-list for extension type_name
|
13, // 38: api.Machine.InspectWireGuardNetwork:output_type -> api.InspectWireGuardNetworkResponse
|
||||||
18, // [18:18] is the sub-list for extension extendee
|
24, // 39: api.Machine.Reset:output_type -> google.protobuf.Empty
|
||||||
0, // [0:18] is the sub-list for field type_name
|
12, // 40: api.Machine.InspectService:output_type -> api.InspectServiceResponse
|
||||||
|
26, // 41: api.Machine.MachineLogs:output_type -> api.LogEntry
|
||||||
|
32, // [32:42] is the sub-list for method output_type
|
||||||
|
22, // [22:32] is the sub-list for method input_type
|
||||||
|
22, // [22:22] is the sub-list for extension type_name
|
||||||
|
22, // [22:22] is the sub-list for extension extendee
|
||||||
|
0, // [0:22] is the sub-list for field type_name
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() { file_internal_machine_api_pb_machine_proto_init() }
|
func init() { file_internal_machine_api_pb_machine_proto_init() }
|
||||||
@@ -1460,6 +1556,18 @@ func file_internal_machine_api_pb_machine_proto_init() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
file_internal_machine_api_pb_machine_proto_msgTypes[15].Exporter = func(v any, i int) any {
|
file_internal_machine_api_pb_machine_proto_msgTypes[15].Exporter = func(v any, i int) any {
|
||||||
|
switch v := v.(*RTTStats); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file_internal_machine_api_pb_machine_proto_msgTypes[17].Exporter = func(v any, i int) any {
|
||||||
switch v := v.(*Service_Container); i {
|
switch v := v.(*Service_Container); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
@@ -1482,7 +1590,7 @@ func file_internal_machine_api_pb_machine_proto_init() {
|
|||||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||||
RawDescriptor: file_internal_machine_api_pb_machine_proto_rawDesc,
|
RawDescriptor: file_internal_machine_api_pb_machine_proto_rawDesc,
|
||||||
NumEnums: 0,
|
NumEnums: 0,
|
||||||
NumMessages: 16,
|
NumMessages: 18,
|
||||||
NumExtensions: 0,
|
NumExtensions: 0,
|
||||||
NumServices: 1,
|
NumServices: 1,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ package api;
|
|||||||
|
|
||||||
option go_package = "github.com/psviderski/uncloud/internal/machine/api/pb";
|
option go_package = "github.com/psviderski/uncloud/internal/machine/api/pb";
|
||||||
|
|
||||||
|
import "google/protobuf/duration.proto";
|
||||||
import "google/protobuf/empty.proto";
|
import "google/protobuf/empty.proto";
|
||||||
import "google/protobuf/timestamp.proto";
|
import "google/protobuf/timestamp.proto";
|
||||||
import "internal/machine/api/pb/common.proto";
|
import "internal/machine/api/pb/common.proto";
|
||||||
@@ -24,6 +25,8 @@ service Machine {
|
|||||||
rpc Reset(ResetRequest) returns (google.protobuf.Empty);
|
rpc Reset(ResetRequest) returns (google.protobuf.Empty);
|
||||||
|
|
||||||
rpc InspectService(InspectServiceRequest) returns (InspectServiceResponse);
|
rpc InspectService(InspectServiceRequest) returns (InspectServiceResponse);
|
||||||
|
|
||||||
|
rpc MachineLogs(LogsRequest) returns (stream LogEntry);
|
||||||
}
|
}
|
||||||
|
|
||||||
message MachineInfo {
|
message MachineInfo {
|
||||||
@@ -81,6 +84,8 @@ message MachineDetails {
|
|||||||
MachineInfo machine = 2;
|
MachineInfo machine = 2;
|
||||||
// Current Corrosion cr-sqlite database version (Lamport timestamp) of the cluster store.
|
// Current Corrosion cr-sqlite database version (Lamport timestamp) of the cluster store.
|
||||||
int64 store_db_version = 3;
|
int64 store_db_version = 3;
|
||||||
|
// Round-trip times to other machines in the cluster, keyed by peer machine ID.
|
||||||
|
map<string, RTTStats> rtts = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
message TokenResponse {
|
message TokenResponse {
|
||||||
@@ -127,3 +132,8 @@ message WireGuardPeer {
|
|||||||
int64 transmit_bytes = 5;
|
int64 transmit_bytes = 5;
|
||||||
repeated string allowed_ips = 6;
|
repeated string allowed_ips = 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message RTTStats {
|
||||||
|
google.protobuf.Duration median = 1;
|
||||||
|
google.protobuf.Duration std_dev = 2;
|
||||||
|
}
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ const (
|
|||||||
Machine_InspectWireGuardNetwork_FullMethodName = "/api.Machine/InspectWireGuardNetwork"
|
Machine_InspectWireGuardNetwork_FullMethodName = "/api.Machine/InspectWireGuardNetwork"
|
||||||
Machine_Reset_FullMethodName = "/api.Machine/Reset"
|
Machine_Reset_FullMethodName = "/api.Machine/Reset"
|
||||||
Machine_InspectService_FullMethodName = "/api.Machine/InspectService"
|
Machine_InspectService_FullMethodName = "/api.Machine/InspectService"
|
||||||
|
Machine_MachineLogs_FullMethodName = "/api.Machine/MachineLogs"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MachineClient is the client API for Machine service.
|
// MachineClient is the client API for Machine service.
|
||||||
@@ -49,6 +50,7 @@ type MachineClient interface {
|
|||||||
// Reset restores the machine to a clean state, removing all cluster-related configuration and data.
|
// Reset restores the machine to a clean state, removing all cluster-related configuration and data.
|
||||||
Reset(ctx context.Context, in *ResetRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)
|
Reset(ctx context.Context, in *ResetRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)
|
||||||
InspectService(ctx context.Context, in *InspectServiceRequest, opts ...grpc.CallOption) (*InspectServiceResponse, error)
|
InspectService(ctx context.Context, in *InspectServiceRequest, opts ...grpc.CallOption) (*InspectServiceResponse, error)
|
||||||
|
MachineLogs(ctx context.Context, in *LogsRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[LogEntry], error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type machineClient struct {
|
type machineClient struct {
|
||||||
@@ -149,6 +151,25 @@ func (c *machineClient) InspectService(ctx context.Context, in *InspectServiceRe
|
|||||||
return out, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *machineClient) MachineLogs(ctx context.Context, in *LogsRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[LogEntry], error) {
|
||||||
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
|
stream, err := c.cc.NewStream(ctx, &Machine_ServiceDesc.Streams[0], Machine_MachineLogs_FullMethodName, cOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
x := &grpc.GenericClientStream[LogsRequest, LogEntry]{ClientStream: stream}
|
||||||
|
if err := x.ClientStream.SendMsg(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := x.ClientStream.CloseSend(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return x, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||||
|
type Machine_MachineLogsClient = grpc.ServerStreamingClient[LogEntry]
|
||||||
|
|
||||||
// MachineServer is the server API for Machine service.
|
// MachineServer is the server API for Machine service.
|
||||||
// All implementations must embed UnimplementedMachineServer
|
// All implementations must embed UnimplementedMachineServer
|
||||||
// for forward compatibility.
|
// for forward compatibility.
|
||||||
@@ -167,6 +188,7 @@ type MachineServer interface {
|
|||||||
// Reset restores the machine to a clean state, removing all cluster-related configuration and data.
|
// Reset restores the machine to a clean state, removing all cluster-related configuration and data.
|
||||||
Reset(context.Context, *ResetRequest) (*emptypb.Empty, error)
|
Reset(context.Context, *ResetRequest) (*emptypb.Empty, error)
|
||||||
InspectService(context.Context, *InspectServiceRequest) (*InspectServiceResponse, error)
|
InspectService(context.Context, *InspectServiceRequest) (*InspectServiceResponse, error)
|
||||||
|
MachineLogs(*LogsRequest, grpc.ServerStreamingServer[LogEntry]) error
|
||||||
mustEmbedUnimplementedMachineServer()
|
mustEmbedUnimplementedMachineServer()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -204,6 +226,9 @@ func (UnimplementedMachineServer) Reset(context.Context, *ResetRequest) (*emptyp
|
|||||||
func (UnimplementedMachineServer) InspectService(context.Context, *InspectServiceRequest) (*InspectServiceResponse, error) {
|
func (UnimplementedMachineServer) InspectService(context.Context, *InspectServiceRequest) (*InspectServiceResponse, error) {
|
||||||
return nil, status.Errorf(codes.Unimplemented, "method InspectService not implemented")
|
return nil, status.Errorf(codes.Unimplemented, "method InspectService not implemented")
|
||||||
}
|
}
|
||||||
|
func (UnimplementedMachineServer) MachineLogs(*LogsRequest, grpc.ServerStreamingServer[LogEntry]) error {
|
||||||
|
return status.Errorf(codes.Unimplemented, "method MachineLogs not implemented")
|
||||||
|
}
|
||||||
func (UnimplementedMachineServer) mustEmbedUnimplementedMachineServer() {}
|
func (UnimplementedMachineServer) mustEmbedUnimplementedMachineServer() {}
|
||||||
func (UnimplementedMachineServer) testEmbeddedByValue() {}
|
func (UnimplementedMachineServer) testEmbeddedByValue() {}
|
||||||
|
|
||||||
@@ -387,6 +412,17 @@ func _Machine_InspectService_Handler(srv interface{}, ctx context.Context, dec f
|
|||||||
return interceptor(ctx, in, info, handler)
|
return interceptor(ctx, in, info, handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func _Machine_MachineLogs_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||||
|
m := new(LogsRequest)
|
||||||
|
if err := stream.RecvMsg(m); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return srv.(MachineServer).MachineLogs(m, &grpc.GenericServerStream[LogsRequest, LogEntry]{ServerStream: stream})
|
||||||
|
}
|
||||||
|
|
||||||
|
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||||
|
type Machine_MachineLogsServer = grpc.ServerStreamingServer[LogEntry]
|
||||||
|
|
||||||
// Machine_ServiceDesc is the grpc.ServiceDesc for Machine service.
|
// Machine_ServiceDesc is the grpc.ServiceDesc for Machine service.
|
||||||
// It's only intended for direct use with grpc.RegisterService,
|
// It's only intended for direct use with grpc.RegisterService,
|
||||||
// and not to be introspected or modified (even as a copy)
|
// and not to be introspected or modified (even as a copy)
|
||||||
@@ -431,6 +467,12 @@ var Machine_ServiceDesc = grpc.ServiceDesc{
|
|||||||
Handler: _Machine_InspectService_Handler,
|
Handler: _Machine_InspectService_Handler,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Streams: []grpc.StreamDesc{},
|
Streams: []grpc.StreamDesc{
|
||||||
|
{
|
||||||
|
StreamName: "MachineLogs",
|
||||||
|
Handler: _Machine_MachineLogs_Handler,
|
||||||
|
ServerStreams: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
Metadata: "internal/machine/api/pb/machine.proto",
|
Metadata: "internal/machine/api/pb/machine.proto",
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
caddyfileHeaderFmt = `# Caddyfile autogenerated by Uncloud (DO NOT EDIT): %s
|
caddyfileHeaderFmt = `# Caddyfile autogenerated by Uncloud on machine '%s' (DO NOT EDIT): %s
|
||||||
# Automatically updated on service or health status changes.
|
# Automatically updated on service or health status changes.
|
||||||
# Docs: https://uncloud.run/docs/concepts/ingress/overview
|
# Docs: https://uncloud.run/docs/concepts/ingress/overview
|
||||||
`
|
`
|
||||||
@@ -68,8 +68,10 @@ https://{{$hostname}} {
|
|||||||
type CaddyfileGenerator struct {
|
type CaddyfileGenerator struct {
|
||||||
// machineID is the unique identifier of the machine where the controller is running.
|
// machineID is the unique identifier of the machine where the controller is running.
|
||||||
machineID string
|
machineID string
|
||||||
validator CaddyfileValidator
|
// machineName is the human-friendly name of the machine.
|
||||||
log *slog.Logger
|
machineName string
|
||||||
|
validator CaddyfileValidator
|
||||||
|
log *slog.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
// CaddyfileValidator is an interface for validating Caddyfile configurations.
|
// CaddyfileValidator is an interface for validating Caddyfile configurations.
|
||||||
@@ -77,14 +79,17 @@ type CaddyfileValidator interface {
|
|||||||
Validate(ctx context.Context, caddyfile string) error
|
Validate(ctx context.Context, caddyfile string) error
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewCaddyfileGenerator(machineID string, validator CaddyfileValidator, log *slog.Logger) *CaddyfileGenerator {
|
func NewCaddyfileGenerator(
|
||||||
|
machineID, machineName string, validator CaddyfileValidator, log *slog.Logger,
|
||||||
|
) *CaddyfileGenerator {
|
||||||
if log == nil {
|
if log == nil {
|
||||||
log = slog.Default()
|
log = slog.Default()
|
||||||
}
|
}
|
||||||
return &CaddyfileGenerator{
|
return &CaddyfileGenerator{
|
||||||
machineID: machineID,
|
machineID: machineID,
|
||||||
validator: validator,
|
machineName: machineName,
|
||||||
log: log,
|
validator: validator,
|
||||||
|
log: log,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,24 +112,30 @@ func NewCaddyfileGenerator(machineID string, validator CaddyfileValidator, log *
|
|||||||
func (g *CaddyfileGenerator) Generate(
|
func (g *CaddyfileGenerator) Generate(
|
||||||
ctx context.Context, records []store.ContainerRecord, includeCustom bool,
|
ctx context.Context, records []store.ContainerRecord, includeCustom bool,
|
||||||
) (string, error) {
|
) (string, error) {
|
||||||
|
// Sort records by local machine first, then by service name and creation time. Placing containers on the local
|
||||||
|
// machine first lets user-defined Caddy configs pair this ordering with the "first" lb_policy to always send
|
||||||
|
// traffic to the same-host replica (skipping the cross-machine hop) and only fall back to remote upstreams when
|
||||||
|
// the local one is unhealthy.
|
||||||
|
// The service name and creation time tiebreakers keep the generated Caddyfile stable across regenerations.
|
||||||
|
slices.SortStableFunc(records, func(a, b store.ContainerRecord) int {
|
||||||
|
return cmp.Or(
|
||||||
|
g.localMachineRank(a.MachineID)-g.localMachineRank(b.MachineID),
|
||||||
|
strings.Compare(a.Container.ServiceName(), b.Container.ServiceName()),
|
||||||
|
a.Container.CreatedTime().Compare(b.Container.CreatedTime()),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
containers := make([]api.ServiceContainer, len(records))
|
containers := make([]api.ServiceContainer, len(records))
|
||||||
for i, cr := range records {
|
for i, cr := range records {
|
||||||
containers[i] = cr.Container
|
containers[i] = cr.Container
|
||||||
}
|
}
|
||||||
// Sort containers by service name and creation time to generate a stable Caddyfile.
|
|
||||||
slices.SortStableFunc(containers, func(a, b api.ServiceContainer) int {
|
|
||||||
return cmp.Or(
|
|
||||||
strings.Compare(a.ServiceName(), b.ServiceName()),
|
|
||||||
a.CreatedTime().Compare(b.CreatedTime()),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
caddyfile, err := g.generateBaseFromPorts(containers)
|
caddyfile, err := g.generateBaseFromPorts(containers)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("generate base Caddyfile from service ports: %w", err)
|
return "", fmt.Errorf("generate base Caddyfile from service ports: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
caddyfileHeader := fmt.Sprintf(caddyfileHeaderFmt, time.Now().UTC().Format(time.RFC3339))
|
caddyfileHeader := fmt.Sprintf(caddyfileHeaderFmt, g.machineName, time.Now().UTC().Format(time.RFC3339))
|
||||||
if !includeCustom {
|
if !includeCustom {
|
||||||
return fmt.Sprintf("%s\n%s\n%s", caddyfileHeader, caddyfile, caddyfileUnavailabeFooter), nil
|
return fmt.Sprintf("%s\n%s\n%s", caddyfileHeader, caddyfile, caddyfileUnavailabeFooter), nil
|
||||||
}
|
}
|
||||||
@@ -238,6 +249,15 @@ func (g *CaddyfileGenerator) Generate(
|
|||||||
return caddyfileHeader + "\n" + caddyfile, nil
|
return caddyfileHeader + "\n" + caddyfile, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// localMachineRank returns 0 if the given machineID matches the local machine and 1 otherwise.
|
||||||
|
// Useful for sorting containers running locally first.
|
||||||
|
func (g *CaddyfileGenerator) localMachineRank(machineID string) int {
|
||||||
|
if g.machineID == machineID {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
func (g *CaddyfileGenerator) generateBaseFromPorts(containers []api.ServiceContainer) (string, error) {
|
func (g *CaddyfileGenerator) generateBaseFromPorts(containers []api.ServiceContainer) (string, error) {
|
||||||
httpHostUpstreams, httpsHostUpstreams := httpUpstreamsFromPorts(containers)
|
httpHostUpstreams, httpsHostUpstreams := httpUpstreamsFromPorts(containers)
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ func normaliseGeneratedTimestamp(caddyfile string) string {
|
|||||||
return generatedTimestampRegex.ReplaceAllString(caddyfile, "(DO NOT EDIT): TIMESTAMP_PLACEHOLDER")
|
return generatedTimestampRegex.ReplaceAllString(caddyfile, "(DO NOT EDIT): TIMESTAMP_PLACEHOLDER")
|
||||||
}
|
}
|
||||||
|
|
||||||
const testCaddyfileHeader = `# Caddyfile autogenerated by Uncloud (DO NOT EDIT): TIMESTAMP_PLACEHOLDER
|
const testCaddyfileHeader = `# Caddyfile autogenerated by Uncloud on machine 'test-machine' (DO NOT EDIT): TIMESTAMP_PLACEHOLDER
|
||||||
# Automatically updated on service or health status changes.
|
# Automatically updated on service or health status changes.
|
||||||
# Docs: https://uncloud.run/docs/concepts/ingress/overview
|
# Docs: https://uncloud.run/docs/concepts/ingress/overview
|
||||||
|
|
||||||
@@ -159,6 +159,25 @@ https://secure.example.com {
|
|||||||
}
|
}
|
||||||
log
|
log
|
||||||
}
|
}
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "local machine upstreams listed first",
|
||||||
|
containers: []store.ContainerRecord{
|
||||||
|
newContainerRecord(newContainer("10.210.0.2", "app.example.com:8080/http"), "mach1"),
|
||||||
|
newContainerRecord(newContainer("10.210.1.2", "app.example.com:8080/http"), "test-machine-id"),
|
||||||
|
newContainerRecord(newContainer("10.210.2.2", "app.example.com:8080/http"), "mach2"),
|
||||||
|
newContainerRecord(newContainer("10.210.1.3", "app.example.com:8080/http"), "test-machine-id"),
|
||||||
|
},
|
||||||
|
want: testCaddyfileHeader + `
|
||||||
|
# Sites generated from service ports.
|
||||||
|
|
||||||
|
http://app.example.com {
|
||||||
|
reverse_proxy 10.210.1.2:8080 10.210.1.3:8080 10.210.0.2:8080 10.210.2.2:8080 {
|
||||||
|
import common_proxy
|
||||||
|
}
|
||||||
|
log
|
||||||
|
}
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -190,7 +209,7 @@ https://secure.example.com {
|
|||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
// Validator is not expected to be called in these tests.
|
// Validator is not expected to be called in these tests.
|
||||||
generator := NewCaddyfileGenerator("test-machine-id", nil, nil)
|
generator := NewCaddyfileGenerator("test-machine-id", "test-machine", nil, nil)
|
||||||
|
|
||||||
config, err := generator.Generate(ctx, tt.containers, true)
|
config, err := generator.Generate(ctx, tt.containers, true)
|
||||||
|
|
||||||
@@ -226,7 +245,7 @@ func TestCaddyfileGeneratorWithCustomConfigs(t *testing.T) {
|
|||||||
time.Now(),
|
time.Now(),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
want: `# Caddyfile autogenerated by Uncloud (DO NOT EDIT): TIMESTAMP_PLACEHOLDER
|
want: `# Caddyfile autogenerated by Uncloud on machine 'test-machine' (DO NOT EDIT): TIMESTAMP_PLACEHOLDER
|
||||||
# Automatically updated on service or health status changes.
|
# Automatically updated on service or health status changes.
|
||||||
# Docs: https://uncloud.run/docs/concepts/ingress/overview
|
# Docs: https://uncloud.run/docs/concepts/ingress/overview
|
||||||
|
|
||||||
@@ -423,7 +442,7 @@ web.example.com {
|
|||||||
time.Now(),
|
time.Now(),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
want: `# Caddyfile autogenerated by Uncloud (DO NOT EDIT): TIMESTAMP_PLACEHOLDER
|
want: `# Caddyfile autogenerated by Uncloud on machine 'test-machine' (DO NOT EDIT): TIMESTAMP_PLACEHOLDER
|
||||||
# Automatically updated on service or health status changes.
|
# Automatically updated on service or health status changes.
|
||||||
# Docs: https://uncloud.run/docs/concepts/ingress/overview
|
# Docs: https://uncloud.run/docs/concepts/ingress/overview
|
||||||
|
|
||||||
@@ -661,7 +680,7 @@ badconfig.com {
|
|||||||
time.Now(),
|
time.Now(),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
want: `# Caddyfile autogenerated by Uncloud (DO NOT EDIT): TIMESTAMP_PLACEHOLDER
|
want: `# Caddyfile autogenerated by Uncloud on machine 'test-machine' (DO NOT EDIT): TIMESTAMP_PLACEHOLDER
|
||||||
# Automatically updated on service or health status changes.
|
# Automatically updated on service or health status changes.
|
||||||
# Docs: https://uncloud.run/docs/concepts/ingress/overview
|
# Docs: https://uncloud.run/docs/concepts/ingress/overview
|
||||||
|
|
||||||
@@ -834,7 +853,7 @@ valid.example.com {
|
|||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
generator := NewCaddyfileGenerator("test-machine-id", validator, nil)
|
generator := NewCaddyfileGenerator("test-machine-id", "test-machine", validator, nil)
|
||||||
|
|
||||||
config, err := generator.Generate(ctx, tt.containers, true)
|
config, err := generator.Generate(ctx, tt.containers, true)
|
||||||
|
|
||||||
@@ -976,7 +995,7 @@ http://api.example.com {
|
|||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
// Validator is not expected to be called in these tests.
|
// Validator is not expected to be called in these tests.
|
||||||
generator := NewCaddyfileGenerator("test-machine-id", nil, nil)
|
generator := NewCaddyfileGenerator("test-machine-id", "test-machine", nil, nil)
|
||||||
|
|
||||||
config, err := generator.Generate(ctx, tt.containers, false)
|
config, err := generator.Generate(ctx, tt.containers, false)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|||||||
@@ -34,21 +34,16 @@ func NewCaddyAdminClient(socketPath string) *CaddyAdminClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsAvailable checks if the local Caddy instance is running and responding to admin API requests.
|
// IsAvailable checks if the local Caddy instance is listening on the admin socket.
|
||||||
func (c *CaddyAdminClient) IsAvailable(ctx context.Context) bool {
|
func (c *CaddyAdminClient) IsAvailable() bool {
|
||||||
// Caddy doesn't serve a /ping endpoint. It's a random endpoint we can use to check if Caddy is running.
|
conn, err := net.DialTimeout("unix", c.socketPath, 1*time.Second)
|
||||||
req, err := http.NewRequestWithContext(ctx, "GET", "http://localhost/ping", nil)
|
// A stale socket file left over from a crashed Caddy container returns ECONNREFUSED so this is correctly handled
|
||||||
|
// as unavailable.
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
conn.Close()
|
||||||
|
|
||||||
resp, err := c.client.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
// Any HTTP response means Caddy is running and accessible.
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,8 +5,11 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
|
"net/netip"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"slices"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/psviderski/uncloud/internal/fs"
|
"github.com/psviderski/uncloud/internal/fs"
|
||||||
"github.com/psviderski/uncloud/internal/machine/store"
|
"github.com/psviderski/uncloud/internal/machine/store"
|
||||||
@@ -29,6 +32,28 @@ type Controller struct {
|
|||||||
client *CaddyAdminClient
|
client *CaddyAdminClient
|
||||||
store *store.Store
|
store *store.Store
|
||||||
log *slog.Logger
|
log *slog.Logger
|
||||||
|
// lastFingerprint caches the fingerprint of the containers used to generate the latest successfully loaded
|
||||||
|
// Caddyfile. nil means it hasn't been loaded yet or the last load failed.
|
||||||
|
lastFingerprint []containerFingerprint
|
||||||
|
// lastCaddyfile caches the last generated Caddyfile.
|
||||||
|
lastCaddyfile string
|
||||||
|
}
|
||||||
|
|
||||||
|
// containerFingerprint is the subset of container data that the Caddyfile generator depends on.
|
||||||
|
// Comparing fingerprints lets the controller skip no-op regenerations.
|
||||||
|
type containerFingerprint struct {
|
||||||
|
ID string
|
||||||
|
IP netip.Addr
|
||||||
|
Ports []api.PortSpec
|
||||||
|
CaddyConfig string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equal returns whether two fingerprints describe the same container input to the Caddyfile generator.
|
||||||
|
func (f containerFingerprint) Equal(other containerFingerprint) bool {
|
||||||
|
return f.ID == other.ID &&
|
||||||
|
f.IP == other.IP &&
|
||||||
|
api.PortsEqual(f.Ports, other.Ports) &&
|
||||||
|
f.CaddyConfig == other.CaddyConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewController(machineID, configDir, adminSock string, store *store.Store) (*Controller, error) {
|
func NewController(machineID, configDir, adminSock string, store *store.Store) (*Controller, error) {
|
||||||
@@ -41,12 +66,11 @@ func NewController(machineID, configDir, adminSock string, store *store.Store) (
|
|||||||
|
|
||||||
log := slog.With("component", "caddy-controller")
|
log := slog.With("component", "caddy-controller")
|
||||||
client := NewCaddyAdminClient(adminSock)
|
client := NewCaddyAdminClient(adminSock)
|
||||||
generator := NewCaddyfileGenerator(machineID, client, log)
|
|
||||||
|
|
||||||
|
// generator is initialised by Run() once the machine name is resolved from the store.
|
||||||
return &Controller{
|
return &Controller{
|
||||||
machineID: machineID,
|
machineID: machineID,
|
||||||
caddyfilePath: filepath.Join(configDir, "Caddyfile"),
|
caddyfilePath: filepath.Join(configDir, "Caddyfile"),
|
||||||
generator: generator,
|
|
||||||
client: client,
|
client: client,
|
||||||
store: store,
|
store: store,
|
||||||
log: log,
|
log: log,
|
||||||
@@ -54,6 +78,17 @@ func NewController(machineID, configDir, adminSock string, store *store.Store) (
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Controller) Run(ctx context.Context) error {
|
func (c *Controller) Run(ctx context.Context) error {
|
||||||
|
// Default the machine name to the machine ID so the Caddyfile header still carries a stable identifier if
|
||||||
|
// the store lookup fails.
|
||||||
|
machineName := c.machineID
|
||||||
|
if m, err := c.store.GetMachine(ctx, c.machineID); err != nil {
|
||||||
|
c.log.Error("Failed to get machine from store, Caddy configuration will use machine ID as the name.",
|
||||||
|
"machine_id", c.machineID, "err", err)
|
||||||
|
} else {
|
||||||
|
machineName = m.Name
|
||||||
|
}
|
||||||
|
c.generator = NewCaddyfileGenerator(c.machineID, machineName, c.client, c.log)
|
||||||
|
|
||||||
containers, changes, err := c.store.SubscribeContainers(ctx)
|
containers, changes, err := c.store.SubscribeContainers(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("subscribe to container changes: %w", err)
|
return fmt.Errorf("subscribe to container changes: %w", err)
|
||||||
@@ -74,7 +109,7 @@ func (c *Controller) Run(ctx context.Context) error {
|
|||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("containers subscription failed")
|
return fmt.Errorf("containers subscription failed")
|
||||||
}
|
}
|
||||||
c.log.Info("Cluster containers changed, updating Caddy configuration.")
|
c.log.Debug("Cluster containers changed, regenerating Caddy configuration.")
|
||||||
|
|
||||||
containers, err = c.store.ListContainers(ctx, store.ListOptions{})
|
containers, err = c.store.ListContainers(ctx, store.ListOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -94,13 +129,16 @@ func (c *Controller) Run(ctx context.Context) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// filterHealthyContainers filters out containers that are not healthy.
|
// filterHealthyContainers filters out unhealthy and hook containers.
|
||||||
// TODO: Filters out containers from this machine that are likely unavailable. The availability can be determined
|
// TODO: Filters out containers from this machine that are likely unavailable. The availability can be determined
|
||||||
// by the cluster membership state of the machine that the container is running on. Implement machine membership
|
// by the cluster membership state of the machine that the container is running on. Implement machine membership
|
||||||
// check using Corrossion Admin client.
|
// check using Corrossion Admin client.
|
||||||
func filterHealthyContainers(containers []store.ContainerRecord) []store.ContainerRecord {
|
func filterHealthyContainers(containers []store.ContainerRecord) []store.ContainerRecord {
|
||||||
healthy := make([]store.ContainerRecord, 0, len(containers))
|
healthy := make([]store.ContainerRecord, 0, len(containers))
|
||||||
for _, cr := range containers {
|
for _, cr := range containers {
|
||||||
|
if cr.Container.IsHook() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
if cr.Container.Healthy() {
|
if cr.Container.Healthy() {
|
||||||
healthy = append(healthy, cr)
|
healthy = append(healthy, cr)
|
||||||
}
|
}
|
||||||
@@ -108,9 +146,20 @@ func filterHealthyContainers(containers []store.ContainerRecord) []store.Contain
|
|||||||
return healthy
|
return healthy
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// generateAndLoadCaddyfile regenerates the Caddyfile from the given containers and loads it into the local Caddy
|
||||||
|
// if available.
|
||||||
func (c *Controller) generateAndLoadCaddyfile(ctx context.Context, containers []store.ContainerRecord) {
|
func (c *Controller) generateAndLoadCaddyfile(ctx context.Context, containers []store.ContainerRecord) {
|
||||||
// Check if Caddy is available before attempting to generate and load config.
|
// Check if Caddy is available before attempting to generate and load config.
|
||||||
caddyAvailable := c.client.IsAvailable(ctx)
|
caddyAvailable := c.client.IsAvailable()
|
||||||
|
|
||||||
|
// Skip regeneration when Caddy is available and the containers since the last successful load haven't changed.
|
||||||
|
// When Caddy is unavailable we still regenerate to keep the Caddyfile on disk updated.
|
||||||
|
fingerprint := fingerprintContainers(containers)
|
||||||
|
if caddyAvailable && slices.EqualFunc(fingerprint, c.lastFingerprint, containerFingerprint.Equal) {
|
||||||
|
c.log.Debug("Caddy configuration is unchanged.", "path", c.caddyfilePath)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
caddyfile, err := c.generator.Generate(ctx, containers, caddyAvailable)
|
caddyfile, err := c.generator.Generate(ctx, containers, caddyAvailable)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.log.Error("Failed to generate Caddyfile configuration.", "err", err)
|
c.log.Error("Failed to generate Caddyfile configuration.", "err", err)
|
||||||
@@ -120,7 +169,7 @@ func (c *Controller) generateAndLoadCaddyfile(ctx context.Context, containers []
|
|||||||
if !caddyAvailable {
|
if !caddyAvailable {
|
||||||
// Caddy is not running so the generated Caddyfile should not include user-defined configs thus must be valid.
|
// Caddy is not running so the generated Caddyfile should not include user-defined configs thus must be valid.
|
||||||
// It's safe to write the config to disk so that when Caddy is deployed on this machine, it can pick it up.
|
// It's safe to write the config to disk so that when Caddy is deployed on this machine, it can pick it up.
|
||||||
if err = c.writeCaddyfile(caddyfile); err != nil {
|
if err = c.writeCaddyfileIfChanged(caddyfile); err != nil {
|
||||||
c.log.Error("Failed to write Caddyfile to disk.", "err", err)
|
c.log.Error("Failed to write Caddyfile to disk.", "err", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -134,30 +183,71 @@ func (c *Controller) generateAndLoadCaddyfile(ctx context.Context, containers []
|
|||||||
if err = c.client.Load(ctx, caddyfile); err != nil {
|
if err = c.client.Load(ctx, caddyfile); err != nil {
|
||||||
c.log.Error("Failed to load new Caddy configuration into local Caddy instance.",
|
c.log.Error("Failed to load new Caddy configuration into local Caddy instance.",
|
||||||
"err", err, "path", c.caddyfilePath)
|
"err", err, "path", c.caddyfilePath)
|
||||||
|
// Mark the cache stale so the next container change retries the load even if the container set is unchanged.
|
||||||
|
c.lastFingerprint = nil
|
||||||
// Don't write invalid config to disk.
|
// Don't write invalid config to disk.
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
c.lastFingerprint = fingerprint
|
||||||
|
|
||||||
// Config loaded successfully, now write it to disk.
|
// Config loaded successfully, now write it to disk.
|
||||||
if err = c.writeCaddyfile(caddyfile); err != nil {
|
if err = c.writeCaddyfileIfChanged(caddyfile); err != nil {
|
||||||
c.log.Error("Failed to write Caddyfile to disk after successful load.", "err", err)
|
c.log.Error("Failed to write Caddyfile to disk after successful load.", "err", err)
|
||||||
// Config is already loaded in Caddy, so this is not critical.
|
// Config is already loaded in Caddy, so this is not critical. The next regeneration retries the disk write.
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
c.log.Info("New Caddy configuration loaded into local Caddy instance.", "path", c.caddyfilePath)
|
c.log.Info("New Caddy configuration loaded into local Caddy instance.", "path", c.caddyfilePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
// writeCaddyfile writes the Caddyfile content to disk with proper permissions.
|
// fingerprintContainers returns a fingerprint of containers that the Caddyfile generator depends on.
|
||||||
func (c *Controller) writeCaddyfile(caddyfile string) error {
|
func fingerprintContainers(containers []store.ContainerRecord) []containerFingerprint {
|
||||||
|
fingerprints := make([]containerFingerprint, len(containers))
|
||||||
|
for i, cr := range containers {
|
||||||
|
// Ignore ports parsing error as not much we can do about it. The generator just logs them and continues.
|
||||||
|
ports, _ := cr.Container.ServicePorts()
|
||||||
|
fingerprints[i] = containerFingerprint{
|
||||||
|
ID: cr.Container.ID,
|
||||||
|
IP: cr.Container.UncloudNetworkIP(),
|
||||||
|
Ports: ports,
|
||||||
|
CaddyConfig: cr.Container.ServiceSpec.CaddyConfig(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
slices.SortFunc(fingerprints, func(a, b containerFingerprint) int {
|
||||||
|
return strings.Compare(a.ID, b.ID)
|
||||||
|
})
|
||||||
|
|
||||||
|
return fingerprints
|
||||||
|
}
|
||||||
|
|
||||||
|
// writeCaddyfileIfChanged writes the Caddyfile content to disk with proper permissions only if its body differs
|
||||||
|
// from the last successfully written content. The first line of the Caddyfile carries a generation timestamp that
|
||||||
|
// changes on every regeneration, so it's excluded from the comparison to avoid redundant writes.
|
||||||
|
func (c *Controller) writeCaddyfileIfChanged(caddyfile string) error {
|
||||||
|
if caddyfileBody(caddyfile) == caddyfileBody(c.lastCaddyfile) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
if err := os.WriteFile(c.caddyfilePath, []byte(caddyfile), 0o640); err != nil {
|
if err := os.WriteFile(c.caddyfilePath, []byte(caddyfile), 0o640); err != nil {
|
||||||
return fmt.Errorf("write Caddyfile to file '%s': %w", c.caddyfilePath, err)
|
return fmt.Errorf("write Caddyfile to file '%s': %w", c.caddyfilePath, err)
|
||||||
}
|
}
|
||||||
if err := fs.Chown(c.caddyfilePath, "", CaddyGroup); err != nil {
|
if err := fs.Chown(c.caddyfilePath, "", CaddyGroup); err != nil {
|
||||||
return fmt.Errorf("change owner of Caddyfile '%s': %w", c.caddyfilePath, err)
|
return fmt.Errorf("change owner of Caddyfile '%s': %w", c.caddyfilePath, err)
|
||||||
}
|
}
|
||||||
|
c.lastCaddyfile = caddyfile
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// caddyfileBody returns the Caddyfile content without its first line, which carries a generation timestamp that
|
||||||
|
// rotates on every regeneration.
|
||||||
|
func caddyfileBody(caddyfile string) string {
|
||||||
|
if i := strings.IndexByte(caddyfile, '\n'); i >= 0 {
|
||||||
|
return caddyfile[i+1:]
|
||||||
|
}
|
||||||
|
return caddyfile
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Controller) generateJSONConfig(containers []store.ContainerRecord) error {
|
func (c *Controller) generateJSONConfig(containers []store.ContainerRecord) error {
|
||||||
serviceContainers := make([]api.ServiceContainer, len(containers))
|
serviceContainers := make([]api.ServiceContainer, len(containers))
|
||||||
for i, cr := range containers {
|
for i, cr := range containers {
|
||||||
|
|||||||
@@ -0,0 +1,64 @@
|
|||||||
|
package caddyconfig
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/netip"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/psviderski/uncloud/pkg/api"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestContainerFingerprint_EqualCoversAllFields is a guard: when a field is added to containerFingerprint,
|
||||||
|
// Equal must also compare it. A mutation of any single field should flip equality to false. If this test fails
|
||||||
|
// after adding a field, update Equal to include it.
|
||||||
|
func TestContainerFingerprint_EqualCoversAllFields(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
base := containerFingerprint{
|
||||||
|
ID: "container-1",
|
||||||
|
IP: netip.MustParseAddr("10.210.0.2"),
|
||||||
|
Ports: []api.PortSpec{{
|
||||||
|
Hostname: "app.example.com",
|
||||||
|
ContainerPort: 8080,
|
||||||
|
Protocol: api.ProtocolHTTP,
|
||||||
|
Mode: api.PortModeIngress,
|
||||||
|
}},
|
||||||
|
CaddyConfig: "caddy-config",
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.True(t, base.Equal(base), "base fingerprint must be equal to itself")
|
||||||
|
|
||||||
|
rt := reflect.TypeOf(base)
|
||||||
|
for i := 0; i < rt.NumField(); i++ {
|
||||||
|
field := rt.Field(i)
|
||||||
|
t.Run(field.Name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
mutated := base
|
||||||
|
mutated.Ports = append([]api.PortSpec(nil), base.Ports...)
|
||||||
|
v := reflect.ValueOf(&mutated).Elem().FieldByName(field.Name)
|
||||||
|
|
||||||
|
switch field.Name {
|
||||||
|
case "ID", "CaddyConfig":
|
||||||
|
v.SetString(v.String() + "-changed")
|
||||||
|
case "IP":
|
||||||
|
v.Set(reflect.ValueOf(netip.MustParseAddr("10.210.0.99")))
|
||||||
|
case "Ports":
|
||||||
|
mutated.Ports = []api.PortSpec{{
|
||||||
|
Hostname: "different.example.com",
|
||||||
|
ContainerPort: 9090,
|
||||||
|
Protocol: api.ProtocolHTTP,
|
||||||
|
Mode: api.PortModeIngress,
|
||||||
|
}}
|
||||||
|
default:
|
||||||
|
t.Fatalf("containerFingerprint has a new field %q without a mutation case in this test. "+
|
||||||
|
"Add a case here and make sure Equal() compares it.", field.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.False(t, base.Equal(mutated),
|
||||||
|
"changing %q must flip Equal to false. Update containerFingerprint.Equal to compare it.",
|
||||||
|
field.Name)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -344,3 +344,8 @@ func (c *Cluster) RemoveMachine(ctx context.Context, req *pb.RemoveMachineReques
|
|||||||
|
|
||||||
return &emptypb.Empty{}, nil
|
return &emptypb.Empty{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MemberRTTs returns the median and standard deviation of round-trip times from this member to each cluster member.
|
||||||
|
func (c *Cluster) MemberRTTs() ([]corrosion.MemberRTTStats, error) {
|
||||||
|
return c.corroAdmin.ClusterMemberRTTs()
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,7 +4,9 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
|
"maps"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
|
"slices"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -72,6 +74,9 @@ func (r *ClusterResolver) updateServiceIPs(containers []store.ContainerRecord) {
|
|||||||
|
|
||||||
containersCount := 0
|
containersCount := 0
|
||||||
for _, record := range containers {
|
for _, record := range containers {
|
||||||
|
if record.Container.IsHook() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
if !record.Container.Healthy() {
|
if !record.Container.Healthy() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -99,12 +104,21 @@ func (r *ClusterResolver) updateServiceIPs(containers []store.ContainerRecord) {
|
|||||||
containersCount++
|
containersCount++
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sort each service's IPs so they have a deterministic order for comparison.
|
||||||
|
for _, ips := range newServiceIPs {
|
||||||
|
slices.SortFunc(ips, func(a, b netip.Addr) int { return a.Compare(b) })
|
||||||
|
}
|
||||||
|
// Skip the swap when the services or their container IPs haven't changed.
|
||||||
|
if maps.EqualFunc(r.serviceIPs, newServiceIPs, slices.Equal[[]netip.Addr]) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Update the serviceIPs map atomically.
|
// Update the serviceIPs map atomically.
|
||||||
r.mu.Lock()
|
r.mu.Lock()
|
||||||
r.serviceIPs = newServiceIPs
|
r.serviceIPs = newServiceIPs
|
||||||
r.mu.Unlock()
|
r.mu.Unlock()
|
||||||
|
|
||||||
r.log.Debug("DNS records updated.", "services", len(newServiceIPs)/3, "containers", containersCount)
|
r.log.Info("DNS records updated.", "services", len(newServiceIPs)/3, "containers", containersCount)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resolve returns IP addresses of the service containers.
|
// Resolve returns IP addresses of the service containers.
|
||||||
|
|||||||
@@ -0,0 +1,77 @@
|
|||||||
|
package dns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/docker/docker/api/types/container"
|
||||||
|
"github.com/docker/docker/api/types/network"
|
||||||
|
"github.com/psviderski/uncloud/internal/machine/store"
|
||||||
|
"github.com/psviderski/uncloud/pkg/api"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestClusterResolver_UpdateServiceIPs(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
containers := []store.ContainerRecord{
|
||||||
|
newRecord("svc-id-1", "web", "10.210.0.2", "mach-1"),
|
||||||
|
newRecord("svc-id-1", "web", "10.210.0.3", "mach-2"),
|
||||||
|
newRecord("svc-id-2", "api", "10.210.1.2", "mach-1"),
|
||||||
|
}
|
||||||
|
|
||||||
|
r := NewClusterResolver(nil)
|
||||||
|
r.updateServiceIPs(containers)
|
||||||
|
|
||||||
|
firstMapPtr := reflect.ValueOf(r.serviceIPs).Pointer()
|
||||||
|
assert.NotZero(t, firstMapPtr, "first call should populate the map")
|
||||||
|
assert.NotEmpty(t, r.Resolve("web"))
|
||||||
|
assert.NotEmpty(t, r.Resolve("api"))
|
||||||
|
|
||||||
|
// Second call with the same input must not swap the map.
|
||||||
|
r.updateServiceIPs(containers)
|
||||||
|
assert.Equal(t, firstMapPtr, reflect.ValueOf(r.serviceIPs).Pointer(),
|
||||||
|
"second call with identical input must not rewrite the map")
|
||||||
|
|
||||||
|
// Reordering the input also must not rewrite the map.
|
||||||
|
reordered := []store.ContainerRecord{containers[2], containers[0], containers[1]}
|
||||||
|
r.updateServiceIPs(reordered)
|
||||||
|
assert.Equal(t, firstMapPtr, reflect.ValueOf(r.serviceIPs).Pointer(),
|
||||||
|
"reordered input with same containers must not rewrite the map")
|
||||||
|
|
||||||
|
// A real change must rewrite the map.
|
||||||
|
changed := append([]store.ContainerRecord{}, containers...)
|
||||||
|
changed = append(changed, newRecord("svc-id-3", "db", "10.210.2.2", "mach-1"))
|
||||||
|
r.updateServiceIPs(changed)
|
||||||
|
assert.NotEqual(t, firstMapPtr, reflect.ValueOf(r.serviceIPs).Pointer(),
|
||||||
|
"adding a new service should rewrite the map")
|
||||||
|
assert.NotEmpty(t, r.Resolve("db"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func newRecord(serviceID, serviceName, ip, machineID string) store.ContainerRecord {
|
||||||
|
return store.ContainerRecord{
|
||||||
|
Container: api.ServiceContainer{
|
||||||
|
Container: api.Container{
|
||||||
|
InspectResponse: container.InspectResponse{
|
||||||
|
ContainerJSONBase: &container.ContainerJSONBase{
|
||||||
|
ID: serviceName + "-" + ip,
|
||||||
|
State: &container.State{Running: true},
|
||||||
|
},
|
||||||
|
NetworkSettings: &container.NetworkSettings{
|
||||||
|
Networks: map[string]*network.EndpointSettings{
|
||||||
|
// Hardcoded to avoid an import cycle via internal/machine/docker.
|
||||||
|
"uncloud": {IPAddress: ip},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Config: &container.Config{
|
||||||
|
Labels: map[string]string{
|
||||||
|
api.LabelServiceID: serviceID,
|
||||||
|
api.LabelServiceName: serviceName,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MachineID: machineID,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -420,34 +420,6 @@ func (c *Client) RemoveVolume(ctx context.Context, id string, force bool) error
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateServiceContainer creates a new container for the service with the given specifications.
|
|
||||||
func (c *Client) CreateServiceContainer(
|
|
||||||
ctx context.Context, serviceID string, spec api.ServiceSpec, containerName string,
|
|
||||||
) (container.CreateResponse, error) {
|
|
||||||
var resp container.CreateResponse
|
|
||||||
|
|
||||||
specBytes, err := json.Marshal(spec)
|
|
||||||
if err != nil {
|
|
||||||
return resp, fmt.Errorf("marshal service spec: %w", err)
|
|
||||||
}
|
|
||||||
grpcResp, err := c.GRPCClient.CreateServiceContainer(ctx, &pb.CreateServiceContainerRequest{
|
|
||||||
ServiceId: serviceID,
|
|
||||||
ServiceSpec: specBytes,
|
|
||||||
ContainerName: containerName,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
if status.Convert(err).Code() == codes.NotFound {
|
|
||||||
return resp, errdefs.NotFound(err)
|
|
||||||
}
|
|
||||||
return resp, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = json.Unmarshal(grpcResp.Response, &resp); err != nil {
|
|
||||||
return resp, fmt.Errorf("unmarshal gRPC response: %w", err)
|
|
||||||
}
|
|
||||||
return resp, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// InspectServiceContainer returns the container information and service specification that was used to create the
|
// InspectServiceContainer returns the container information and service specification that was used to create the
|
||||||
// container with the given ID.
|
// container with the given ID.
|
||||||
func (c *Client) InspectServiceContainer(ctx context.Context, id string) (api.ServiceContainer, error) {
|
func (c *Client) InspectServiceContainer(ctx context.Context, id string) (api.ServiceContainer, error) {
|
||||||
@@ -474,10 +446,13 @@ func (c *Client) InspectServiceContainer(ctx context.Context, id string) (api.Se
|
|||||||
type MachineServiceContainers struct {
|
type MachineServiceContainers struct {
|
||||||
Metadata *pb.Metadata
|
Metadata *pb.Metadata
|
||||||
Containers []api.ServiceContainer
|
Containers []api.ServiceContainer
|
||||||
|
// HookContainers are one-shot containers for deployment hooks (e.g. pre-deploy).
|
||||||
|
HookContainers []api.ServiceContainer
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListServiceContainers returns all containers on requested machines that belong to the service with the given
|
// ListServiceContainers returns all containers on requested machines that belong to the service with the given
|
||||||
// name or ID. If serviceNameOrID is empty, all service containers are returned.
|
// name or ID. If serviceNameOrID is empty, all service containers are returned.
|
||||||
|
// Set opts.All to true to include hook containers.
|
||||||
func (c *Client) ListServiceContainers(
|
func (c *Client) ListServiceContainers(
|
||||||
ctx context.Context, serviceNameOrID string, opts container.ListOptions,
|
ctx context.Context, serviceNameOrID string, opts container.ListOptions,
|
||||||
) ([]MachineServiceContainers, error) {
|
) ([]MachineServiceContainers, error) {
|
||||||
@@ -501,22 +476,33 @@ func (c *Client) ListServiceContainers(
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
containers := make([]api.ServiceContainer, len(msg.Containers))
|
machineContainers[i].Containers, err = serviceContainersFromProto(msg.Containers)
|
||||||
for j, sc := range msg.Containers {
|
if err != nil {
|
||||||
if err = json.Unmarshal(sc.Container, &containers[j].Container); err != nil {
|
return nil, err
|
||||||
return nil, fmt.Errorf("unmarshal container: %w", err)
|
}
|
||||||
}
|
machineContainers[i].HookContainers, err = serviceContainersFromProto(msg.HookContainers)
|
||||||
if err = json.Unmarshal(sc.ServiceSpec, &containers[j].ServiceSpec); err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("unmarshal service spec: %w", err)
|
return nil, err
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
machineContainers[i].Containers = containers
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return machineContainers, nil
|
return machineContainers, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// serviceContainersFromProto converts a slice of protobuf service containers to api.ServiceContainer.
|
||||||
|
func serviceContainersFromProto(pbContainers []*pb.ServiceContainer) ([]api.ServiceContainer, error) {
|
||||||
|
containers := make([]api.ServiceContainer, len(pbContainers))
|
||||||
|
for i, sc := range pbContainers {
|
||||||
|
if err := json.Unmarshal(sc.Container, &containers[i].Container); err != nil {
|
||||||
|
return nil, fmt.Errorf("unmarshal container: %w", err)
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal(sc.ServiceSpec, &containers[i].ServiceSpec); err != nil {
|
||||||
|
return nil, fmt.Errorf("unmarshal service spec: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return containers, nil
|
||||||
|
}
|
||||||
|
|
||||||
// RemoveServiceContainer stops (kills after grace period) and removes a service container with the given ID.
|
// RemoveServiceContainer stops (kills after grace period) and removes a service container with the given ID.
|
||||||
// A service container is a container that has been created with CreateServiceContainer.
|
// A service container is a container that has been created with CreateServiceContainer.
|
||||||
func (c *Client) RemoveServiceContainer(ctx context.Context, id string, opts container.RemoveOptions) error {
|
func (c *Client) RemoveServiceContainer(ctx context.Context, id string, opts container.RemoveOptions) error {
|
||||||
|
|||||||
@@ -112,6 +112,7 @@ func (c *Controller) WatchAndSyncContainers(ctx context.Context) error {
|
|||||||
events.ActionDie,
|
events.ActionDie,
|
||||||
events.ActionOOM,
|
events.ActionOOM,
|
||||||
events.ActionDestroy,
|
events.ActionDestroy,
|
||||||
|
events.ActionHealthStatusRunning,
|
||||||
events.ActionHealthStatusHealthy,
|
events.ActionHealthStatusHealthy,
|
||||||
events.ActionHealthStatusUnhealthy:
|
events.ActionHealthStatusUnhealthy:
|
||||||
|
|
||||||
@@ -152,12 +153,16 @@ func (c *Controller) syncContainersToStore(ctx context.Context) error {
|
|||||||
return fmt.Errorf("list containers from store: %w", err)
|
return fmt.Errorf("list containers from store: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
containers, err := c.service.ListServiceContainers(ctx, "", container.ListOptions{})
|
// List containers for all services, including stopped ones and deployment hooks.
|
||||||
|
result, err := c.service.ListServiceContainers(ctx, "", container.ListOptions{All: true})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// TODO: mark all containers as outdated in the store.
|
// TODO: mark all containers as outdated in the store.
|
||||||
return fmt.Errorf("list service containers: %w", err)
|
return fmt.Errorf("list service containers: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sync both regular and one-off hook containers to the store.
|
||||||
|
containers := append(result.Containers, result.HookContainers...)
|
||||||
|
|
||||||
// Delete containers from the store that are no longer present in the Docker daemon.
|
// Delete containers from the store that are no longer present in the Docker daemon.
|
||||||
var deleteIDs []string
|
var deleteIDs []string
|
||||||
for _, sc := range storeContainers {
|
for _, sc := range storeContainers {
|
||||||
|
|||||||
@@ -664,6 +664,39 @@ func (s *Server) CreateServiceContainer(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Strip the container configuration that doesn't make sense for the pre-deploy hook and apply its overrides.
|
||||||
|
if spec.PreDeploy != nil && req.ContainerType == pb.CreateServiceContainerRequest_PRE_DEPLOY {
|
||||||
|
config.Labels = map[string]string{
|
||||||
|
api.LabelServiceID: req.ServiceId,
|
||||||
|
api.LabelServiceName: spec.Name,
|
||||||
|
api.LabelHook: api.LabelHookPreDeploy,
|
||||||
|
api.LabelManaged: "",
|
||||||
|
}
|
||||||
|
config.Healthcheck = &container.HealthConfig{
|
||||||
|
Test: []string{"NONE"},
|
||||||
|
}
|
||||||
|
hostConfig.PortBindings = nil
|
||||||
|
hostConfig.RestartPolicy = container.RestartPolicy{
|
||||||
|
Name: container.RestartPolicyDisabled,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply the pre-deploy hook overrides.
|
||||||
|
config.Cmd = spec.PreDeploy.Command
|
||||||
|
|
||||||
|
for k, v := range spec.PreDeploy.Env {
|
||||||
|
envVars[k] = v
|
||||||
|
}
|
||||||
|
envVars["UNCLOUD_HOOK_PRE_DEPLOY"] = "true"
|
||||||
|
config.Env = envVars.ToSlice()
|
||||||
|
|
||||||
|
if spec.PreDeploy.Privileged != nil {
|
||||||
|
hostConfig.Privileged = *spec.PreDeploy.Privileged
|
||||||
|
}
|
||||||
|
if spec.PreDeploy.User != "" {
|
||||||
|
config.User = spec.PreDeploy.User
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
networkConfig := &network.NetworkingConfig{
|
networkConfig := &network.NetworkingConfig{
|
||||||
EndpointsConfig: map[string]*network.EndpointSettings{
|
EndpointsConfig: map[string]*network.EndpointSettings{
|
||||||
NetworkName: {},
|
NetworkName: {},
|
||||||
@@ -1009,37 +1042,49 @@ func (s *Server) ListServiceContainers(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
containers, err := s.service.ListServiceContainers(ctx, req.ServiceId, opts)
|
result, err := s.service.ListServiceContainers(ctx, req.ServiceId, opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, status.Error(codes.Internal, err.Error())
|
return nil, status.Error(codes.Internal, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert to protobuf format.
|
pbContainers, err := serviceContainersToProto(result.Containers)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
pbHookContainers, err := serviceContainersToProto(result.HookContainers)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &pb.ListServiceContainersResponse{
|
||||||
|
Messages: []*pb.MachineServiceContainers{
|
||||||
|
{
|
||||||
|
Containers: pbContainers,
|
||||||
|
HookContainers: pbHookContainers,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// serviceContainersToProto converts a slice of service containers to protobuf format.
|
||||||
|
func serviceContainersToProto(containers []api.ServiceContainer) ([]*pb.ServiceContainer, error) {
|
||||||
pbContainers := make([]*pb.ServiceContainer, 0, len(containers))
|
pbContainers := make([]*pb.ServiceContainer, 0, len(containers))
|
||||||
for _, ctr := range containers {
|
for _, ctr := range containers {
|
||||||
ctrBytes, err := json.Marshal(ctr.Container)
|
ctrBytes, err := json.Marshal(ctr.Container)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, status.Errorf(codes.Internal, "marshal container: %v", err)
|
return nil, status.Errorf(codes.Internal, "marshal container: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
specBytes, err := json.Marshal(ctr.ServiceSpec)
|
specBytes, err := json.Marshal(ctr.ServiceSpec)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, status.Errorf(codes.Internal, "marshal service spec: %v", err)
|
return nil, status.Errorf(codes.Internal, "marshal service spec: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
pbContainers = append(pbContainers, &pb.ServiceContainer{
|
pbContainers = append(pbContainers, &pb.ServiceContainer{
|
||||||
Container: ctrBytes,
|
Container: ctrBytes,
|
||||||
ServiceSpec: specBytes,
|
ServiceSpec: specBytes,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return &pb.ListServiceContainersResponse{
|
return pbContainers, nil
|
||||||
Messages: []*pb.MachineServiceContainers{
|
|
||||||
{
|
|
||||||
Containers: pbContainers,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveServiceContainer stops (kills after grace period) and removes a service container with the given ID.
|
// RemoveServiceContainer stops (kills after grace period) and removes a service container with the given ID.
|
||||||
@@ -1078,20 +1123,19 @@ const logsHeartbeatInterval = 200 * time.Millisecond
|
|||||||
|
|
||||||
// ContainerLogs streams logs from a container.
|
// ContainerLogs streams logs from a container.
|
||||||
func (s *Server) ContainerLogs(
|
func (s *Server) ContainerLogs(
|
||||||
req *pb.ContainerLogsRequest, stream grpc.ServerStreamingServer[pb.ContainerLogEntry],
|
req *pb.LogsRequest, stream grpc.ServerStreamingServer[pb.LogEntry],
|
||||||
) error {
|
) error {
|
||||||
// Stream context is cancelled when the client has disconnected or the stream has ended.
|
// Stream context is cancelled when the client has disconnected or the stream has ended.
|
||||||
ctx := stream.Context()
|
ctx := stream.Context()
|
||||||
|
|
||||||
opts := ContainerLogsOptions{
|
opts := api.ServiceLogsOptions{
|
||||||
ContainerID: req.ContainerId,
|
Follow: req.Follow,
|
||||||
Follow: req.Follow,
|
Tail: int(req.Tail),
|
||||||
Tail: int(req.Tail),
|
Since: req.Since,
|
||||||
Since: req.Since,
|
Until: req.Until,
|
||||||
Until: req.Until,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
logsCh, err := s.service.ContainerLogs(ctx, opts)
|
logsCh, err := s.service.ContainerLogs(ctx, req.Id, opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errdefs.IsNotFound(err) {
|
if errdefs.IsNotFound(err) {
|
||||||
return status.Error(codes.NotFound, err.Error())
|
return status.Error(codes.NotFound, err.Error())
|
||||||
@@ -1099,7 +1143,7 @@ func (s *Server) ContainerLogs(
|
|||||||
return status.Errorf(codes.Internal, "get container logs: %v", err)
|
return status.Errorf(codes.Internal, "get container logs: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
log := slog.With("container_id", req.ContainerId, "stream_id", fmt.Sprintf("%p", stream)[2:])
|
log := slog.With("container_id", req.Id, "stream_id", fmt.Sprintf("%p", stream)[2:])
|
||||||
log.Debug("Starting container logs streaming.",
|
log.Debug("Starting container logs streaming.",
|
||||||
"follow", req.Follow, "tail", req.Tail, "since", req.Since, "until", req.Until)
|
"follow", req.Follow, "tail", req.Tail, "since", req.Since, "until", req.Until)
|
||||||
|
|
||||||
@@ -1127,7 +1171,7 @@ func (s *Server) ContainerLogs(
|
|||||||
return status.Error(codes.Internal, entry.Err.Error())
|
return status.Error(codes.Internal, entry.Err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
pbEntry := &pb.ContainerLogEntry{
|
pbEntry := &pb.LogEntry{
|
||||||
Stream: api.LogStreamTypeToProto(entry.Stream),
|
Stream: api.LogStreamTypeToProto(entry.Stream),
|
||||||
Timestamp: timestamppb.New(entry.Timestamp),
|
Timestamp: timestamppb.New(entry.Timestamp),
|
||||||
Message: entry.Message,
|
Message: entry.Message,
|
||||||
@@ -1148,8 +1192,8 @@ func (s *Server) ContainerLogs(
|
|||||||
// Use the timestamp one heartbeat in the past to be conservative. This reduces the chance of sending
|
// Use the timestamp one heartbeat in the past to be conservative. This reduces the chance of sending
|
||||||
// a timestamp that is greater than a log entry currently being parsed but not yet sent, which would
|
// a timestamp that is greater than a log entry currently being parsed but not yet sent, which would
|
||||||
// cause the client to incorrectly believe it has received all logs up to that point.
|
// cause the client to incorrectly believe it has received all logs up to that point.
|
||||||
heartbeat := &pb.ContainerLogEntry{
|
heartbeat := &pb.LogEntry{
|
||||||
Stream: pb.ContainerLogEntry_HEARTBEAT,
|
Stream: pb.LogEntry_HEARTBEAT,
|
||||||
Timestamp: timestamppb.New(now.Add(-logsHeartbeatInterval)),
|
Timestamp: timestamppb.New(now.Add(-logsHeartbeatInterval)),
|
||||||
}
|
}
|
||||||
if err = stream.Send(heartbeat); err != nil {
|
if err = stream.Send(heartbeat); err != nil {
|
||||||
|
|||||||
@@ -74,11 +74,20 @@ func (s *Service) InspectServiceContainer(ctx context.Context, nameOrID string)
|
|||||||
return serviceCtr, nil
|
return serviceCtr, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ListServiceContainersResult holds the result of listing service containers, split into regular
|
||||||
|
// service containers and one-off hook containers.
|
||||||
|
type ListServiceContainersResult struct {
|
||||||
|
Containers []api.ServiceContainer
|
||||||
|
HookContainers []api.ServiceContainer
|
||||||
|
}
|
||||||
|
|
||||||
// ListServiceContainers lists Docker containers that belong to the service with the given name or ID.
|
// ListServiceContainers lists Docker containers that belong to the service with the given name or ID.
|
||||||
// If serviceIDOrName is empty, all service containers are returned. The opts parameter allows additional filtering.
|
// If serviceIDOrName is empty, all service containers are returned. The opts parameter allows additional filtering.
|
||||||
func (s *Service) ListServiceContainers(
|
func (s *Service) ListServiceContainers(
|
||||||
ctx context.Context, serviceNameOrID string, opts container.ListOptions,
|
ctx context.Context, serviceNameOrID string, opts container.ListOptions,
|
||||||
) ([]api.ServiceContainer, error) {
|
) (ListServiceContainersResult, error) {
|
||||||
|
var result ListServiceContainersResult
|
||||||
|
|
||||||
if opts.Filters.Len() == 0 {
|
if opts.Filters.Len() == 0 {
|
||||||
opts.Filters = filters.NewArgs()
|
opts.Filters = filters.NewArgs()
|
||||||
}
|
}
|
||||||
@@ -88,10 +97,9 @@ func (s *Service) ListServiceContainers(
|
|||||||
|
|
||||||
containerSummaries, err := s.Client.ContainerList(ctx, opts)
|
containerSummaries, err := s.Client.ContainerList(ctx, opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return result, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var containers []api.ServiceContainer
|
|
||||||
for _, cs := range containerSummaries {
|
for _, cs := range containerSummaries {
|
||||||
// Filter by service name or ID if provided.
|
// Filter by service name or ID if provided.
|
||||||
if serviceNameOrID != "" &&
|
if serviceNameOrID != "" &&
|
||||||
@@ -106,10 +114,15 @@ func (s *Service) ListServiceContainers(
|
|||||||
slog.Error("Failed to inspect service container.", "service", serviceNameOrID, "id", cs.ID, "err", err)
|
slog.Error("Failed to inspect service container.", "service", serviceNameOrID, "id", cs.ID, "err", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
containers = append(containers, ctr)
|
|
||||||
|
if ctr.IsHook() {
|
||||||
|
result.HookContainers = append(result.HookContainers, ctr)
|
||||||
|
} else {
|
||||||
|
result.Containers = append(result.Containers, ctr)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return containers, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsContainerdImageStoreEnabled checks if Docker is configured to use the containerd image store:
|
// IsContainerdImageStoreEnabled checks if Docker is configured to use the containerd image store:
|
||||||
@@ -155,18 +168,9 @@ func (s *Service) ListImages(ctx context.Context, opts image.ListOptions) (Image
|
|||||||
return imagesResp, nil
|
return imagesResp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ContainerLogsOptions specifies parameters for ContainerLogs.
|
|
||||||
type ContainerLogsOptions struct {
|
|
||||||
ContainerID string
|
|
||||||
Follow bool
|
|
||||||
Tail int
|
|
||||||
Since string
|
|
||||||
Until string
|
|
||||||
}
|
|
||||||
|
|
||||||
// ContainerLogs streams logs from a container and returns demultiplexed entries via a channel.
|
// ContainerLogs streams logs from a container and returns demultiplexed entries via a channel.
|
||||||
// The channel is closed when streaming completes or context is cancelled.
|
// The channel is closed when streaming completes or context is cancelled.
|
||||||
func (s *Service) ContainerLogs(ctx context.Context, opts ContainerLogsOptions) (<-chan api.ContainerLogEntry, error) {
|
func (s *Service) ContainerLogs(ctx context.Context, containerID string, opts api.ServiceLogsOptions) (<-chan api.LogEntry, error) {
|
||||||
dockerOpts := container.LogsOptions{
|
dockerOpts := container.LogsOptions{
|
||||||
ShowStdout: true,
|
ShowStdout: true,
|
||||||
ShowStderr: true,
|
ShowStderr: true,
|
||||||
@@ -177,12 +181,12 @@ func (s *Service) ContainerLogs(ctx context.Context, opts ContainerLogsOptions)
|
|||||||
Timestamps: true,
|
Timestamps: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
reader, err := s.Client.ContainerLogs(ctx, opts.ContainerID, dockerOpts)
|
reader, err := s.Client.ContainerLogs(ctx, containerID, dockerOpts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
outCh := make(chan api.ContainerLogEntry)
|
outCh := make(chan api.LogEntry)
|
||||||
stdoutWriter := &logsChannelWriter{ctx: ctx, ch: outCh, isStderr: false}
|
stdoutWriter := &logsChannelWriter{ctx: ctx, ch: outCh, isStderr: false}
|
||||||
stderrWriter := &logsChannelWriter{ctx: ctx, ch: outCh, isStderr: true}
|
stderrWriter := &logsChannelWriter{ctx: ctx, ch: outCh, isStderr: true}
|
||||||
|
|
||||||
@@ -198,7 +202,7 @@ func (s *Service) ContainerLogs(ctx context.Context, opts ContainerLogsOptions)
|
|||||||
if _, err := stdcopy.StdCopy(stdoutWriter, stderrWriter, reader); err != nil {
|
if _, err := stdcopy.StdCopy(stdoutWriter, stderrWriter, reader); err != nil {
|
||||||
// Send error as the last entry.
|
// Send error as the last entry.
|
||||||
select {
|
select {
|
||||||
case outCh <- api.ContainerLogEntry{Err: fmt.Errorf("demultiplex container logs: %w", err)}:
|
case outCh <- api.LogEntry{Err: fmt.Errorf("demultiplex container logs: %w", err)}:
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -216,7 +220,7 @@ func (s *Service) ContainerLogs(ctx context.Context, opts ContainerLogsOptions)
|
|||||||
// logsChannelWriter is a writer for stdcopy.StdCopy that sends demultiplexed container logs to a channel.
|
// logsChannelWriter is a writer for stdcopy.StdCopy that sends demultiplexed container logs to a channel.
|
||||||
type logsChannelWriter struct {
|
type logsChannelWriter struct {
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
ch chan<- api.ContainerLogEntry
|
ch chan<- api.LogEntry
|
||||||
isStderr bool
|
isStderr bool
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -236,7 +240,7 @@ func (w *logsChannelWriter) Write(data []byte) (n int, err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
entry := api.ContainerLogEntry{
|
entry := api.LogEntry{
|
||||||
Timestamp: timestamp,
|
Timestamp: timestamp,
|
||||||
// Clone is required because message is a slice into data, which stdcopy.StdCopy may reuse
|
// Clone is required because message is a slice into data, which stdcopy.StdCopy may reuse
|
||||||
// after Write returns but before the entry is consumed from the channel.
|
// after Write returns but before the entry is consumed from the channel.
|
||||||
|
|||||||
+170
-24
@@ -14,12 +14,16 @@ import (
|
|||||||
"slices"
|
"slices"
|
||||||
"strconv"
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/containerd/errdefs"
|
||||||
"github.com/docker/docker/client"
|
"github.com/docker/docker/client"
|
||||||
"github.com/docker/go-connections/sockets"
|
"github.com/docker/go-connections/sockets"
|
||||||
"github.com/psviderski/uncloud/internal/corrosion"
|
"github.com/psviderski/uncloud/internal/corrosion"
|
||||||
"github.com/psviderski/uncloud/internal/docker"
|
"github.com/psviderski/uncloud/internal/docker"
|
||||||
"github.com/psviderski/uncloud/internal/fs"
|
"github.com/psviderski/uncloud/internal/fs"
|
||||||
|
"github.com/psviderski/uncloud/internal/grpcversion"
|
||||||
|
"github.com/psviderski/uncloud/internal/journal"
|
||||||
"github.com/psviderski/uncloud/internal/machine/api/pb"
|
"github.com/psviderski/uncloud/internal/machine/api/pb"
|
||||||
apiproxy "github.com/psviderski/uncloud/internal/machine/api/proxy"
|
apiproxy "github.com/psviderski/uncloud/internal/machine/api/proxy"
|
||||||
"github.com/psviderski/uncloud/internal/machine/caddyconfig"
|
"github.com/psviderski/uncloud/internal/machine/caddyconfig"
|
||||||
@@ -30,6 +34,7 @@ import (
|
|||||||
machinedocker "github.com/psviderski/uncloud/internal/machine/docker"
|
machinedocker "github.com/psviderski/uncloud/internal/machine/docker"
|
||||||
"github.com/psviderski/uncloud/internal/machine/network"
|
"github.com/psviderski/uncloud/internal/machine/network"
|
||||||
"github.com/psviderski/uncloud/internal/machine/store"
|
"github.com/psviderski/uncloud/internal/machine/store"
|
||||||
|
"github.com/psviderski/uncloud/pkg/api"
|
||||||
"github.com/psviderski/unregistry"
|
"github.com/psviderski/unregistry"
|
||||||
"github.com/siderolabs/grpc-proxy/proxy"
|
"github.com/siderolabs/grpc-proxy/proxy"
|
||||||
"golang.org/x/sync/errgroup"
|
"golang.org/x/sync/errgroup"
|
||||||
@@ -37,6 +42,7 @@ import (
|
|||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
"google.golang.org/grpc/status"
|
"google.golang.org/grpc/status"
|
||||||
|
"google.golang.org/protobuf/types/known/durationpb"
|
||||||
"google.golang.org/protobuf/types/known/emptypb"
|
"google.golang.org/protobuf/types/known/emptypb"
|
||||||
"google.golang.org/protobuf/types/known/timestamppb"
|
"google.golang.org/protobuf/types/known/timestamppb"
|
||||||
)
|
)
|
||||||
@@ -98,27 +104,6 @@ func (c *Config) SetDefaults() (*Config, error) {
|
|||||||
}
|
}
|
||||||
cfg.DockerClient = cli
|
cfg.DockerClient = cli
|
||||||
}
|
}
|
||||||
if cfg.ContainerdSockPath == "" {
|
|
||||||
// Auto-detect the containerd.sock path used by Docker.
|
|
||||||
paths := []string{
|
|
||||||
"/run/containerd/containerd.sock", // Default path on most Linux distributions.
|
|
||||||
"/run/docker/containerd/containerd.sock",
|
|
||||||
"/var/run/containerd/containerd.sock",
|
|
||||||
"/var/run/docker/containerd/containerd.sock",
|
|
||||||
}
|
|
||||||
for _, path := range paths {
|
|
||||||
if _, err := os.Stat(path); err == nil {
|
|
||||||
cfg.ContainerdSockPath = path
|
|
||||||
slog.Debug("Detected containerd socket used by Docker.", "path", path)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg.ContainerdSockPath == "" {
|
|
||||||
slog.Warn("Failed to auto-detect containerd socket used by Docker.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg.CorrosionDir == "" {
|
if cfg.CorrosionDir == "" {
|
||||||
cfg.CorrosionDir = filepath.Join(cfg.DataDir, "corrosion")
|
cfg.CorrosionDir = filepath.Join(cfg.DataDir, "corrosion")
|
||||||
}
|
}
|
||||||
@@ -268,6 +253,8 @@ func NewMachine(config *Config) (*Machine, error) {
|
|||||||
proxyDirector := apiproxy.NewDirector(config.MachineSockPath, constants.MachineAPIPort)
|
proxyDirector := apiproxy.NewDirector(config.MachineSockPath, constants.MachineAPIPort)
|
||||||
localProxyServer := grpc.NewServer(
|
localProxyServer := grpc.NewServer(
|
||||||
grpc.ForceServerCodecV2(proxy.Codec()),
|
grpc.ForceServerCodecV2(proxy.Codec()),
|
||||||
|
grpc.UnaryInterceptor(grpcversion.ServerUnaryInterceptor),
|
||||||
|
grpc.StreamInterceptor(grpcversion.ServerStreamInterceptor),
|
||||||
grpc.UnknownServiceHandler(
|
grpc.UnknownServiceHandler(
|
||||||
proxy.TransparentHandler(proxyDirector.Director),
|
proxy.TransparentHandler(proxyDirector.Director),
|
||||||
),
|
),
|
||||||
@@ -332,6 +319,31 @@ func (m *Machine) Initialised() bool {
|
|||||||
return m.state.ID != ""
|
return m.state.ID != ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ContainerdSock returns the path to the containerd socket used by Docker, auto-discovering it from well-known
|
||||||
|
// locations if it's not explicitly configured.
|
||||||
|
// Returns an empty string if the socket cannot be detected.
|
||||||
|
func (m *Machine) ContainerdSock() string {
|
||||||
|
if m.config.ContainerdSockPath != "" {
|
||||||
|
return m.config.ContainerdSockPath
|
||||||
|
}
|
||||||
|
|
||||||
|
paths := []string{
|
||||||
|
"/run/containerd/containerd.sock", // Default path on most Linux distributions.
|
||||||
|
"/run/docker/containerd/containerd.sock",
|
||||||
|
"/var/run/containerd/containerd.sock",
|
||||||
|
"/var/run/docker/containerd/containerd.sock",
|
||||||
|
}
|
||||||
|
for _, path := range paths {
|
||||||
|
if _, err := os.Stat(path); err == nil {
|
||||||
|
slog.Debug("Detected containerd socket used by Docker.", "path", path)
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
slog.Warn("Failed to auto-detect containerd socket used by Docker.")
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
// IP returns the machine IPv4 address in the cluster network which is the first address in the machine subnet.
|
// IP returns the machine IPv4 address in the cluster network which is the first address in the machine subnet.
|
||||||
func (m *Machine) IP() netip.Addr {
|
func (m *Machine) IP() netip.Addr {
|
||||||
if !m.Initialised() {
|
if !m.Initialised() {
|
||||||
@@ -421,6 +433,8 @@ func (m *Machine) Run(ctx context.Context) error {
|
|||||||
m.proxyDirector.UpdateLocalAddress(m.state.Network.ManagementIP.String())
|
m.proxyDirector.UpdateLocalAddress(m.state.Network.ManagementIP.String())
|
||||||
proxyServer := grpc.NewServer(
|
proxyServer := grpc.NewServer(
|
||||||
grpc.ForceServerCodecV2(proxy.Codec()),
|
grpc.ForceServerCodecV2(proxy.Codec()),
|
||||||
|
grpc.UnaryInterceptor(grpcversion.ServerUnaryInterceptor),
|
||||||
|
grpc.StreamInterceptor(grpcversion.ServerStreamInterceptor),
|
||||||
grpc.UnknownServiceHandler(
|
grpc.UnknownServiceHandler(
|
||||||
proxy.TransparentHandler(m.proxyDirector.Director),
|
proxy.TransparentHandler(m.proxyDirector.Director),
|
||||||
),
|
),
|
||||||
@@ -450,7 +464,7 @@ func (m *Machine) Run(ctx context.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var unreg *unregistry.Registry
|
var unreg *unregistry.Registry
|
||||||
if m.config.ContainerdSockPath != "" {
|
if containerdSock := m.ContainerdSock(); containerdSock != "" {
|
||||||
isContainerdStore, err := m.dockerService.IsContainerdImageStoreEnabled(ctx)
|
isContainerdStore, err := m.dockerService.IsContainerdImageStoreEnabled(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("check if Docker uses containerd image store: %w", err)
|
return fmt.Errorf("check if Docker uses containerd image store: %w", err)
|
||||||
@@ -462,7 +476,7 @@ func (m *Machine) Run(ctx context.Context) error {
|
|||||||
unreg, err = unregistry.NewRegistry(unregistry.Config{
|
unreg, err = unregistry.NewRegistry(unregistry.Config{
|
||||||
Addr: net.JoinHostPort(m.IP().String(), strconv.Itoa(constants.UnregistryPort)),
|
Addr: net.JoinHostPort(m.IP().String(), strconv.Itoa(constants.UnregistryPort)),
|
||||||
ContainerdNamespace: "moby",
|
ContainerdNamespace: "moby",
|
||||||
ContainerdSock: m.config.ContainerdSockPath,
|
ContainerdSock: containerdSock,
|
||||||
LogFormatter: "text",
|
LogFormatter: "text",
|
||||||
LogLevel: "info",
|
LogLevel: "info",
|
||||||
})
|
})
|
||||||
@@ -473,7 +487,7 @@ func (m *Machine) Run(ctx context.Context) error {
|
|||||||
slog.Warn("Skipping embedded unregistry setup as Docker is not using the containerd image store.")
|
slog.Warn("Skipping embedded unregistry setup as Docker is not using the containerd image store.")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
slog.Warn("Skipping embedded unregistry setup as the containerd socket path is not configured.")
|
slog.Warn("Skipping embedded unregistry setup as the containerd socket path could not be detected.")
|
||||||
}
|
}
|
||||||
|
|
||||||
m.mu.Lock()
|
m.mu.Lock()
|
||||||
@@ -903,6 +917,14 @@ func (m *Machine) InspectMachine(ctx context.Context, _ *emptypb.Empty) (*pb.Ins
|
|||||||
return nil, status.Errorf(codes.Internal, "get database version of the cluster store: %v", err)
|
return nil, status.Errorf(codes.Internal, "get database version of the cluster store: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var rtts map[string]*pb.RTTStats
|
||||||
|
if m.Initialised() {
|
||||||
|
rtts, err = m.getMachineRTTs(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return &pb.InspectMachineResponse{
|
return &pb.InspectMachineResponse{
|
||||||
Machines: []*pb.MachineDetails{
|
Machines: []*pb.MachineDetails{
|
||||||
{
|
{
|
||||||
@@ -917,11 +939,46 @@ func (m *Machine) InspectMachine(ctx context.Context, _ *emptypb.Empty) (*pb.Ins
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
StoreDbVersion: dbVersion,
|
StoreDbVersion: dbVersion,
|
||||||
|
Rtts: rtts,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getMachineRTTs retrieves round-trip times to other machines in the cluster.
|
||||||
|
func (m *Machine) getMachineRTTs(ctx context.Context) (map[string]*pb.RTTStats, error) {
|
||||||
|
rtts, err := m.cluster.MemberRTTs()
|
||||||
|
if err != nil {
|
||||||
|
return nil, status.Errorf(codes.Internal, "get member rtts: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// List machines to map IPs to Machine IDs.
|
||||||
|
machines, err := m.store.ListMachines(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, status.Errorf(codes.Internal, "list machines: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map Management IP -> Machine ID
|
||||||
|
ipToMachineID := make(map[netip.Addr]string)
|
||||||
|
for _, mach := range machines {
|
||||||
|
ip, _ := mach.Network.ManagementIp.ToAddr()
|
||||||
|
ipToMachineID[ip] = mach.Id
|
||||||
|
}
|
||||||
|
|
||||||
|
pbRTTs := make(map[string]*pb.RTTStats)
|
||||||
|
for _, stats := range rtts {
|
||||||
|
// Corrosion uses the management IP for gossip.
|
||||||
|
if mid, ok := ipToMachineID[stats.Addr.Addr()]; ok {
|
||||||
|
pbRTTs[mid] = &pb.RTTStats{
|
||||||
|
Median: durationpb.New(stats.Median),
|
||||||
|
StdDev: durationpb.New(stats.StdDev),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pbRTTs, nil
|
||||||
|
}
|
||||||
|
|
||||||
// IsNetworkReady returns true if the Docker network is ready for containers.
|
// IsNetworkReady returns true if the Docker network is ready for containers.
|
||||||
func (m *Machine) IsNetworkReady() bool {
|
func (m *Machine) IsNetworkReady() bool {
|
||||||
if !m.Initialised() {
|
if !m.Initialised() {
|
||||||
@@ -1073,3 +1130,92 @@ func (m *Machine) InspectService(
|
|||||||
}
|
}
|
||||||
return &pb.InspectServiceResponse{Service: svc}, nil
|
return &pb.InspectServiceResponse{Service: svc}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// logsHeartbeatInterval is the interval at which heartbeat entries are sent when there are no logs to stream.
|
||||||
|
const logsHeartbeatInterval = 200 * time.Millisecond
|
||||||
|
|
||||||
|
// MachineLogs streams logs from a systemd service.
|
||||||
|
func (m *Machine) MachineLogs(
|
||||||
|
req *pb.LogsRequest, stream grpc.ServerStreamingServer[pb.LogEntry],
|
||||||
|
) error {
|
||||||
|
// TODO(miek): almost duplicate of docker/server.ContainerLogs
|
||||||
|
ctx := stream.Context()
|
||||||
|
|
||||||
|
opts := api.ServiceLogsOptions{
|
||||||
|
Follow: req.Follow,
|
||||||
|
Tail: int(req.Tail),
|
||||||
|
Since: req.Since,
|
||||||
|
Until: req.Until,
|
||||||
|
}
|
||||||
|
|
||||||
|
logsCh, err := journal.Logs(ctx, req.Id, opts)
|
||||||
|
if err != nil {
|
||||||
|
if errdefs.IsNotFound(err) {
|
||||||
|
return status.Error(codes.NotFound, err.Error())
|
||||||
|
}
|
||||||
|
return status.Errorf(codes.Internal, "get journal logs: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
log := slog.With("unit", req.Id, "stream_id", fmt.Sprintf("%p", stream)[2:])
|
||||||
|
log.Debug("Starting systemd service logs streaming.",
|
||||||
|
"follow", req.Follow, "tail", req.Tail, "since", req.Since, "until", req.Until)
|
||||||
|
|
||||||
|
// Heartbeats are needed only when following logs to let the client know when there are no new log entries
|
||||||
|
// to allow it to advance the watermark of last received log timestamp.
|
||||||
|
var heartbeatCh <-chan time.Time
|
||||||
|
if req.Follow {
|
||||||
|
heartbeatTicker := time.NewTicker(logsHeartbeatInterval)
|
||||||
|
defer heartbeatTicker.Stop()
|
||||||
|
heartbeatCh = heartbeatTicker.C
|
||||||
|
}
|
||||||
|
|
||||||
|
started := time.Now()
|
||||||
|
lastSent := time.Time{}
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case entry, ok := <-logsCh:
|
||||||
|
if !ok {
|
||||||
|
// Channel closed, no more log entries.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if entry.Err != nil {
|
||||||
|
return status.Error(codes.Internal, entry.Err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
pbEntry := &pb.LogEntry{
|
||||||
|
Stream: api.LogStreamTypeToProto(entry.Stream),
|
||||||
|
Timestamp: timestamppb.New(entry.Timestamp),
|
||||||
|
Message: entry.Message,
|
||||||
|
}
|
||||||
|
if err = stream.Send(pbEntry); err != nil {
|
||||||
|
return status.Errorf(codes.Internal, "send log entry: %v", err)
|
||||||
|
}
|
||||||
|
lastSent = entry.Timestamp
|
||||||
|
|
||||||
|
case now := <-heartbeatCh:
|
||||||
|
// Only send heartbeat if no log entries have been sent since the last heartbeat interval or
|
||||||
|
// if no log entries have been sent at all for at least a heartbeat interval since starting.
|
||||||
|
if now.Sub(lastSent) < logsHeartbeatInterval ||
|
||||||
|
(lastSent.IsZero() && now.Sub(started) < logsHeartbeatInterval) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use the timestamp one heartbeat in the past to be conservative. This reduces the chance of sending
|
||||||
|
// a timestamp that is greater than a log entry currently being parsed but not yet sent, which would
|
||||||
|
// cause the client to incorrectly believe it has received all logs up to that point.
|
||||||
|
heartbeat := &pb.LogEntry{
|
||||||
|
Stream: pb.LogEntry_HEARTBEAT,
|
||||||
|
Timestamp: timestamppb.New(now.Add(-logsHeartbeatInterval)),
|
||||||
|
}
|
||||||
|
if err = stream.Send(heartbeat); err != nil {
|
||||||
|
return status.Errorf(codes.Internal, "send log stream heartbeat: %v", err)
|
||||||
|
}
|
||||||
|
lastSent = heartbeat.Timestamp.AsTime()
|
||||||
|
|
||||||
|
case <-ctx.Done():
|
||||||
|
return status.Error(codes.Canceled, ctx.Err().Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,14 +1,17 @@
|
|||||||
package store
|
package store
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"cmp"
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
sq "github.com/Masterminds/squirrel"
|
sq "github.com/Masterminds/squirrel"
|
||||||
|
"github.com/docker/docker/api/types/container"
|
||||||
"github.com/psviderski/uncloud/pkg/api"
|
"github.com/psviderski/uncloud/pkg/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -49,10 +52,9 @@ type DeleteOptions struct {
|
|||||||
// CreateOrUpdateContainer creates a new container record or updates an existing one in the store database.
|
// CreateOrUpdateContainer creates a new container record or updates an existing one in the store database.
|
||||||
// The container is associated with the given machine ID that indicates which machine the container is running on.
|
// The container is associated with the given machine ID that indicates which machine the container is running on.
|
||||||
func (s *Store) CreateOrUpdateContainer(ctx context.Context, ctr api.ServiceContainer, machineID string) error {
|
func (s *Store) CreateOrUpdateContainer(ctx context.Context, ctr api.ServiceContainer, machineID string) error {
|
||||||
// Remove the environment variables from the container record before storing it in the database
|
// Stabilise the order of slices that Docker returns non-deterministically, so that byte-level
|
||||||
// to avoid leaking secrets.
|
// comparison of the serialised container does not flag spurious changes.
|
||||||
ctr.Config.Env = nil
|
normaliseContainerForStore(&ctr)
|
||||||
ctr.ServiceSpec.Container.Env = nil
|
|
||||||
|
|
||||||
cJSON, err := json.Marshal(ctr)
|
cJSON, err := json.Marshal(ctr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -80,6 +82,23 @@ func (s *Store) CreateOrUpdateContainer(ctx context.Context, ctr api.ServiceCont
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// normaliseContainerForStore removes potentially sensitive data and normalises the container fields that Docker may
|
||||||
|
// return in non-deterministic order so that byte-level comparison of the serialised container does not flag spurious
|
||||||
|
// changes.
|
||||||
|
func normaliseContainerForStore(ctr *api.ServiceContainer) {
|
||||||
|
// Remove the environment variables to avoid leaking secrets.
|
||||||
|
ctr.Config.Env = nil
|
||||||
|
ctr.ServiceSpec.Container.Env = nil
|
||||||
|
|
||||||
|
// Docker returns Mounts in a non-deterministic order so sort them.
|
||||||
|
slices.SortFunc(ctr.Mounts, func(a, b container.MountPoint) int {
|
||||||
|
return cmp.Or(
|
||||||
|
strings.Compare(a.Destination, b.Destination),
|
||||||
|
strings.Compare(a.Source, b.Source),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// ListContainers returns a list of container records from the store database that match the given options.
|
// ListContainers returns a list of container records from the store database that match the given options.
|
||||||
func (s *Store) ListContainers(ctx context.Context, opts ListOptions) ([]ContainerRecord, error) {
|
func (s *Store) ListContainers(ctx context.Context, opts ListOptions) ([]ContainerRecord, error) {
|
||||||
q := sq.Select("id", "container", "machine_id", "sync_status", "updated_at").From("containers").
|
q := sq.Select("id", "container", "machine_id", "sync_status", "updated_at").From("containers").
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ func NewSSHCLIRemote(user, host string, port int, keyPath string) *SSHCLIRemote
|
|||||||
func (r *SSHCLIRemote) newSSHCommand(ctx context.Context, cmd string) *exec.Cmd {
|
func (r *SSHCLIRemote) newSSHCommand(ctx context.Context, cmd string) *exec.Cmd {
|
||||||
args := []string{
|
args := []string{
|
||||||
"-o", "ConnectTimeout=5",
|
"-o", "ConnectTimeout=5",
|
||||||
|
"-o", "StrictHostKeyChecking=accept-new",
|
||||||
// Disable pseudo-terminal allocation to prevent SSH from executing as a login shell.
|
// Disable pseudo-terminal allocation to prevent SSH from executing as a login shell.
|
||||||
"-T",
|
"-T",
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ func TestSSHCLIRemote_newSSHCommand(t *testing.T) {
|
|||||||
host: "example.com",
|
host: "example.com",
|
||||||
cmd: "whoami",
|
cmd: "whoami",
|
||||||
expected: []string{
|
expected: []string{
|
||||||
"ssh", "-o", "ConnectTimeout=5", "-T", "example.com", "whoami",
|
"ssh", "-o", "ConnectTimeout=5", "-o", "StrictHostKeyChecking=accept-new", "-T", "example.com", "whoami",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -33,7 +33,7 @@ func TestSSHCLIRemote_newSSHCommand(t *testing.T) {
|
|||||||
host: "example.com",
|
host: "example.com",
|
||||||
cmd: "whoami",
|
cmd: "whoami",
|
||||||
expected: []string{
|
expected: []string{
|
||||||
"ssh", "-o", "ConnectTimeout=5", "-T", "root@example.com", "whoami",
|
"ssh", "-o", "ConnectTimeout=5", "-o", "StrictHostKeyChecking=accept-new", "-T", "root@example.com", "whoami",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -42,7 +42,7 @@ func TestSSHCLIRemote_newSSHCommand(t *testing.T) {
|
|||||||
port: 2222,
|
port: 2222,
|
||||||
cmd: "whoami",
|
cmd: "whoami",
|
||||||
expected: []string{
|
expected: []string{
|
||||||
"ssh", "-o", "ConnectTimeout=5", "-T", "-p", "2222", "example.com", "whoami",
|
"ssh", "-o", "ConnectTimeout=5", "-o", "StrictHostKeyChecking=accept-new", "-T", "-p", "2222", "example.com", "whoami",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -51,7 +51,7 @@ func TestSSHCLIRemote_newSSHCommand(t *testing.T) {
|
|||||||
keyPath: "/path/to/key",
|
keyPath: "/path/to/key",
|
||||||
cmd: "whoami",
|
cmd: "whoami",
|
||||||
expected: []string{
|
expected: []string{
|
||||||
"ssh", "-o", "ConnectTimeout=5", "-T", "-i", "/path/to/key", "example.com", "whoami",
|
"ssh", "-o", "ConnectTimeout=5", "-o", "StrictHostKeyChecking=accept-new", "-T", "-i", "/path/to/key", "example.com", "whoami",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -61,7 +61,7 @@ func TestSSHCLIRemote_newSSHCommand(t *testing.T) {
|
|||||||
port: 2222,
|
port: 2222,
|
||||||
cmd: "ls -la",
|
cmd: "ls -la",
|
||||||
expected: []string{
|
expected: []string{
|
||||||
"ssh", "-o", "ConnectTimeout=5", "-T", "-p", "2222", "ubuntu@192.168.1.10", "ls -la",
|
"ssh", "-o", "ConnectTimeout=5", "-o", "StrictHostKeyChecking=accept-new", "-T", "-p", "2222", "ubuntu@192.168.1.10", "ls -la",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -71,7 +71,7 @@ func TestSSHCLIRemote_newSSHCommand(t *testing.T) {
|
|||||||
keyPath: "/path/to/key",
|
keyPath: "/path/to/key",
|
||||||
cmd: "whoami",
|
cmd: "whoami",
|
||||||
expected: []string{
|
expected: []string{
|
||||||
"ssh", "-o", "ConnectTimeout=5", "-T", "-i", "/path/to/key", "root@example.com", "whoami",
|
"ssh", "-o", "ConnectTimeout=5", "-o", "StrictHostKeyChecking=accept-new", "-T", "-i", "/path/to/key", "root@example.com", "whoami",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -81,7 +81,7 @@ func TestSSHCLIRemote_newSSHCommand(t *testing.T) {
|
|||||||
keyPath: "~/.ssh/id_rsa",
|
keyPath: "~/.ssh/id_rsa",
|
||||||
cmd: "whoami",
|
cmd: "whoami",
|
||||||
expected: []string{
|
expected: []string{
|
||||||
"ssh", "-o", "ConnectTimeout=5", "-T", "-p", "22", "-i", "~/.ssh/id_rsa", "example.com", "whoami",
|
"ssh", "-o", "ConnectTimeout=5", "-o", "StrictHostKeyChecking=accept-new", "-T", "-p", "22", "-i", "~/.ssh/id_rsa", "example.com", "whoami",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -92,7 +92,7 @@ func TestSSHCLIRemote_newSSHCommand(t *testing.T) {
|
|||||||
keyPath: "~/.ssh/id_rsa",
|
keyPath: "~/.ssh/id_rsa",
|
||||||
cmd: "sudo bash -c 'echo hello'",
|
cmd: "sudo bash -c 'echo hello'",
|
||||||
expected: []string{
|
expected: []string{
|
||||||
"ssh", "-o", "ConnectTimeout=5", "-T", "-p", "2222", "-i", "~/.ssh/id_rsa",
|
"ssh", "-o", "ConnectTimeout=5", "-o", "StrictHostKeyChecking=accept-new", "-T", "-p", "2222", "-i", "~/.ssh/id_rsa",
|
||||||
"admin@server.local", "sudo bash -c 'echo hello'",
|
"admin@server.local", "sudo bash -c 'echo hello'",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ func (p *Provisioner) initCluster(ctx context.Context, machines []Machine) error
|
|||||||
}
|
}
|
||||||
defer initClient.Close()
|
defer initClient.Close()
|
||||||
|
|
||||||
if err := initClient.WaitMachineReady(ctx, 30*time.Second); err != nil {
|
if err := initClient.WaitMachineReady(ctx, 90*time.Second); err != nil {
|
||||||
return fmt.Errorf("wait for machine %q to be ready: %w", initMachine.Name, err)
|
return fmt.Errorf("wait for machine %q to be ready: %w", initMachine.Name, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -140,7 +140,7 @@ func (p *Provisioner) initCluster(ctx context.Context, machines []Machine) error
|
|||||||
|
|
||||||
fmt.Printf("Cluster %q initialised with machine %q\n", initMachine.ClusterName, initResp.Machine.Name)
|
fmt.Printf("Cluster %q initialised with machine %q\n", initMachine.ClusterName, initResp.Machine.Name)
|
||||||
fmt.Printf("Waiting for cluster to be ready...")
|
fmt.Printf("Waiting for cluster to be ready...")
|
||||||
if err = initClient.WaitClusterReady(ctx, 30*time.Second); err != nil {
|
if err = initClient.WaitClusterReady(ctx, 90*time.Second); err != nil {
|
||||||
return fmt.Errorf("wait for cluster to be ready: %w", err)
|
return fmt.Errorf("wait for cluster to be ready: %w", err)
|
||||||
}
|
}
|
||||||
fmt.Println(" done.")
|
fmt.Println(" done.")
|
||||||
@@ -160,7 +160,7 @@ func (p *Provisioner) initCluster(ctx context.Context, machines []Machine) error
|
|||||||
//goland:noinspection GoDeferInLoop
|
//goland:noinspection GoDeferInLoop
|
||||||
defer cli.Close()
|
defer cli.Close()
|
||||||
|
|
||||||
if err := cli.WaitMachineReady(ctx, 30*time.Second); err != nil {
|
if err := cli.WaitMachineReady(ctx, 90*time.Second); err != nil {
|
||||||
return fmt.Errorf("wait for machine %q to be ready: %w", m.Name, err)
|
return fmt.Errorf("wait for machine %q to be ready: %w", m.Name, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -322,6 +322,26 @@ func (p *Provisioner) WaitClusterReady(ctx context.Context, c Cluster, timeout t
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Provisioner) ListClusters(ctx context.Context) ([]Cluster, error) {
|
||||||
|
nets, err := p.dockerCli.NetworkList(ctx, network.ListOptions{
|
||||||
|
Filters: filters.NewArgs(
|
||||||
|
filters.Arg("label", ManagedLabel),
|
||||||
|
),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
clusters := []Cluster{}
|
||||||
|
for _, net := range nets {
|
||||||
|
cluster, err := p.InspectCluster(ctx, net.Name)
|
||||||
|
if err == nil {
|
||||||
|
clusters = append(clusters, cluster)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return clusters, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (p *Provisioner) RemoveCluster(ctx context.Context, name string) error {
|
func (p *Provisioner) RemoveCluster(ctx context.Context, name string) error {
|
||||||
if _, err := p.InspectCluster(ctx, name); err != nil {
|
if _, err := p.InspectCluster(ctx, name); err != nil {
|
||||||
if errors.Is(err, ErrNotFound) {
|
if errors.Is(err, ErrNotFound) {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/containerd/errdefs"
|
"github.com/containerd/errdefs"
|
||||||
@@ -62,6 +63,16 @@ func (p *Provisioner) CreateMachine(ctx context.Context, clusterName string, opt
|
|||||||
}
|
}
|
||||||
|
|
||||||
apiPort := nat.Port(fmt.Sprintf("%d/tcp", UncloudAPIPort))
|
apiPort := nat.Port(fmt.Sprintf("%d/tcp", UncloudAPIPort))
|
||||||
|
// Pre-allocate a free host port on 127.0.0.1 and bind it explicitly. Letting Docker assign a random port
|
||||||
|
// (HostPort: "") breaks the cached machine address when the container restarts because Docker picks a new random
|
||||||
|
// port each time.
|
||||||
|
// It's been noticed ucind restarts on rare first-boot failures, which showed up as flaky
|
||||||
|
// "connection refused" errors in e2e tests. This has been addressed by upgrading to the latest dind image.
|
||||||
|
hostPort, err := availableLocalPort()
|
||||||
|
if err != nil {
|
||||||
|
return m, fmt.Errorf("reserve host port for machine API: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
config := &container.Config{
|
config := &container.Config{
|
||||||
Image: img,
|
Image: img,
|
||||||
Labels: map[string]string{
|
Labels: map[string]string{
|
||||||
@@ -78,8 +89,8 @@ func (p *Provisioner) CreateMachine(ctx context.Context, clusterName string, opt
|
|||||||
PortBindings: nat.PortMap{
|
PortBindings: nat.PortMap{
|
||||||
apiPort: []nat.PortBinding{
|
apiPort: []nat.PortBinding{
|
||||||
{
|
{
|
||||||
HostIP: "127.0.0.1",
|
HostIP: "127.0.0.1",
|
||||||
// Host port is a random available port.
|
HostPort: strconv.Itoa(hostPort),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -185,6 +196,21 @@ func (p *Provisioner) waitPortPublished(ctx context.Context, containerID string,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// availableLocalPort asks the kernel for a free TCP port on 127.0.0.1 and returns it.
|
||||||
|
func availableLocalPort() (int, error) {
|
||||||
|
l, err := net.ListenTCP("tcp4", &net.TCPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 0})
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
port := l.Addr().(*net.TCPAddr).Port
|
||||||
|
if err = l.Close(); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return port, nil
|
||||||
|
}
|
||||||
|
|
||||||
func randomMachineName() (string, error) {
|
func randomMachineName() (string, error) {
|
||||||
suffix, err := secret.RandomAlphaNumeric(4)
|
suffix, err := secret.RandomAlphaNumeric(4)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -3,5 +3,8 @@ package version
|
|||||||
var version string
|
var version string
|
||||||
|
|
||||||
func String() string {
|
func String() string {
|
||||||
|
if version == "" {
|
||||||
|
return "999.0.0-dev"
|
||||||
|
}
|
||||||
return version
|
return version
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,10 +31,26 @@ backend = "aqua:golangci/golangci-lint"
|
|||||||
[tools.golangci-lint."platforms.linux-x64"]
|
[tools.golangci-lint."platforms.linux-x64"]
|
||||||
checksum = "sha256:852741ea769f4543ab4b6cf86b313cb7173f2e8d2d39f00eb0cd5ef5d4d5eda9"
|
checksum = "sha256:852741ea769f4543ab4b6cf86b313cb7173f2e8d2d39f00eb0cd5ef5d4d5eda9"
|
||||||
url = "https://github.com/golangci/golangci-lint/releases/download/v2.11.2/golangci-lint-2.11.2-linux-amd64.tar.gz"
|
url = "https://github.com/golangci/golangci-lint/releases/download/v2.11.2/golangci-lint-2.11.2-linux-amd64.tar.gz"
|
||||||
|
provenance = "github-attestations"
|
||||||
|
|
||||||
[tools.golangci-lint."platforms.macos-arm64"]
|
[tools.golangci-lint."platforms.macos-arm64"]
|
||||||
checksum = "sha256:11e9549b955bae4bfb71aa3b5c2762e446e2d3f975fe963d6d767b8f745d9344"
|
checksum = "sha256:11e9549b955bae4bfb71aa3b5c2762e446e2d3f975fe963d6d767b8f745d9344"
|
||||||
url = "https://github.com/golangci/golangci-lint/releases/download/v2.11.2/golangci-lint-2.11.2-darwin-arm64.tar.gz"
|
url = "https://github.com/golangci/golangci-lint/releases/download/v2.11.2/golangci-lint-2.11.2-darwin-arm64.tar.gz"
|
||||||
|
provenance = "github-attestations"
|
||||||
|
|
||||||
|
[[tools.goreleaser]]
|
||||||
|
version = "2.15.2"
|
||||||
|
backend = "aqua:goreleaser/goreleaser"
|
||||||
|
|
||||||
|
[tools.goreleaser."platforms.linux-x64"]
|
||||||
|
checksum = "sha256:0ebdbf0353aba566b969dde746cc4e4806f96c27aa2f3971b229a9df7611fedc"
|
||||||
|
url = "https://github.com/goreleaser/goreleaser/releases/download/v2.15.2/goreleaser_Linux_x86_64.tar.gz"
|
||||||
|
provenance = "github-attestations"
|
||||||
|
|
||||||
|
[tools.goreleaser."platforms.macos-arm64"]
|
||||||
|
checksum = "sha256:0e6bd67688ac949780bf1166813a91f89856898ef4c40d7d46c2c74ebaa4b9ee"
|
||||||
|
url = "https://github.com/goreleaser/goreleaser/releases/download/v2.15.2/goreleaser_Darwin_all.tar.gz"
|
||||||
|
provenance = "github-attestations"
|
||||||
|
|
||||||
[[tools.node]]
|
[[tools.node]]
|
||||||
version = "22.20.0"
|
version = "22.20.0"
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ experimental = true
|
|||||||
"aqua:vektra/mockery" = "3.5.3"
|
"aqua:vektra/mockery" = "3.5.3"
|
||||||
go = "1.26.1"
|
go = "1.26.1"
|
||||||
golangci-lint = "2.11.2"
|
golangci-lint = "2.11.2"
|
||||||
|
goreleaser = "2.15.2"
|
||||||
node = "22.20.0"
|
node = "22.20.0"
|
||||||
protoc = "27.3"
|
protoc = "27.3"
|
||||||
protoc-gen-go = "1.34.2"
|
protoc-gen-go = "1.34.2"
|
||||||
|
|||||||
+12
-8
@@ -12,6 +12,7 @@ type Client interface {
|
|||||||
ContainerClient
|
ContainerClient
|
||||||
DNSClient
|
DNSClient
|
||||||
ImageClient
|
ImageClient
|
||||||
|
LogsClient
|
||||||
MachineClient
|
MachineClient
|
||||||
ServiceClient
|
ServiceClient
|
||||||
VolumeClient
|
VolumeClient
|
||||||
@@ -20,7 +21,10 @@ type Client interface {
|
|||||||
type ContainerClient interface {
|
type ContainerClient interface {
|
||||||
CreateContainer(
|
CreateContainer(
|
||||||
ctx context.Context, serviceID string, spec ServiceSpec, machineID string,
|
ctx context.Context, serviceID string, spec ServiceSpec, machineID string,
|
||||||
) (container.CreateResponse, error)
|
) (CreateContainerResponse, error)
|
||||||
|
CreatePreDeployHookContainer(
|
||||||
|
ctx context.Context, serviceID string, spec ServiceSpec, machineID string,
|
||||||
|
) (CreateContainerResponse, error)
|
||||||
ExecContainer(ctx context.Context, serviceNameOrID, containerNameOrID string, config ExecOptions) (int, error)
|
ExecContainer(ctx context.Context, serviceNameOrID, containerNameOrID string, config ExecOptions) (int, error)
|
||||||
InspectContainer(ctx context.Context, serviceNameOrID, containerNameOrID string) (MachineServiceContainer, error)
|
InspectContainer(ctx context.Context, serviceNameOrID, containerNameOrID string) (MachineServiceContainer, error)
|
||||||
StartContainer(ctx context.Context, serviceNameOrID, containerNameOrID string) error
|
StartContainer(ctx context.Context, serviceNameOrID, containerNameOrID string) error
|
||||||
@@ -40,6 +44,13 @@ type ImageClient interface {
|
|||||||
InspectRemoteImage(ctx context.Context, id string) ([]MachineRemoteImage, error)
|
InspectRemoteImage(ctx context.Context, id string) ([]MachineRemoteImage, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type LogsClient interface {
|
||||||
|
ServiceLogs(
|
||||||
|
ctx context.Context, serviceNameOrID string, opts ServiceLogsOptions,
|
||||||
|
) (Service, <-chan ServiceLogEntry, error)
|
||||||
|
MachineLogs(ctx context.Context, unit string, opts ServiceLogsOptions) (<-chan ServiceLogEntry, error)
|
||||||
|
}
|
||||||
|
|
||||||
type MachineClient interface {
|
type MachineClient interface {
|
||||||
InspectMachine(ctx context.Context, id string) (*pb.MachineMember, error)
|
InspectMachine(ctx context.Context, id string) (*pb.MachineMember, error)
|
||||||
ListMachines(ctx context.Context, filter *MachineFilter) (MachineMembersList, error)
|
ListMachines(ctx context.Context, filter *MachineFilter) (MachineMembersList, error)
|
||||||
@@ -60,10 +71,3 @@ type VolumeClient interface {
|
|||||||
ListVolumes(ctx context.Context, filter *VolumeFilter) ([]MachineVolume, error)
|
ListVolumes(ctx context.Context, filter *VolumeFilter) ([]MachineVolume, error)
|
||||||
RemoveVolume(ctx context.Context, machineNameOrID, volumeName string, force bool) error
|
RemoveVolume(ctx context.Context, machineNameOrID, volumeName string, force bool) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// AsPtr returns a pointer to the given value. Useful for optional fields in API structs.
|
|
||||||
//
|
|
||||||
//go:fix inline
|
|
||||||
func AsPtr[T any](v T) *T {
|
|
||||||
return new(v)
|
|
||||||
}
|
|
||||||
|
|||||||
+20
-1
@@ -18,11 +18,16 @@ const (
|
|||||||
// DockerNetworkName is the name of the Docker network used by uncloud. Keep the value in sync with NetworkName
|
// DockerNetworkName is the name of the Docker network used by uncloud. Keep the value in sync with NetworkName
|
||||||
// in internal/machine/docker/manager.go.
|
// in internal/machine/docker/manager.go.
|
||||||
DockerNetworkName = "uncloud"
|
DockerNetworkName = "uncloud"
|
||||||
|
|
||||||
LabelManaged = "uncloud.managed"
|
LabelManaged = "uncloud.managed"
|
||||||
LabelServiceID = "uncloud.service.id"
|
LabelServiceID = "uncloud.service.id"
|
||||||
LabelServiceName = "uncloud.service.name"
|
LabelServiceName = "uncloud.service.name"
|
||||||
LabelServiceMode = "uncloud.service.mode"
|
LabelServiceMode = "uncloud.service.mode"
|
||||||
LabelServicePorts = "uncloud.service.ports"
|
LabelServicePorts = "uncloud.service.ports"
|
||||||
|
// LabelHook marks a container as a deployment hook. The value indicates the hook type (e.g. LabelHookPreDeploy).
|
||||||
|
LabelHook = "uncloud.service.hook"
|
||||||
|
// LabelHookPreDeploy indicates that the container is a pre-deploy hook that runs before deploying the service.
|
||||||
|
LabelHookPreDeploy = "pre-deploy"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Container struct {
|
type Container struct {
|
||||||
@@ -159,6 +164,13 @@ func (c *Container) UnmarshalJSON(data []byte) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CreateContainerResponse wraps a container creation response with the container name assigned during creation.
|
||||||
|
type CreateContainerResponse struct {
|
||||||
|
container.CreateResponse
|
||||||
|
// Name is the container name assigned during creation.
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
|
||||||
type ServiceContainer struct {
|
type ServiceContainer struct {
|
||||||
Container
|
Container
|
||||||
ServiceSpec ServiceSpec
|
ServiceSpec ServiceSpec
|
||||||
@@ -181,10 +193,17 @@ func (c *ServiceContainer) ServiceName() string {
|
|||||||
|
|
||||||
// ServiceMode returns the replication mode of the service this container belongs to.
|
// ServiceMode returns the replication mode of the service this container belongs to.
|
||||||
func (c *ServiceContainer) ServiceMode() string {
|
func (c *ServiceContainer) ServiceMode() string {
|
||||||
return c.Config.Labels[LabelServiceMode]
|
return c.ServiceSpec.Mode
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsHook returns true if the container is a deployment hook (e.g. pre-deploy).
|
||||||
|
func (c *ServiceContainer) IsHook() bool {
|
||||||
|
_, ok := c.Config.Labels[LabelHook]
|
||||||
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
// ServicePorts returns the ports this container publishes as part of its service.
|
// ServicePorts returns the ports this container publishes as part of its service.
|
||||||
|
// TODO: return ports from ServiceSpec to allow updating ingress ports without recreating containers.
|
||||||
func (c *ServiceContainer) ServicePorts() ([]PortSpec, error) {
|
func (c *ServiceContainer) ServicePorts() ([]PortSpec, error) {
|
||||||
encoded, ok := c.Config.Labels[LabelServicePorts]
|
encoded, ok := c.Config.Labels[LabelServicePorts]
|
||||||
if !ok {
|
if !ok {
|
||||||
|
|||||||
+26
-18
@@ -18,66 +18,74 @@ const (
|
|||||||
|
|
||||||
type LogStreamType int
|
type LogStreamType int
|
||||||
|
|
||||||
// LogStreamTypeFromProto converts a protobuf ContainerLogEntry.StreamType to the internal LogStreamType.
|
// LogStreamTypeFromProto converts a protobuf LogEntry.StreamType to the internal LogStreamType.
|
||||||
func LogStreamTypeFromProto(s pb.ContainerLogEntry_StreamType) LogStreamType {
|
func LogStreamTypeFromProto(s pb.LogEntry_StreamType) LogStreamType {
|
||||||
switch s {
|
switch s {
|
||||||
case pb.ContainerLogEntry_STDOUT:
|
case pb.LogEntry_STDOUT:
|
||||||
return LogStreamStdout
|
return LogStreamStdout
|
||||||
case pb.ContainerLogEntry_STDERR:
|
case pb.LogEntry_STDERR:
|
||||||
return LogStreamStderr
|
return LogStreamStderr
|
||||||
case pb.ContainerLogEntry_HEARTBEAT:
|
case pb.LogEntry_HEARTBEAT:
|
||||||
return LogStreamHeartbeat
|
return LogStreamHeartbeat
|
||||||
default:
|
default:
|
||||||
return LogStreamUnknown
|
return LogStreamUnknown
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// LogStreamTypeToProto converts LogStreamType to protobuf ContainerLogEntry.StreamType.
|
// LogStreamTypeToProto converts LogStreamType to protobuf LogEntry.StreamType.
|
||||||
func LogStreamTypeToProto(s LogStreamType) pb.ContainerLogEntry_StreamType {
|
func LogStreamTypeToProto(s LogStreamType) pb.LogEntry_StreamType {
|
||||||
switch s {
|
switch s {
|
||||||
case LogStreamStdout:
|
case LogStreamStdout:
|
||||||
return pb.ContainerLogEntry_STDOUT
|
return pb.LogEntry_STDOUT
|
||||||
case LogStreamStderr:
|
case LogStreamStderr:
|
||||||
return pb.ContainerLogEntry_STDERR
|
return pb.LogEntry_STDERR
|
||||||
case LogStreamHeartbeat:
|
case LogStreamHeartbeat:
|
||||||
return pb.ContainerLogEntry_HEARTBEAT
|
return pb.LogEntry_HEARTBEAT
|
||||||
default:
|
default:
|
||||||
return pb.ContainerLogEntry_UNKNOWN
|
return pb.LogEntry_UNKNOWN
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ServiceLogsOptions specifies parameters for ServiceLogs.
|
// ServiceLogsOptions specifies parameters for ServiceLogs and MachineLogs.
|
||||||
type ServiceLogsOptions struct {
|
type ServiceLogsOptions struct {
|
||||||
Follow bool
|
Follow bool
|
||||||
Tail int
|
Tail int
|
||||||
Since string
|
Since string
|
||||||
Until string
|
Until string
|
||||||
|
// Containers filters logs to only include the specified service containers (names, full IDs,
|
||||||
|
// or unique ID prefixes). If empty, logs from all containers in the service are included.
|
||||||
|
// Ignored by MachineLogs.
|
||||||
|
Containers []string
|
||||||
// Machines filters logs to only include containers running on the specified machines (names or IDs).
|
// Machines filters logs to only include containers running on the specified machines (names or IDs).
|
||||||
// If empty, logs from all machines are included.
|
// If empty, logs from all machines are included.
|
||||||
Machines []string
|
Machines []string
|
||||||
}
|
}
|
||||||
|
|
||||||
// ServiceLogEntry represents a single log entry from a service container.
|
// ServiceLogEntry represents a single log entry from a service container or systemd service.
|
||||||
type ServiceLogEntry struct {
|
type ServiceLogEntry struct {
|
||||||
// Metadata may not be set if an error occurred (Err is not nil).
|
// Metadata may not be set if an error occurred (Err is not nil).
|
||||||
Metadata ServiceLogEntryMetadata
|
Metadata ServiceLogEntryMetadata
|
||||||
ContainerLogEntry
|
LogEntry
|
||||||
}
|
}
|
||||||
|
|
||||||
// ServiceLogEntryMetadata contains metadata about the source of a log entry.
|
// ServiceLogEntryMetadata identifies the source of a log entry.
|
||||||
|
// For systemd service logs, ServiceID and ServiceName hold the unit name and ContainerID is empty.
|
||||||
type ServiceLogEntryMetadata struct {
|
type ServiceLogEntryMetadata struct {
|
||||||
ServiceID string
|
ServiceID string
|
||||||
ServiceName string
|
ServiceName string
|
||||||
ContainerID string
|
ContainerID string
|
||||||
MachineID string
|
MachineID string
|
||||||
MachineName string
|
MachineName string
|
||||||
|
// Hook is the hook type of the source container (e.g., "pre-deploy"). Empty for non-hook containers.
|
||||||
|
Hook string
|
||||||
}
|
}
|
||||||
|
|
||||||
// ContainerLogEntry represents a single log entry from a container.
|
// LogEntry represents a single log entry from a container or a service.
|
||||||
type ContainerLogEntry struct {
|
type LogEntry struct {
|
||||||
Stream LogStreamType
|
Stream LogStreamType
|
||||||
Timestamp time.Time
|
Timestamp time.Time
|
||||||
Message []byte
|
// Message is the raw log line as bytes terminated with a trailing newline.
|
||||||
|
Message []byte
|
||||||
// Err indicates that an error occurred while streaming logs from a container.
|
// Err indicates that an error occurred while streaming logs from a container.
|
||||||
// Other fields are not set if Err is not nil.
|
// Other fields are not set if Err is not nil.
|
||||||
Err error
|
Err error
|
||||||
|
|||||||
+80
-4
@@ -55,7 +55,7 @@ type ServiceSpec struct {
|
|||||||
// Caddy is the optional Caddy reverse proxy configuration for the service.
|
// Caddy is the optional Caddy reverse proxy configuration for the service.
|
||||||
// Caddy and Ports cannot be specified simultaneously.
|
// Caddy and Ports cannot be specified simultaneously.
|
||||||
Caddy *CaddySpec `json:",omitempty"`
|
Caddy *CaddySpec `json:",omitempty"`
|
||||||
// Configs is list of configuration objects that can be mounted into the container.
|
// Configs is a list of configuration objects that can be mounted into the container.
|
||||||
Configs []ConfigSpec
|
Configs []ConfigSpec
|
||||||
// Container defines the desired state of each container in the service.
|
// Container defines the desired state of each container in the service.
|
||||||
Container ContainerSpec
|
Container ContainerSpec
|
||||||
@@ -67,6 +67,9 @@ type ServiceSpec struct {
|
|||||||
// Ports defines what service ports to publish to make the service accessible outside the cluster.
|
// Ports defines what service ports to publish to make the service accessible outside the cluster.
|
||||||
// Caddy and Ports cannot be specified simultaneously.
|
// Caddy and Ports cannot be specified simultaneously.
|
||||||
Ports []PortSpec
|
Ports []PortSpec
|
||||||
|
// PreDeploy is an optional hook that runs a command in a temporary container before deploying the service.
|
||||||
|
// The container uses the service's image and inherits its configuration.
|
||||||
|
PreDeploy *PreDeployHook `json:",omitempty"`
|
||||||
// Replicas is the number of containers to run for the service. Only valid for a replicated service.
|
// Replicas is the number of containers to run for the service. Only valid for a replicated service.
|
||||||
Replicas uint `json:",omitempty"`
|
Replicas uint `json:",omitempty"`
|
||||||
// UpdateConfig configures how the service is updated during a deployment.
|
// UpdateConfig configures how the service is updated during a deployment.
|
||||||
@@ -205,6 +208,12 @@ func (s *ServiceSpec) Validate() error {
|
|||||||
return fmt.Errorf("validate service configs and mounts: %w", err)
|
return fmt.Errorf("validate service configs and mounts: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if s.PreDeploy != nil {
|
||||||
|
if err := s.PreDeploy.Validate(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -216,6 +225,7 @@ func (s *ServiceSpec) Clone() ServiceSpec {
|
|||||||
spec.Caddy = &caddyCopy
|
spec.Caddy = &caddyCopy
|
||||||
}
|
}
|
||||||
spec.Container = s.Container.Clone()
|
spec.Container = s.Container.Clone()
|
||||||
|
spec.PreDeploy = s.PreDeploy.Clone()
|
||||||
|
|
||||||
if s.Ports != nil {
|
if s.Ports != nil {
|
||||||
spec.Ports = make([]PortSpec, len(s.Ports))
|
spec.Ports = make([]PortSpec, len(s.Ports))
|
||||||
@@ -436,6 +446,46 @@ type LogDriver struct {
|
|||||||
Options map[string]string
|
Options map[string]string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PreDeployHook defines a command to run in a temporary container before deploying the service.
|
||||||
|
// The container uses the service's image and inherits its configuration (env, volumes, placement).
|
||||||
|
// It must exit successfully (code 0) for the deployment to proceed.
|
||||||
|
type PreDeployHook struct {
|
||||||
|
// Command to execute in the container.
|
||||||
|
Command []string
|
||||||
|
// Env defines additional environment variables for the container, merged with the service's environment variables.
|
||||||
|
Env EnvVars `json:",omitempty"`
|
||||||
|
// Privileged overrides the container's privileged mode. nil means inherit from the service.
|
||||||
|
Privileged *bool `json:",omitempty"`
|
||||||
|
// Timeout is the maximum duration to wait for the command to complete. On timeout, the container is stopped
|
||||||
|
// and the deployment fails. If nil, a default timeout is used.
|
||||||
|
Timeout *time.Duration `json:",omitempty"`
|
||||||
|
// User to run the command as. If empty, the service user is used. Format: user|UID[:group|GID].
|
||||||
|
User string `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *PreDeployHook) Validate() error {
|
||||||
|
if len(h.Command) == 0 {
|
||||||
|
return fmt.Errorf("pre-deploy hook command is required")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *PreDeployHook) Clone() *PreDeployHook {
|
||||||
|
if h == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
hook := *h
|
||||||
|
hook.Command = slices.Clone(h.Command)
|
||||||
|
hook.Env = maps.Clone(h.Env)
|
||||||
|
|
||||||
|
return &hook
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *PreDeployHook) Equals(other *PreDeployHook) bool {
|
||||||
|
return cmp.Equal(h, other, cmpopts.EquateEmpty())
|
||||||
|
}
|
||||||
|
|
||||||
// UpdateConfig configures how a service is updated during a deployment.
|
// UpdateConfig configures how a service is updated during a deployment.
|
||||||
type UpdateConfig struct {
|
type UpdateConfig struct {
|
||||||
// Order specifies the order of operations during an update.
|
// Order specifies the order of operations during an update.
|
||||||
@@ -455,10 +505,13 @@ type RunServiceResponse struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Service struct {
|
type Service struct {
|
||||||
ID string
|
ID string
|
||||||
Name string
|
Name string
|
||||||
Mode string
|
Mode string
|
||||||
|
// Containers is the regular long-running service containers.
|
||||||
Containers []MachineServiceContainer
|
Containers []MachineServiceContainer
|
||||||
|
// HookContainers are one-shot containers for deployment hooks (e.g. pre-deploy).
|
||||||
|
HookContainers []MachineServiceContainer
|
||||||
}
|
}
|
||||||
|
|
||||||
type MachineServiceContainer struct {
|
type MachineServiceContainer struct {
|
||||||
@@ -466,6 +519,29 @@ type MachineServiceContainer struct {
|
|||||||
Container ServiceContainer
|
Container ServiceContainer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FindContainer returns the service container by exact name, ID, or unique ID prefix.
|
||||||
|
// Returns ErrNotFound if no container matches, or an error if an ID prefix matches more than one container.
|
||||||
|
func (s *Service) FindContainer(nameOrID string) (MachineServiceContainer, error) {
|
||||||
|
var prefixMatches []MachineServiceContainer
|
||||||
|
for _, c := range append(s.Containers, s.HookContainers...) {
|
||||||
|
if c.Container.ID == nameOrID || c.Container.Name == nameOrID {
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(c.Container.ID, nameOrID) {
|
||||||
|
prefixMatches = append(prefixMatches, c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(prefixMatches) == 1 {
|
||||||
|
return prefixMatches[0], nil
|
||||||
|
}
|
||||||
|
if len(prefixMatches) > 1 {
|
||||||
|
return MachineServiceContainer{}, fmt.Errorf("multiple containers found with ID prefix '%s'", nameOrID)
|
||||||
|
}
|
||||||
|
|
||||||
|
return MachineServiceContainer{}, ErrNotFound
|
||||||
|
}
|
||||||
|
|
||||||
// MachineIDs returns a list of unique machine IDs where the service containers are running.
|
// MachineIDs returns a list of unique machine IDs where the service containers are running.
|
||||||
func (s *Service) MachineIDs() []string {
|
func (s *Service) MachineIDs() []string {
|
||||||
ids := mapset.NewSet[string]()
|
ids := mapset.NewSet[string]()
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package compose
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/compose-spec/compose-go/v2/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
const PreDeployHookExtensionKey = "x-pre_deploy"
|
||||||
|
|
||||||
|
// PreDeployHook represents the parsed x-pre_deploy extension config.
|
||||||
|
type PreDeployHook struct {
|
||||||
|
Command types.ShellCommand `yaml:"command" json:"command"`
|
||||||
|
Environment types.MappingWithEquals `yaml:"environment,omitempty" json:"environment,omitempty"`
|
||||||
|
Privileged *bool `yaml:"privileged,omitempty" json:"privileged,omitempty"`
|
||||||
|
Timeout *types.Duration `yaml:"timeout,omitempty" json:"timeout,omitempty"`
|
||||||
|
User string `yaml:"user,omitempty" json:"user,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate checks that the pre-deploy hook configuration is valid.
|
||||||
|
func (p *PreDeployHook) Validate() error {
|
||||||
|
if len(p.Command) == 0 {
|
||||||
|
return fmt.Errorf("missing required attribute 'command' in %s extension", PreDeployHookExtensionKey)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,149 @@
|
|||||||
|
package compose
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/compose-spec/compose-go/v2/types"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestPreDeployHookExtension(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
yaml string
|
||||||
|
want PreDeployHook
|
||||||
|
wantErr string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "command only",
|
||||||
|
yaml: `
|
||||||
|
services:
|
||||||
|
web:
|
||||||
|
image: nginx
|
||||||
|
x-pre_deploy:
|
||||||
|
command: ["echo", "hello"]
|
||||||
|
`,
|
||||||
|
want: PreDeployHook{
|
||||||
|
Command: types.ShellCommand{"echo", "hello"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "command as string",
|
||||||
|
yaml: `
|
||||||
|
services:
|
||||||
|
web:
|
||||||
|
image: nginx
|
||||||
|
x-pre_deploy:
|
||||||
|
command: echo hello
|
||||||
|
`,
|
||||||
|
want: PreDeployHook{
|
||||||
|
Command: types.ShellCommand{"echo", "hello"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// TODO: explore ways to error on unknown attributes instead of ignoring them.
|
||||||
|
{
|
||||||
|
name: "all attributes",
|
||||||
|
yaml: `
|
||||||
|
services:
|
||||||
|
web:
|
||||||
|
image: nginx
|
||||||
|
x-pre_deploy:
|
||||||
|
command: ["sh", "-c", "migrate up"]
|
||||||
|
environment:
|
||||||
|
DB_HOST: localhost
|
||||||
|
DB_PORT: "5432"
|
||||||
|
privileged: true
|
||||||
|
timeout: 2m30s
|
||||||
|
user: root
|
||||||
|
unknown_attribute: should be ignored
|
||||||
|
`,
|
||||||
|
want: PreDeployHook{
|
||||||
|
Command: types.ShellCommand{"sh", "-c", "migrate up"},
|
||||||
|
Environment: types.MappingWithEquals{
|
||||||
|
"DB_HOST": new("localhost"),
|
||||||
|
"DB_PORT": new("5432"),
|
||||||
|
},
|
||||||
|
Privileged: new(true),
|
||||||
|
Timeout: new(types.Duration(2*time.Minute + 30*time.Second)),
|
||||||
|
User: "root",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "timeout as seconds",
|
||||||
|
yaml: `
|
||||||
|
services:
|
||||||
|
web:
|
||||||
|
image: nginx
|
||||||
|
x-pre_deploy:
|
||||||
|
command: ["true"]
|
||||||
|
timeout: 30s
|
||||||
|
`,
|
||||||
|
want: PreDeployHook{
|
||||||
|
Command: types.ShellCommand{"true"},
|
||||||
|
Timeout: new(types.Duration(30 * time.Second)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "privileged false",
|
||||||
|
yaml: `
|
||||||
|
services:
|
||||||
|
web:
|
||||||
|
image: nginx
|
||||||
|
x-pre_deploy:
|
||||||
|
command: ["true"]
|
||||||
|
privileged: false
|
||||||
|
`,
|
||||||
|
want: PreDeployHook{
|
||||||
|
Command: types.ShellCommand{"true"},
|
||||||
|
Privileged: new(false),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "missing command should fail",
|
||||||
|
yaml: `
|
||||||
|
services:
|
||||||
|
web:
|
||||||
|
image: nginx
|
||||||
|
x-pre_deploy:
|
||||||
|
user: root
|
||||||
|
`,
|
||||||
|
wantErr: "missing required attribute 'command'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty command should fail",
|
||||||
|
yaml: `
|
||||||
|
services:
|
||||||
|
web:
|
||||||
|
image: nginx
|
||||||
|
x-pre_deploy:
|
||||||
|
command: []
|
||||||
|
`,
|
||||||
|
wantErr: "missing required attribute 'command'",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
project, err := LoadProjectFromContent(context.Background(), tt.yaml)
|
||||||
|
|
||||||
|
if tt.wantErr != "" {
|
||||||
|
require.ErrorContains(t, err, tt.wantErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
service, err := project.GetService("web")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
ext, ok := service.Extensions[PreDeployHookExtensionKey]
|
||||||
|
require.True(t, ok, "x-pre_deploy extension not found")
|
||||||
|
|
||||||
|
hook, ok := ext.(PreDeployHook)
|
||||||
|
require.True(t, ok, "x-pre_deploy extension is not PreDeployHook type")
|
||||||
|
assert.Equal(t, tt.want, hook)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,6 +12,7 @@ import (
|
|||||||
"github.com/compose-spec/compose-go/v2/transform"
|
"github.com/compose-spec/compose-go/v2/transform"
|
||||||
"github.com/compose-spec/compose-go/v2/tree"
|
"github.com/compose-spec/compose-go/v2/tree"
|
||||||
"github.com/compose-spec/compose-go/v2/types"
|
"github.com/compose-spec/compose-go/v2/types"
|
||||||
|
"github.com/psviderski/uncloud/internal/cli/tui"
|
||||||
"github.com/psviderski/uncloud/pkg/api"
|
"github.com/psviderski/uncloud/pkg/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -38,6 +39,7 @@ func LoadProject(ctx context.Context, paths []string, opts ...composecli.Project
|
|||||||
composecli.WithExtension(CaddyExtensionKey, Caddy{}),
|
composecli.WithExtension(CaddyExtensionKey, Caddy{}),
|
||||||
composecli.WithExtension(MachinesExtensionKey, MachinesSource{}),
|
composecli.WithExtension(MachinesExtensionKey, MachinesSource{}),
|
||||||
composecli.WithExtension(PortsExtensionKey, PortsSource{}),
|
composecli.WithExtension(PortsExtensionKey, PortsSource{}),
|
||||||
|
composecli.WithExtension(PreDeployHookExtensionKey, PreDeployHook{}),
|
||||||
}
|
}
|
||||||
|
|
||||||
options, err := composecli.NewProjectOptions(
|
options, err := composecli.NewProjectOptions(
|
||||||
@@ -66,6 +68,10 @@ func LoadProject(ctx context.Context, paths []string, opts ...composecli.Project
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, err = range validateServicesFeatures(project) {
|
||||||
|
tui.PrintWarning(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
// Process image templates in services to expand Go template expressions using git repo state.
|
// Process image templates in services to expand Go template expressions using git repo state.
|
||||||
if project, err = ProcessImageTemplates(project); err != nil {
|
if project, err = ProcessImageTemplates(project); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@@ -2,8 +2,10 @@ package compose
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
@@ -186,3 +188,132 @@ REDIS_URL=redis://localhost:6379
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestLoadProject_Unsupported checks that unsupported features lead to warnings.
|
||||||
|
func TestLoadProject_Unsupported(t *testing.T) {
|
||||||
|
// captureStderr runs fn while capturing stderr output and returns what was written.
|
||||||
|
captureStderr := func(t *testing.T, fn func()) string {
|
||||||
|
t.Helper()
|
||||||
|
old := os.Stderr
|
||||||
|
r, w, err := os.Pipe()
|
||||||
|
require.NoError(t, err)
|
||||||
|
os.Stderr = w
|
||||||
|
defer func() { os.Stderr = old }()
|
||||||
|
|
||||||
|
fn()
|
||||||
|
|
||||||
|
w.Close()
|
||||||
|
out, err := io.ReadAll(r)
|
||||||
|
require.NoError(t, err)
|
||||||
|
r.Close()
|
||||||
|
return string(out)
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
composeYAML string
|
||||||
|
warnCount int
|
||||||
|
warnContains []string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "unsupported dns",
|
||||||
|
composeYAML: `services:
|
||||||
|
app:
|
||||||
|
image: myapp:latest
|
||||||
|
dns: 8.8.8.8
|
||||||
|
`,
|
||||||
|
warnCount: 1,
|
||||||
|
warnContains: []string{"dns"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "unsupported networks",
|
||||||
|
composeYAML: `services:
|
||||||
|
app:
|
||||||
|
image: myapp:latest
|
||||||
|
networks:
|
||||||
|
- frontend
|
||||||
|
|
||||||
|
networks:
|
||||||
|
frontend:
|
||||||
|
`,
|
||||||
|
warnCount: 1,
|
||||||
|
warnContains: []string{"networks"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "unsupported depends_on service_completed_successfully",
|
||||||
|
composeYAML: `services:
|
||||||
|
migrate:
|
||||||
|
image: alpine
|
||||||
|
command: ["true"]
|
||||||
|
app:
|
||||||
|
image: nginx
|
||||||
|
depends_on:
|
||||||
|
migrate:
|
||||||
|
condition: service_completed_successfully
|
||||||
|
`,
|
||||||
|
warnCount: 1,
|
||||||
|
warnContains: []string{
|
||||||
|
"service_completed_successfully",
|
||||||
|
"pre-deploy hook",
|
||||||
|
"https://uncloud.run/docs/guides/deployments/pre-deploy-hooks",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "multiple unsupported features",
|
||||||
|
composeYAML: `services:
|
||||||
|
app:
|
||||||
|
image: myapp:latest
|
||||||
|
dns: 8.8.8.8
|
||||||
|
links:
|
||||||
|
- db
|
||||||
|
db:
|
||||||
|
image: postgres:latest
|
||||||
|
secrets:
|
||||||
|
- db_password
|
||||||
|
|
||||||
|
secrets:
|
||||||
|
db_password:
|
||||||
|
file: ./secret.txt
|
||||||
|
`,
|
||||||
|
warnCount: 3,
|
||||||
|
warnContains: []string{"dns", "links", "secrets"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "supported networks",
|
||||||
|
composeYAML: `services:
|
||||||
|
app:
|
||||||
|
image: myapp:latest
|
||||||
|
networks:
|
||||||
|
default: {}
|
||||||
|
|
||||||
|
web:
|
||||||
|
image: nginx
|
||||||
|
networks:
|
||||||
|
- default
|
||||||
|
|
||||||
|
networks:
|
||||||
|
default:
|
||||||
|
`,
|
||||||
|
warnCount: 0,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
stderr := captureStderr(t, func() {
|
||||||
|
_, err := LoadProjectFromContent(context.Background(), tt.composeYAML)
|
||||||
|
require.NoError(t, err)
|
||||||
|
})
|
||||||
|
|
||||||
|
if tt.warnCount == 0 {
|
||||||
|
assert.Empty(t, stderr)
|
||||||
|
} else {
|
||||||
|
assert.Equal(t, tt.warnCount, strings.Count(stderr, "WARNING:"),
|
||||||
|
"expected %d warnings, got stderr: %s", tt.warnCount, stderr)
|
||||||
|
for _, substr := range tt.warnContains {
|
||||||
|
assert.Contains(t, stderr, substr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user