+A terminal UI for [Apple Container](https://developer.apple.com/documentation/virtualization/running_linux_containers) -- the native macOS container runtime introduced in macOS 26.
-
-
-
+This is a fork of [lazydocker](https://github.com/jesseduffield/lazydocker) by Jesse Duffield, rewritten to work with Apple's `container` CLI instead of Docker.
-A simple terminal UI for both docker and docker-compose, written in Go with the [gocui](https://github.com/jroimartin/gocui 'gocui') library.
+## What is this?
-
-[](https://goreportcard.com/report/github.com/jesseduffield/lazydocker)
-[](https://golangci.com)
-[](http://godoc.org/github.com/jesseduffield/lazydocker)
-
-[](https://github.com/jesseduffield/lazydocker/releases)
-[](https://github.com/jesseduffield/lazydocker/releases/latest)
-[](https://github.com/Homebrew/homebrew-core/blob/master/Formula/lazydocker.rb)
+Apple introduced a native container runtime for macOS. It ships a CLI tool called `container` that can run Linux containers without Docker. **lazyapple** gives you a terminal UI to manage those containers, just like lazydocker does for Docker.
-
+### What works
-[Demo](https://youtu.be/NICqQPxwJWw)
+- **Containers**: list, start, stop, restart, kill, remove, view logs, exec shell, view stats/env/config/top
+- **Images**: list, pull, remove, prune
+- **Volumes**: list, remove, prune
+- **Networks**: list, remove, prune
-## Sponsors
+### What doesn't exist (because Apple Container doesn't support it)
-
- Maintenance of this project is made possible by all the contributors and sponsors. If you'd like to sponsor this project and have your avatar or company logo appear below click here. 💙
-
-
-
-
-
-
-## Elevator Pitch
-
-Minor rant incoming: Something's not working? Maybe a service is down. `docker-compose ps`. Yep, it's that microservice that's still buggy. No issue, I'll just restart it: `docker-compose restart`. Okay now let's try again. Oh wait the issue is still there. Hmm. `docker-compose ps`. Right so the service must have just stopped immediately after starting. I probably would have known that if I was reading the log stream, but there is a lot of clutter in there from other services. I could get the logs for just that one service with `docker compose logs --follow myservice` but that dies everytime the service dies so I'd need to run that command every time I restart the service. I could alternatively run `docker-compose up myservice` and in that terminal window if the service is down I could just `up` it again, but now I've got one service hogging a terminal window even after I no longer care about its logs. I guess when I want to reclaim the terminal realestate I can do `ctrl+P,Q`, but... wait, that's not working for some reason. Should I use ctrl+C instead? I can't remember if that closes the foreground process or kills the actual service.
-
-What a headache!
-
-Memorising docker commands is hard. Memorising aliases is slightly less hard. Keeping track of your containers across multiple terminal windows is near impossible. What if you had all the information you needed in one terminal window with every common command living one keypress away (and the ability to add custom commands as well). Lazydocker's goal is to make that dream a reality.
-
-- [Requirements](https://github.com/jesseduffield/lazydocker#requirements)
-- [Installation](https://github.com/jesseduffield/lazydocker#installation)
-- [Usage](https://github.com/jesseduffield/lazydocker#usage)
-- [Keybindings](/docs/keybindings)
-- [Cool Features](https://github.com/jesseduffield/lazydocker#cool-features)
-- [Contributing](https://github.com/jesseduffield/lazydocker#contributing)
-- [Video Tutorial](https://youtu.be/NICqQPxwJWw)
-- [Config Docs](/docs/Config.md)
-- [Twitch Stream](https://www.twitch.tv/jesseduffield)
-- [FAQ](https://github.com/jesseduffield/lazydocker#faq)
+- No docker-compose / services / projects
+- No pause/unpause
+- No attach (use exec shell instead)
+- No events streaming (polling is used)
+- No `--since` or `--timestamps` on logs
## Requirements
-- Docker >= **29.0.0** (API >= **1.24**)
-- Docker-Compose >= **1.23.2** (optional)
+- **macOS 26** (Tahoe) or later
+- Apple Container CLI installed (`container` command available in PATH)
-## Installation
-
-### Homebrew
-
-Normally `lazydocker` formula can be found in the Homebrew core but we suggest you to tap our formula to get frequently updated one. It works with Linux, too.
-
-**Tap**:
-```sh
-brew install jesseduffield/lazydocker/lazydocker
-```
-
-**Core**:
-```sh
-brew install lazydocker
-```
-
-### Scoop (Windows)
-
-You can install `lazydocker` using [scoop](https://scoop.sh/):
-
-```sh
-scoop install lazydocker
-```
-### Chocolatey (Windows)
-
-You can install `lazydocker` using [Chocolatey](https://chocolatey.org/):
-
-```sh
-choco install lazydocker
-```
-### asdf-vm
-
-You can install [asdf-lazydocker plugin](https://github.com/comdotlinux/asdf-lazydocker) using [asdf-vm](https://asdf-vm.com/):
-#### Setup (Once)
-```sh
-asdf plugin add lazydocker https://github.com/comdotlinux/asdf-lazydocker.git
-```
-
-#### For Install / Upgrade
-```sh
-asdf list all lazydocker
-asdf install lazydocker latest
-asdf global lazydocker latest
-```
-
-### Binary Release (Linux/OSX/Windows)
-
-You can manually download a binary release from [the release page](https://github.com/jesseduffield/lazydocker/releases).
-
-Automated install/update, don't forget to always verify what you're piping into bash:
-
-```sh
-curl https://raw.githubusercontent.com/jesseduffield/lazydocker/master/scripts/install_update_linux.sh | bash
-```
-The script installs downloaded binary to `$HOME/.local/bin` directory by default, but it can be changed by setting `DIR` environment variable.
-
-### Go
-
-Required Go Version >= **1.19**
-
-```sh
-go install github.com/jesseduffield/lazydocker@latest
-```
-
-Required Go version >= **1.8**, <= **1.17**
-
-```sh
-go get github.com/jesseduffield/lazydocker
-```
-
-### Arch Linux AUR
-
-You can install lazydocker using the [AUR](https://aur.archlinux.org/packages/lazydocker) by running:
-
-```sh
-yay -S lazydocker
-```
-
-### Docker
-
-[](https://hub.docker.com/r/lazyteam/lazydocker)
-[](https://hub.docker.com/r/lazyteam/lazydocker)
-[](https://hub.docker.com/r/lazyteam/lazydocker)
-
-1. Click if you have an ARM device
-
- - If you have a ARM 32 bit v6 architecture
-
- ```sh
- docker build -t lazyteam/lazydocker \
- --build-arg BASE_IMAGE_BUILDER=arm32v6/golang \
- --build-arg GOARCH=arm \
- --build-arg GOARM=6 \
- https://github.com/jesseduffield/lazydocker.git
- ```
-
- - If you have a ARM 32 bit v7 architecture
-
- ```sh
- docker build -t lazyteam/lazydocker \
- --build-arg BASE_IMAGE_BUILDER=arm32v7/golang \
- --build-arg GOARCH=arm \
- --build-arg GOARM=7 \
- https://github.com/jesseduffield/lazydocker.git
- ```
-
- - If you have a ARM 64 bit v8 architecture
-
- ```sh
- docker build -t lazyteam/lazydocker \
- --build-arg BASE_IMAGE_BUILDER=arm64v8/golang \
- --build-arg GOARCH=arm64 \
- https://github.com/jesseduffield/lazydocker.git
- ```
-
-
-
-1. Run the container
-
- ```sh
- docker run --rm -it -v \
- /var/run/docker.sock:/var/run/docker.sock \
- -v /yourpath:/.config/jesseduffield/lazydocker \
- lazyteam/lazydocker
- ```
-
- - Don't forget to change `/yourpath` to an actual path you created to store lazydocker's config
- - You can also use this [docker-compose.yml](https://github.com/jesseduffield/lazydocker/blob/master/docker-compose.yml)
- - You might want to create an alias, for example:
-
- ```sh
- echo "alias lzd='docker run --rm -it -v /var/run/docker.sock:/var/run/docker.sock -v /yourpath/config:/.config/jesseduffield/lazydocker lazyteam/lazydocker'" >> ~/.zshrc
- ```
-
-
-
-For development, you can build the image using:
-
-```sh
-git clone https://github.com/jesseduffield/lazydocker.git
-cd lazydocker
-docker build -t lazyteam/lazydocker \
- --build-arg BUILD_DATE=`date -u +"%Y-%m-%dT%H:%M:%SZ"` \
- --build-arg VCS_REF=`git rev-parse --short HEAD` \
- --build-arg VERSION=`git describe --abbrev=0 --tag` \
- .
-```
-
-If you encounter a compatibility issue with Docker bundled binary, try rebuilding
-the image with the build argument `--build-arg DOCKER_VERSION="v$(docker -v | cut -d" " -f3 | rev | cut -c 2- | rev)"`
-so that the bundled docker binary matches your host docker binary version.
-
-### Manual
-
-You'll need to [install Go](https://golang.org/doc/install)
+Verify it works:
```
-git clone https://github.com/jesseduffield/lazydocker.git
-cd lazydocker
-go install
+container list --all
```
-You can also use `go run main.go` to compile and run in one go (pun definitely intended)
+If you get `command not found`, Apple Container is not installed on your system.
-## Usage
+## Install
-Call `lazydocker` in your terminal. I personally use this a lot so I've made an alias for it like so:
+### Build from source
-```
-echo "alias lzd='lazydocker'" >> ~/.zshrc
+```bash
+git clone https://github.com/g-battaglia/lazy-apple-container.git
+cd lazy-apple-container
+go build -o lazyapple .
```
-(you can substitute .zshrc for whatever rc file you're using)
+### Run it
-- Basic video tutorial [here](https://youtu.be/NICqQPxwJWw).
-- List of keybindings
- [here](/docs/keybindings).
+```bash
+./lazyapple
+```
-## Cool features
+### Optional: install to PATH
-everything is one keypress away (or one click away! Mouse support FTW):
+```bash
+sudo cp lazyapple /usr/local/bin/
+```
-- viewing the state of your docker or docker-compose container environment at a glance
-- viewing logs for a container/service
-- viewing ascii graphs of your containers' metrics so that you can not only feel but also look like a developer
-- customising those graphs to measure nearly any metric you want
-- attaching to a container/service
-- restarting/removing/rebuilding containers/services
-- viewing the ancestor layers of a given image
-- pruning containers, images, or volumes that are hogging up disk space
+Then just run `lazyapple` from anywhere.
-## Contributing
+## Keybindings
-There is still a lot of work to go! Please check out the [contributing guide](CONTRIBUTING.md).
-For contributor discussion about things not better discussed here in the repo, join the discord channel
+### Global
-
+| Key | Action |
+|-----|--------|
+| `q` / `Ctrl+C` | Quit |
+| `1` | Containers |
+| `2` | Images |
+| `3` | Volumes |
+| `4` | Networks |
+| `x` / `?` | Menu |
+| `Enter` | Focus main panel |
+| `PgUp` / `PgDn` | Scroll |
+| `[` / `]` | Prev/next tab |
+| `/` | Filter list |
-## Donate
+### Containers
-If you would like to support the development of lazydocker, consider [sponsoring me](https://github.com/sponsors/jesseduffield) (github is matching all donations dollar-for-dollar for 12 months)
+| Key | Action |
+|-----|--------|
+| `s` | Stop |
+| `r` | Restart (stop + start) |
+| `k` | Kill |
+| `d` | Remove |
+| `E` | Exec shell |
+| `a` | Attach (exec shell) |
+| `m` | View logs in terminal |
+| `e` | Toggle stopped containers |
+| `w` | Open in browser |
-## Social
+### Images / Volumes / Networks
-If you want to see what I (Jesse) am up to in terms of development, follow me on
-[twitter](https://twitter.com/DuffieldJesse) or watch me program on
-[twitch](https://www.twitch.tv/jesseduffield)
+| Key | Action |
+|-----|--------|
+| `d` | Remove |
+| `b` | Bulk commands (prune, etc.) |
-## FAQ
+## Config
-### How do I edit my config?
+Config lives at `~/.config/lazyapple/config.yml`.
-By opening lazydocker, clicking on the 'project' panel in the top left, and pressing 'o' (or 'e' if your editor is vim). See [Config Docs](/docs/Config.md)
+```yaml
+gui:
+ scrollHeight: 2
+ sidePanelWidth: 0.333
+ theme:
+ activeBorderColor:
+ - green
+ - bold
+logs:
+ tail: "100"
+stats:
+ graphs:
+ - caption: CPU (%)
+ statPath: DerivedStats.CPUPercentage
+ color: blue
+ - caption: Memory (%)
+ statPath: DerivedStats.MemoryPercentage
+ color: green
+```
-### How do I get text to wrap in my main panel?
+## How it differs from lazydocker
-In the future I want to make this the default, but for now there are some CPU issues that arise with wrapping. If you want to enable wrapping, use `gui.wrapMainPanel: true`
+| | lazydocker | lazyapple |
+|---|---|---|
+| Runtime | Docker Engine | Apple Container |
+| CLI | `docker` | `container` |
+| Compose | Yes | No |
+| Services/Projects panels | Yes | No |
+| Pause/Unpause | Yes | No |
+| Events API | Yes (streaming) | No (polling) |
+| Platform | Linux, macOS, Windows | macOS 26+ only |
-### How do you select text?
+## Credits
-Because we support mouse events, you will need to hold option while dragging the mouse to indicate you're trying to select text rather than click on something. Alternatively you can disable mouse events via the `gui.ignoreMouseEvents` config value.
+Fork of [lazydocker](https://github.com/jesseduffield/lazydocker) by [Jesse Duffield](https://github.com/jesseduffield). All the TUI framework, panel system, and keybinding infrastructure comes from the original project.
-Mac Users: See [Issue #190](https://github.com/jesseduffield/lazydocker/issues/190) for other options.
+## License
-### Why can't I see my container's logs?
-
-By default we only show logs from the last hour, so that we're not putting too much strain on the machine. This may be why you can't see logs when you first start lazydocker. This can be overwritten in the config's `commandTemplates`
-
-If you are running lazydocker in Docker container, it is a know bug, that you can't see logs or CPU usage.
-
-## Alternatives
-
-- [docui](https://github.com/skanehira/docui) - Skanehira beat me to the punch on making a docker terminal UI, so definitely check out that repo as well! I think the two repos can live in harmony though: lazydocker is more about managing existing containers/services, and docui is more about creating and configuring them.
-- [Portainer](https://github.com/portainer/portainer) - Portainer tries to solve the same problem but it's accessed via your browser rather than your terminal. It also supports docker swarm.
-- See [Awesome Docker list](https://github.com/veggiemonk/awesome-docker/blob/master/README.md#terminal) for similar tools to work with Docker.
+MIT
diff --git a/go.mod b/go.mod
index bf2c2236..292a78f8 100644
--- a/go.mod
+++ b/go.mod
@@ -1,19 +1,14 @@
-module github.com/jesseduffield/lazydocker
+module github.com/jesseduffield/lazycontainer
-go 1.22
-
-toolchain go1.23.6
+go 1.24.0
require (
github.com/OpenPeeDeeP/xdg v0.2.1-0.20190312153938-4ba9e1eb294c
github.com/boz/go-throttle v0.0.0-20160922054636-fdc4eab740c1
github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21
- github.com/docker/cli v27.1.1+incompatible
- github.com/docker/docker v28.5.2+incompatible
github.com/fatih/color v1.10.0
github.com/go-errors/errors v1.5.1
github.com/gookit/color v1.5.0
- github.com/imdario/mergo v0.3.16
github.com/integrii/flaggy v1.4.0
github.com/jesseduffield/asciigraph v0.0.0-20190605104717-6d88e39309ee
github.com/jesseduffield/gocui v0.3.1-0.20240418080333-8cd33929c513
@@ -33,53 +28,26 @@ require (
)
require (
- github.com/containerd/errdefs v1.0.0 // indirect
- github.com/containerd/errdefs/pkg v0.3.0 // indirect
- github.com/moby/sys/atomicwriter v0.1.0 // indirect
- github.com/moby/sys/sequential v0.6.0 // indirect
+ dario.cat/mergo v1.0.2
+ github.com/google/go-cmp v0.7.0 // indirect
+ github.com/onsi/ginkgo v1.16.5 // indirect
+ github.com/onsi/gomega v1.39.1 // indirect
)
require (
- github.com/Microsoft/go-winio v0.6.2 // indirect
- github.com/containerd/log v0.1.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
- github.com/distribution/reference v0.6.0 // indirect
- github.com/docker/docker-credential-helpers v0.8.2 // indirect
- github.com/docker/go-connections v0.5.0 // indirect
- github.com/docker/go-units v0.5.0 // indirect
- github.com/felixge/httpsnoop v1.0.4 // indirect
- github.com/fvbommel/sortorder v1.1.0 // indirect
github.com/gdamore/encoding v1.0.1 // indirect
github.com/gdamore/tcell/v2 v2.7.4 // indirect
- github.com/go-logr/logr v1.4.2 // indirect
- github.com/go-logr/stdr v1.2.2 // indirect
github.com/goccy/go-yaml v1.11.0
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/mattn/go-colorable v0.1.8 // indirect
github.com/mattn/go-isatty v0.0.12 // indirect
- github.com/moby/docker-image-spec v1.3.1 // indirect
- github.com/moby/term v0.5.0 // indirect
- github.com/morikuni/aec v1.0.0 // indirect
- github.com/onsi/ginkgo v1.8.0 // indirect
- github.com/onsi/gomega v1.5.0 // indirect
- github.com/opencontainers/go-digest v1.0.0 // indirect
- github.com/opencontainers/image-spec v1.1.0 // indirect
github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 // indirect
- github.com/pkg/errors v0.9.1 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect
- go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 // indirect
- go.opentelemetry.io/otel v1.28.0 // indirect
- go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.28.0 // indirect
- go.opentelemetry.io/otel/metric v1.28.0 // indirect
- go.opentelemetry.io/otel/sdk v1.28.0 // indirect
- go.opentelemetry.io/otel/trace v1.28.0 // indirect
- golang.org/x/crypto v0.24.0 // indirect
golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect
- golang.org/x/sys v0.24.0 // indirect
+ golang.org/x/sys v0.40.0 // indirect
golang.org/x/term v0.21.0 // indirect
- golang.org/x/text v0.16.0 // indirect
- golang.org/x/time v0.5.0 // indirect
+ golang.org/x/text v0.33.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
- gotest.tools/v3 v3.5.1 // indirect
)
diff --git a/go.sum b/go.sum
index 4f20d188..3000a6ae 100644
--- a/go.sum
+++ b/go.sum
@@ -1,43 +1,19 @@
-github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
-github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
-github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
-github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
+dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8=
+dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA=
github.com/OpenPeeDeeP/xdg v0.2.1-0.20190312153938-4ba9e1eb294c h1:YDsGA6tou+tAxVe0Dre29iSbQ8TrWdWfwOisKArJT5E=
github.com/OpenPeeDeeP/xdg v0.2.1-0.20190312153938-4ba9e1eb294c/go.mod h1:tMoSueLQlMf0TCldjrJLNIjAc5qAOIcHt5REi88/Ygo=
github.com/boz/go-throttle v0.0.0-20160922054636-fdc4eab740c1 h1:1fx+RA5lk1ZkzPAUP7DEgZnVHYxEcHO77vQO/V8z/2Q=
github.com/boz/go-throttle v0.0.0-20160922054636-fdc4eab740c1/go.mod h1:z0nyIb42Zs97wyX1V+8MbEFhHeTw1OgFQfR6q57ZuHc=
-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/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21 h1:tuijfIjZyjZaHq9xDUh0tNitwXshJpbLkqMOJv4H3do=
github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21/go.mod h1:po7NpZ/QiTKzBKyrsEAxwnTamCoh8uDk/egRpQ7siIc=
-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/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE=
-github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk=
-github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
-github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
-github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
-github.com/docker/cli v27.1.1+incompatible h1:goaZxOqs4QKxznZjjBWKONQci/MywhtRv2oNn0GkeZE=
-github.com/docker/cli v27.1.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
-github.com/docker/docker v28.5.2+incompatible h1:DBX0Y0zAjZbSrm1uzOkdr1onVghKaftjlSWt4AFexzM=
-github.com/docker/docker v28.5.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
-github.com/docker/docker-credential-helpers v0.8.2 h1:bX3YxiGzFP5sOXWc3bTPEXdEaZSeVMrFgOr3T+zrFAo=
-github.com/docker/docker-credential-helpers v0.8.2/go.mod h1:P3ci7E3lwkZg6XiHdRKft1KckHiO9a2rNtyFbZ/ry9M=
-github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
-github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
-github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
-github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/fatih/color v1.10.0 h1:s36xzo75JdqLaaWoiEHk767eHiwo0598uUxyfiPkDsg=
github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
-github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
-github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
-github.com/fvbommel/sortorder v1.1.0 h1:fUmoe+HLsBTctBDoaBwpQo5N+nrCp8g/BjKb/6ZQmYw=
-github.com/fvbommel/sortorder v1.1.0/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0=
+github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
+github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
github.com/gdamore/encoding v1.0.1 h1:YzKZckdBL6jVt2Gc+5p82qhrGiqMdG/eNs6Wy0u3Uhw=
github.com/gdamore/encoding v1.0.1/go.mod h1:0Z0cMFinngz9kS1QfMjCP8TY7em3bZYeeklsSDPivEo=
@@ -46,32 +22,30 @@ github.com/gdamore/tcell/v2 v2.7.4/go.mod h1:dSXtXTSK0VsW1biw65DZLZ2NKr7j0qP/0J7
github.com/go-errors/errors v1.0.2/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWEp4lssFEs=
github.com/go-errors/errors v1.5.1 h1:ZwEMSLRCapFLflTpT7NKaAc7ukJ8ZPEjzlxt8rPN8bk=
github.com/go-errors/errors v1.5.1/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
-github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
-github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
-github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
-github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
-github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE=
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
+github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/goccy/go-yaml v1.11.0 h1:n7Z+zx8S9f9KgzG6KtQKf+kwqXZlLNR2F6018Dgau54=
github.com/goccy/go-yaml v1.11.0/go.mod h1:H+mJrWtjPTJAHvRbV09MCK9xYwODM+wRTVFFTWckfng=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
-github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
-github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
-github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+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.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
+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.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
+github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/gookit/color v1.5.0 h1:1Opow3+BWDwqor78DcJkJCIwnkviFi+rrOANki9BUFw=
github.com/gookit/color v1.5.0/go.mod h1:43aQb+Zerm/BWh2GnrgOQm7ffz7tvQXEKV6BFMl7wAo=
-github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0=
-github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k=
-github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
-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/integrii/flaggy v1.4.0 h1:A1x7SYx4jqu5NSrY14z8Z+0UyX2S5ygfJJrfolWR3zM=
github.com/integrii/flaggy v1.4.0/go.mod h1:tnTxHeTJbah0gQ6/K0RW0J7fMUBk9MCF5blhm43LNpI=
github.com/jesseduffield/asciigraph v0.0.0-20190605104717-6d88e39309ee h1:7Zi/OQlGbMz4MT2V1+prN/gv1C64NDyVb/MbJnS0ZfA=
@@ -100,31 +74,21 @@ github.com/mcuadros/go-lookup v0.0.0-20171110082742-5650f26be767 h1:BrhJNdEFWGui
github.com/mcuadros/go-lookup v0.0.0-20171110082742-5650f26be767/go.mod h1:ct+byCpkFokm4J0tiuAvB8cf2ttm6GcCe89Yr25nGKg=
github.com/mgutz/str v1.2.0 h1:4IzWSdIz9qPQWLfKZ0rJcV0jcUDpxvP4JVZ4GXQyvSw=
github.com/mgutz/str v1.2.0/go.mod h1:w1v0ofgLaJdoD0HpQ3fycxKD1WtxpjSo151pK/31q6w=
-github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
-github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
-github.com/moby/sys/atomicwriter v0.1.0 h1:kw5D/EqkBwsBFi0ss9v1VG3wIkVhzGvLklJ+w3A14Sw=
-github.com/moby/sys/atomicwriter v0.1.0/go.mod h1:Ul8oqv2ZMNHOceF643P6FKPXeCmYtlQMvpizfsSoaWs=
-github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU=
-github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko=
-github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
-github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
-github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
-github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
+github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
+github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
+github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
-github.com/onsi/ginkgo v1.8.0 h1:VkHVNpR4iVnU8XQR6DBm8BqYjN7CRzw+xKUbVVbbW9w=
-github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
-github.com/onsi/gomega v1.5.0 h1:izbySO9zDPmjJ8rDjLvkA2zJHIo+HkYXHnf7eN7SSyo=
-github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
-github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
-github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
-github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
-github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
+github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
+github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
+github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
+github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
+github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
+github.com/onsi/gomega v1.39.1 h1:1IJLAad4zjPn2PsnhH70V4DKRFlrCzGBNrNaru+Vf28=
+github.com/onsi/gomega v1.39.1/go.mod h1:hL6yVALoTOxeWudERyfppUcZXjMwIMLnuSfruD2lcfg=
github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 h1:q2e307iGHPdTGp0hoxKjt1H5pDo6utceo3dQVK3I5XQ=
github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCrJTQWu0XVbaOlby/2lO20uSCHEMzzplHXte1o=
-github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
-github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
@@ -144,6 +108,7 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
@@ -151,47 +116,48 @@ github.com/thoas/go-funk v0.9.1 h1:O549iLZqPpTUQ10ykd26sZhzD+rmR5pWhuElrhbC20M=
github.com/thoas/go-funk v0.9.1/go.mod h1:+IWnUfUmFO1+WVYQWQtIJHeRRdaIyyYglZN7xzUPe4Q=
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 h1:QldyIu/L63oPpyvQmHgvgickp1Yw510KJOqX7H24mg8=
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs=
+github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
-go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 h1:4K4tsIXefpVJtvA/8srF4V4y0akAoPHkIslgAkjixJA=
-go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0/go.mod h1:jjdQuTGVsXV4vSs+CJ2qYDeDPf9yIJV23qlIzBm73Vg=
-go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo=
-go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 h1:3Q/xZUyC1BBkualc9ROb4G8qkH90LXEIICcs5zv1OYY=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0/go.mod h1:s75jGIWA9OfCMzF0xr+ZgfrB5FEbbV7UuYo32ahUiFI=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.28.0 h1:j9+03ymgYhPKmeXGk5Zu+cIZOlVzd9Zv7QIiyItjFBU=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.28.0/go.mod h1:Y5+XiUG4Emn1hTfciPzGPJaSI+RpDts6BnCIir0SLqk=
-go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q=
-go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s=
-go.opentelemetry.io/otel/sdk v1.28.0 h1:b9d7hIry8yZsgtbmM0DKyPWMMUMlK9NEKuIG4aBqWyE=
-go.opentelemetry.io/otel/sdk v1.28.0/go.mod h1:oYj7ClPUA7Iw3m+r7GeEjz0qckQRJK2B8zjcZEfu7Pg=
-go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g=
-go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI=
-go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0=
-go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8=
+go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
+go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
-golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
-golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
+golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A=
+golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
+golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
-golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
-golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
+golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=
+golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/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-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/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-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/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-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -199,8 +165,8 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
-golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
-golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
+golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/term v0.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.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
@@ -212,36 +178,33 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
-golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
-golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
-golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
-golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
+golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=
+golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
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/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094 h1:0+ozOGcrp+Y8Aq8TLNN2Aliibms5LEzsq99ZZmAGYm0=
-google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094/go.mod h1:fJ/e3If/Q67Mj99hin0hMhiNyCRmt6BQ2aWIJshUSJw=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 h1:pPJltXNxVzT4pK9yD8vR9X75DaWYYmLGMsEvBfFQZzQ=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU=
-google.golang.org/grpc v1.67.0 h1:IdH9y6PF5MPSdAntIcpjQ+tXO41pcQsfZV2RxtQgVcw=
-google.golang.org/grpc v1.67.0/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA=
-google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
-google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
+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.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
-gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
-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.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/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.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU=
-gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=
diff --git a/lazycontainer b/lazycontainer
new file mode 100755
index 00000000..69dbfb99
Binary files /dev/null and b/lazycontainer differ
diff --git a/main.go b/main.go
index e7679b10..b002e675 100644
--- a/main.go
+++ b/main.go
@@ -8,12 +8,11 @@ import (
"runtime"
"runtime/debug"
- "github.com/docker/docker/client"
"github.com/go-errors/errors"
"github.com/integrii/flaggy"
- "github.com/jesseduffield/lazydocker/pkg/app"
- "github.com/jesseduffield/lazydocker/pkg/config"
- "github.com/jesseduffield/lazydocker/pkg/utils"
+ "github.com/jesseduffield/lazycontainer/pkg/app"
+ "github.com/jesseduffield/lazycontainer/pkg/config"
+ "github.com/jesseduffield/lazycontainer/pkg/utils"
"github.com/jesseduffield/yaml"
"github.com/samber/lo"
)
@@ -28,8 +27,6 @@ var (
configFlag = false
debuggingFlag = false
- composeFiles []string
- projectName string
)
func main() {
@@ -45,14 +42,12 @@ func main() {
runtime.GOARCH,
)
- flaggy.SetName("lazydocker")
- flaggy.SetDescription("The lazier way to manage everything docker")
- flaggy.DefaultParser.AdditionalHelpPrepend = "https://github.com/jesseduffield/lazydocker"
+ flaggy.SetName("lazyapple")
+ flaggy.SetDescription("The lazier way to manage Apple Containers")
+ flaggy.DefaultParser.AdditionalHelpPrepend = "https://github.com/g-battaglia/lazy-apple-container"
flaggy.Bool(&configFlag, "c", "config", "Print the current default config")
flaggy.Bool(&debuggingFlag, "d", "debug", "a boolean")
- flaggy.StringSlice(&composeFiles, "f", "file", "Specify alternate compose files")
- flaggy.String(&projectName, "p", "project", "Specify a docker compose project name")
flaggy.SetVersion(info)
flaggy.Parse()
@@ -73,7 +68,7 @@ func main() {
log.Fatal(err.Error())
}
- appConfig, err := config.NewAppConfig("lazydocker", version, commit, date, buildSource, debuggingFlag, composeFiles, projectDir, projectName)
+ appConfig, err := config.NewAppConfig("lazyapple", version, commit, date, buildSource, debuggingFlag, nil, projectDir, "")
if err != nil {
log.Fatal(err.Error())
}
@@ -90,11 +85,6 @@ func main() {
os.Exit(0)
}
- if client.IsErrConnectionFailed(err) {
- log.Println(app.Tr.ConnectionFailed)
- os.Exit(0)
- }
-
newErr := errors.Wrap(err, 0)
stackTrace := newErr.ErrorStack()
app.Log.Error(stackTrace)
@@ -111,12 +101,9 @@ func updateBuildInfo() {
})
if ok {
commit = revision.Value
- // if lazydocker was built from source we'll show the version as the
- // abbreviated commit hash
version = utils.SafeTruncate(revision.Value, 7)
}
- // if version hasn't been set we assume that neither has the date
time, ok := lo.Find(buildInfo.Settings, func(setting debug.BuildSetting) bool {
return setting.Key == "vcs.time"
})
diff --git a/pkg/app/app.go b/pkg/app/app.go
index 09a49789..517273e7 100644
--- a/pkg/app/app.go
+++ b/pkg/app/app.go
@@ -4,29 +4,27 @@ import (
"io"
"strings"
- "github.com/jesseduffield/lazydocker/pkg/commands"
- "github.com/jesseduffield/lazydocker/pkg/config"
- "github.com/jesseduffield/lazydocker/pkg/gui"
- "github.com/jesseduffield/lazydocker/pkg/i18n"
- "github.com/jesseduffield/lazydocker/pkg/log"
- "github.com/jesseduffield/lazydocker/pkg/utils"
+ "github.com/jesseduffield/lazycontainer/pkg/commands"
+ "github.com/jesseduffield/lazycontainer/pkg/config"
+ "github.com/jesseduffield/lazycontainer/pkg/gui"
+ "github.com/jesseduffield/lazycontainer/pkg/i18n"
+ "github.com/jesseduffield/lazycontainer/pkg/log"
+ "github.com/jesseduffield/lazycontainer/pkg/utils"
"github.com/sirupsen/logrus"
)
-// App struct
type App struct {
closers []io.Closer
Config *config.AppConfig
Log *logrus.Entry
OSCommand *commands.OSCommand
- DockerCommand *commands.DockerCommand
+ ContainerCmd *commands.ContainerCommand
Gui *gui.Gui
Tr *i18n.TranslationSet
ErrorChan chan error
}
-// NewApp bootstrap a new application
func NewApp(config *config.AppConfig) (*App, error) {
app := &App{
closers: []io.Closer{},
@@ -41,14 +39,12 @@ func NewApp(config *config.AppConfig) (*App, error) {
}
app.OSCommand = commands.NewOSCommand(app.Log, config)
- // here is the place to make use of the docker-compose.yml file in the current directory
-
- app.DockerCommand, err = commands.NewDockerCommand(app.Log, app.OSCommand, app.Tr, app.Config, app.ErrorChan)
+ app.ContainerCmd, err = commands.NewContainerCommand(app.Log, app.OSCommand, app.Tr, app.Config, app.ErrorChan)
if err != nil {
return app, err
}
- app.closers = append(app.closers, app.DockerCommand)
- app.Gui, err = gui.NewGui(app.Log, app.DockerCommand, app.OSCommand, app.Tr, config, app.ErrorChan)
+ app.closers = append(app.closers, app.ContainerCmd)
+ app.Gui, err = gui.NewGui(app.Log, app.ContainerCmd, app.OSCommand, app.Tr, config, app.ErrorChan)
if err != nil {
return app, err
}
@@ -68,14 +64,17 @@ type errorMapping struct {
newError string
}
-// KnownError takes an error and tells us whether it's an error that we know about where we can print a nicely formatted version of it rather than panicking with a stack trace
func (app *App) KnownError(err error) (string, bool) {
errorMessage := err.Error()
mappings := []errorMapping{
{
- originalError: "Got permission denied while trying to connect to the Docker daemon socket",
- newError: app.Tr.CannotAccessDockerSocketError,
+ originalError: "container: command not found",
+ newError: "Apple Container is not installed. Please install it and try again.",
+ },
+ {
+ originalError: "container system is not running",
+ newError: "Apple Container system is not running. Run 'container system start' first.",
},
}
diff --git a/pkg/cheatsheet/generate.go b/pkg/cheatsheet/generate.go
index f837da62..f374f559 100644
--- a/pkg/cheatsheet/generate.go
+++ b/pkg/cheatsheet/generate.go
@@ -13,10 +13,10 @@ import (
"log"
"os"
- "github.com/jesseduffield/lazydocker/pkg/app"
- "github.com/jesseduffield/lazydocker/pkg/config"
- "github.com/jesseduffield/lazydocker/pkg/gui"
- "github.com/jesseduffield/lazydocker/pkg/i18n"
+ "github.com/jesseduffield/lazycontainer/pkg/app"
+ "github.com/jesseduffield/lazycontainer/pkg/config"
+ "github.com/jesseduffield/lazycontainer/pkg/gui"
+ "github.com/jesseduffield/lazycontainer/pkg/i18n"
)
const (
@@ -33,7 +33,7 @@ func Generate() {
}
func generateAtDir(dir string) {
- mConfig, err := config.NewAppConfig("lazydocker", "", "", "", "", true, nil, "", "")
+ mConfig, err := config.NewAppConfig("lazyapple", "", "", "", "", true, nil, "", "")
if err != nil {
panic(err)
}
diff --git a/pkg/commands/apple_types.go b/pkg/commands/apple_types.go
new file mode 100644
index 00000000..a6feb9d5
--- /dev/null
+++ b/pkg/commands/apple_types.go
@@ -0,0 +1,171 @@
+package commands
+
+type AppleContainer struct {
+ Configuration struct {
+ ID string `json:"id"`
+ Name string `json:"-"`
+ Image struct {
+ Reference string `json:"reference"`
+ Descriptor struct {
+ Digest string `json:"digest"`
+ Size int64 `json:"size"`
+ MediaType string `json:"mediaType"`
+ } `json:"descriptor"`
+ } `json:"image"`
+ Labels map[string]string `json:"labels"`
+ Resources struct {
+ CPUS int `json:"cpus"`
+ MemoryInBytes int64 `json:"memoryInBytes"`
+ } `json:"resources"`
+ Platform struct {
+ Architecture string `json:"architecture"`
+ OS string `json:"os"`
+ } `json:"platform"`
+ InitProcess struct {
+ Environment []string `json:"environment"`
+ Executable string `json:"executable"`
+ Arguments []string `json:"arguments"`
+ WorkingDir string `json:"workingDirectory"`
+ Terminal bool `json:"terminal"`
+ User struct {
+ ID struct {
+ UID int `json:"uid"`
+ GID int `json:"gid"`
+ } `json:"id"`
+ } `json:"user"`
+ } `json:"initProcess"`
+ Mounts []Mount `json:"mounts"`
+ PublishedPorts []struct {
+ HostIP string `json:"hostIP"`
+ HostPort int `json:"hostPort"`
+ ContainerPort int `json:"containerPort"`
+ Protocol string `json:"protocol"`
+ } `json:"publishedPorts"`
+ Networks []struct {
+ Network string `json:"network"`
+ Options struct {
+ Hostname string `json:"hostname"`
+ } `json:"options"`
+ } `json:"networks"`
+ RuntimeHandler string `json:"runtimeHandler"`
+ Rosetta bool `json:"rosetta"`
+ UseInit bool `json:"useInit"`
+ Virtualization bool `json:"virtualization"`
+ ReadOnly bool `json:"readOnly"`
+ SSH bool `json:"ssh"`
+ } `json:"configuration"`
+ Networks []struct {
+ Network string `json:"network"`
+ IPv4Address string `json:"ipv4Address"`
+ IPv6Address string `json:"ipv6Address"`
+ IPv4Gateway string `json:"ipv4Gateway"`
+ MACAddress string `json:"macAddress"`
+ Hostname string `json:"hostname"`
+ } `json:"networks"`
+ Status string `json:"status"`
+ StartedDate float64 `json:"startedDate"`
+}
+
+type Mount struct {
+ Type string `json:"type"`
+ Source string `json:"source"`
+ Target string `json:"target"`
+ ReadOnly bool `json:"readonly"`
+}
+
+type AppleContainerStats struct {
+ ID string `json:"id"`
+ CPUUsageUsec int64 `json:"cpuUsageUsec"`
+ MemoryUsageBytes int64 `json:"memoryUsageBytes"`
+ MemoryLimitBytes int64 `json:"memoryLimitBytes"`
+ NetworkRxBytes int64 `json:"networkRxBytes"`
+ NetworkTxBytes int64 `json:"networkTxBytes"`
+ BlockReadBytes int64 `json:"blockReadBytes"`
+ BlockWriteBytes int64 `json:"blockWriteBytes"`
+ NumProcesses int `json:"numProcesses"`
+}
+
+type AppleImage struct {
+ Reference string `json:"reference"`
+ Descriptor struct {
+ Digest string `json:"digest"`
+ Size int64 `json:"size"`
+ MediaType string `json:"mediaType"`
+ } `json:"descriptor"`
+ FullSize string `json:"fullSize"`
+}
+
+type AppleVolume struct {
+ Name string `json:"name"`
+ SizeInBytes int64 `json:"sizeInBytes"`
+}
+
+type AppleNetwork struct {
+ ID string `json:"id"`
+ Config struct {
+ Labels map[string]string `json:"labels"`
+ CreationDate float64 `json:"creationDate"`
+ Mode string `json:"mode"`
+ PluginInfo struct {
+ Variant string `json:"variant"`
+ Plugin string `json:"plugin"`
+ } `json:"pluginInfo"`
+ } `json:"config"`
+ State string `json:"state"`
+ Status struct {
+ IPv4Subnet string `json:"ipv4Subnet"`
+ IPv6Subnet string `json:"ipv6Subnet"`
+ IPv4Gateway string `json:"ipv4Gateway"`
+ } `json:"status"`
+}
+
+type SystemDF struct {
+ Containers struct {
+ Active int `json:"active"`
+ Reclaimable int `json:"reclaimable"`
+ SizeInBytes int64 `json:"sizeInBytes"`
+ Total int `json:"total"`
+ } `json:"containers"`
+ Images struct {
+ Active int `json:"active"`
+ Reclaimable int `json:"reclaimable"`
+ SizeInBytes int64 `json:"sizeInBytes"`
+ Total int `json:"total"`
+ } `json:"images"`
+ Volumes struct {
+ Active int `json:"active"`
+ Reclaimable int `json:"reclaimable"`
+ SizeInBytes int64 `json:"sizeInBytes"`
+ Total int `json:"total"`
+ } `json:"volumes"`
+}
+
+type SystemStatus struct {
+ Running bool
+}
+
+type CreateContainerOptions struct {
+ Name string
+ Image string
+ Command []string
+ Env []string
+ WorkingDir string
+ CPUS int
+ Memory string
+ Volumes []string
+ Networks []string
+ Ports []string
+ Labels map[string]string
+ Detach bool
+ Interactive bool
+ TTY bool
+}
+
+type ExecOptions struct {
+ Command []string
+ Env []string
+ WorkingDir string
+ Interactive bool
+ TTY bool
+ User string
+}
diff --git a/pkg/commands/container.go b/pkg/commands/container.go
index 8e33ff60..2078eb4f 100644
--- a/pkg/commands/container.go
+++ b/pkg/commands/container.go
@@ -6,45 +6,32 @@ import (
"os/exec"
"strings"
- "github.com/docker/docker/api/types/container"
- "github.com/docker/docker/api/types/filters"
- "github.com/docker/docker/client"
"github.com/go-errors/errors"
- "github.com/jesseduffield/lazydocker/pkg/i18n"
- "github.com/jesseduffield/lazydocker/pkg/utils"
+ "github.com/jesseduffield/lazycontainer/pkg/i18n"
+ "github.com/jesseduffield/lazycontainer/pkg/utils"
"github.com/sasha-s/go-deadlock"
"github.com/sirupsen/logrus"
"golang.org/x/xerrors"
)
-// Container : A docker Container
type Container struct {
Name string
- ServiceName string
- ContainerNumber string // might make this an int in the future if need be
-
- // OneOff tells us if the container is just a job container or is actually bound to the service
- OneOff bool
- ProjectName string
ID string
- Container container.Summary
- Client *client.Client
+ AppleContainer AppleContainer
+ Client *ContainerClient
OSCommand *OSCommand
Log *logrus.Entry
StatHistory []*RecordedStats
- Details container.InspectResponse
MonitoringStats bool
- DockerCommand LimitedDockerCommand
Tr *i18n.TranslationSet
StatsMutex deadlock.Mutex
}
-// Remove removes the container
-func (c *Container) Remove(options container.RemoveOptions) error {
+func (c *Container) Remove(force bool) error {
c.Log.Warn(fmt.Sprintf("removing container %s", c.Name))
- if err := c.Client.ContainerRemove(context.Background(), c.ID, options); err != nil {
- if strings.Contains(err.Error(), "Stop the container before attempting removal or force remove") {
+ if err := c.Client.RemoveContainer(c.ID, force); err != nil {
+ if strings.Contains(err.Error(), "Stop the container before attempting removal") {
return ComplexError{
Code: MustStopContainer,
Message: err.Error(),
@@ -53,100 +40,146 @@ func (c *Container) Remove(options container.RemoveOptions) error {
}
return err
}
-
return nil
}
-// Start starts the container
func (c *Container) Start() error {
c.Log.Warn(fmt.Sprintf("starting container %s", c.Name))
- return c.Client.ContainerStart(context.Background(), c.ID, container.StartOptions{})
+ return c.Client.StartContainer(c.ID)
}
-// Stop stops the container
func (c *Container) Stop() error {
c.Log.Warn(fmt.Sprintf("stopping container %s", c.Name))
- return c.Client.ContainerStop(context.Background(), c.ID, container.StopOptions{})
+ return c.Client.StopContainer(c.ID)
}
-// Pause pauses the container
func (c *Container) Pause() error {
c.Log.Warn(fmt.Sprintf("pausing container %s", c.Name))
- return c.Client.ContainerPause(context.Background(), c.ID)
+ return errors.New("pause is not supported by Apple Container")
}
-// Unpause unpauses the container
func (c *Container) Unpause() error {
c.Log.Warn(fmt.Sprintf("unpausing container %s", c.Name))
- return c.Client.ContainerUnpause(context.Background(), c.ID)
+ return errors.New("unpause is not supported by Apple Container")
}
-// Restart restarts the container
func (c *Container) Restart() error {
c.Log.Warn(fmt.Sprintf("restarting container %s", c.Name))
- return c.Client.ContainerRestart(context.Background(), c.ID, container.StopOptions{})
+ return c.Client.RestartContainer(c.ID)
+}
+
+func (c *Container) Kill(signal string) error {
+ c.Log.Warn(fmt.Sprintf("killing container %s with signal %s", c.Name, signal))
+ return c.Client.KillContainer(c.ID, signal)
}
-// Attach attaches the container
func (c *Container) Attach() (*exec.Cmd, error) {
- if !c.DetailsLoaded() {
- return nil, errors.New(c.Tr.WaitingForContainerInfo)
- }
-
- // verify that we can in fact attach to this container
- if !c.Details.Config.OpenStdin {
- return nil, errors.New(c.Tr.UnattachableContainerError)
- }
-
- if c.Container.State == "exited" {
+ c.Log.Warn(fmt.Sprintf("attaching to container %s", c.Name))
+ if c.AppleContainer.Status != "running" {
return nil, errors.New(c.Tr.CannotAttachStoppedContainerError)
}
-
- c.Log.Warn(fmt.Sprintf("attaching to container %s", c.Name))
- // TODO: use SDK
- cmd := c.OSCommand.NewCmd("docker", "attach", "--sig-proxy=false", c.ID)
- return cmd, nil
+ return c.Client.ExecContainer(c.ID, ExecOptions{
+ Command: []string{c.getShell()},
+ Interactive: true,
+ TTY: true,
+ }), nil
}
-// Top returns process information
-func (c *Container) Top(ctx context.Context) (container.TopResponse, error) {
- detail, err := c.Inspect()
+func (c *Container) getShell() string {
+ for _, env := range c.AppleContainer.Configuration.InitProcess.Environment {
+ if strings.HasPrefix(env, "PATH=") {
+ path := strings.TrimPrefix(env, "PATH=")
+ for _, dir := range strings.Split(path, ":") {
+ for _, shell := range []string{"/bin/sh", "/bin/bash", "/usr/bin/bash"} {
+ if dir == "/bin" || dir == "/usr/bin" {
+ return shell
+ }
+ }
+ }
+ }
+ }
+ return "/bin/sh"
+}
+
+func (c *Container) Top(ctx context.Context) ([][]string, []string, error) {
+ cmd := c.Client.ExecContainer(c.ID, ExecOptions{
+ Command: []string{"ps", "aux"},
+ })
+ output, err := cmd.Output()
if err != nil {
- return container.TopResponse{}, err
+ return nil, nil, err
}
- // check container status
- if !detail.State.Running {
- return container.TopResponse{}, errors.New("container is not running")
+ lines := strings.Split(strings.TrimSpace(string(output)), "\n")
+ if len(lines) == 0 {
+ return nil, nil, nil
}
- return c.Client.ContainerTop(ctx, c.ID, []string{})
+ titles := strings.Fields(lines[0])
+ var processes [][]string
+ for _, line := range lines[1:] {
+ if strings.TrimSpace(line) != "" {
+ processes = append(processes, strings.Fields(line))
+ }
+ }
+
+ return processes, titles, nil
}
-// PruneContainers prunes containers
-func (c *DockerCommand) PruneContainers() error {
- _, err := c.Client.ContainersPrune(context.Background(), filters.Args{})
- return err
+func (c *Container) Inspect() (*AppleContainer, error) {
+ return c.Client.InspectContainer(c.ID)
}
-// Inspect returns details about the container
-func (c *Container) Inspect() (container.InspectResponse, error) {
- return c.Client.ContainerInspect(context.Background(), c.ID)
-}
-
-// RenderTop returns details about the container
func (c *Container) RenderTop(ctx context.Context) (string, error) {
- result, err := c.Top(ctx)
+ processes, titles, err := c.Top(ctx)
if err != nil {
return "", err
}
- return utils.RenderTable(append([][]string{result.Titles}, result.Processes...))
+ return utils.RenderTable(append([][]string{titles}, processes...))
}
-// DetailsLoaded tells us whether we have yet loaded the details for a container.
-// Sometimes it takes some time for a container to have its details loaded
-// after it starts.
func (c *Container) DetailsLoaded() bool {
- return c.Details.ContainerJSONBase != nil
+ return c.AppleContainer.Configuration.ID != ""
+}
+
+func (c *Container) GetStatus() string {
+ return c.AppleContainer.Status
+}
+
+func (c *Container) GetImage() string {
+ return c.AppleContainer.Configuration.Image.Reference
+}
+
+func (c *Container) GetIP() string {
+ for _, net := range c.AppleContainer.Networks {
+ if net.IPv4Address != "" {
+ return strings.Split(net.IPv4Address, "/")[0]
+ }
+ }
+ return ""
+}
+
+func (c *Container) GetPorts() []string {
+ var ports []string
+ for _, p := range c.AppleContainer.Configuration.PublishedPorts {
+ ports = append(ports, fmt.Sprintf("%s:%d->%d/%s", p.HostIP, p.HostPort, p.ContainerPort, p.Protocol))
+ }
+ return ports
+}
+
+func (c *Container) GetEnv() []string {
+ return c.AppleContainer.Configuration.InitProcess.Environment
+}
+
+func (c *Container) GetLabels() map[string]string {
+ return c.AppleContainer.Configuration.Labels
+}
+
+func (c *Container) GetCPUs() int {
+ return c.AppleContainer.Configuration.Resources.CPUS
+}
+
+func (c *Container) GetMemory() int64 {
+ return c.AppleContainer.Configuration.Resources.MemoryInBytes
}
diff --git a/pkg/commands/container_client.go b/pkg/commands/container_client.go
new file mode 100644
index 00000000..c4751e50
--- /dev/null
+++ b/pkg/commands/container_client.go
@@ -0,0 +1,632 @@
+package commands
+
+import (
+ "encoding/json"
+ "fmt"
+ "io"
+ "os/exec"
+ "strings"
+ "time"
+
+ "github.com/jesseduffield/lazycontainer/pkg/config"
+ "github.com/jesseduffield/lazycontainer/pkg/i18n"
+ "github.com/sasha-s/go-deadlock"
+ "github.com/sirupsen/logrus"
+)
+
+type ContainerClient struct {
+ Log *logrus.Entry
+ OSCommand *OSCommand
+ Tr *i18n.TranslationSet
+ Config *config.AppConfig
+ ErrorChan chan error
+
+ ContainerMutex deadlock.Mutex
+ ImageMutex deadlock.Mutex
+ VolumeMutex deadlock.Mutex
+ NetworkMutex deadlock.Mutex
+}
+
+func NewContainerClient(log *logrus.Entry, osCommand *OSCommand, tr *i18n.TranslationSet, config *config.AppConfig, errorChan chan error) (*ContainerClient, error) {
+ return &ContainerClient{
+ Log: log,
+ OSCommand: osCommand,
+ Tr: tr,
+ Config: config,
+ ErrorChan: errorChan,
+ }, nil
+}
+
+func (c *ContainerClient) runContainerCommand(args ...string) ([]byte, error) {
+ cmd := exec.Command("container", args...)
+ output, err := cmd.Output()
+ if err != nil {
+ if exitErr, ok := err.(*exec.ExitError); ok {
+ return nil, fmt.Errorf("%s: %s", err.Error(), string(exitErr.Stderr))
+ }
+ return nil, err
+ }
+ return output, nil
+}
+
+func (c *ContainerClient) runContainerCommandWithOutput(args ...string) (string, error) {
+ output, err := c.runContainerCommand(args...)
+ return string(output), err
+}
+
+// Containers
+
+func (c *ContainerClient) ListContainers(all bool) ([]AppleContainer, error) {
+ args := []string{"list", "--format", "json"}
+ if all {
+ args = []string{"list", "--all", "--format", "json"}
+ }
+
+ output, err := c.runContainerCommand(args...)
+ if err != nil {
+ return nil, err
+ }
+
+ if len(output) == 0 {
+ return []AppleContainer{}, nil
+ }
+
+ var containers []AppleContainer
+ if err := json.Unmarshal(output, &containers); err != nil {
+ return nil, fmt.Errorf("failed to parse container list: %w", err)
+ }
+
+ return containers, nil
+}
+
+func (c *ContainerClient) InspectContainer(id string) (*AppleContainer, error) {
+ output, err := c.runContainerCommand("inspect", id)
+ if err != nil {
+ return nil, err
+ }
+
+ var containers []AppleContainer
+ if err := json.Unmarshal(output, &containers); err != nil {
+ return nil, fmt.Errorf("failed to parse container inspect: %w", err)
+ }
+
+ if len(containers) == 0 {
+ return nil, fmt.Errorf("container %s not found", id)
+ }
+
+ return &containers[0], nil
+}
+
+func (c *ContainerClient) CreateContainer(opts CreateContainerOptions) error {
+ args := []string{"create"}
+
+ if opts.Name != "" {
+ args = append(args, "--name", opts.Name)
+ }
+
+ for _, env := range opts.Env {
+ args = append(args, "-e", env)
+ }
+
+ if opts.WorkingDir != "" {
+ args = append(args, "-w", opts.WorkingDir)
+ }
+
+ if opts.CPUS > 0 {
+ args = append(args, "-c", fmt.Sprintf("%d", opts.CPUS))
+ }
+
+ if opts.Memory != "" {
+ args = append(args, "-m", opts.Memory)
+ }
+
+ for _, vol := range opts.Volumes {
+ args = append(args, "-v", vol)
+ }
+
+ for _, net := range opts.Networks {
+ args = append(args, "--network", net)
+ }
+
+ for _, port := range opts.Ports {
+ args = append(args, "-p", port)
+ }
+
+ for k, v := range opts.Labels {
+ args = append(args, "-l", fmt.Sprintf("%s=%s", k, v))
+ }
+
+ if opts.Interactive {
+ args = append(args, "-i")
+ }
+
+ if opts.TTY {
+ args = append(args, "-t")
+ }
+
+ args = append(args, opts.Image)
+
+ if len(opts.Command) > 0 {
+ args = append(args, opts.Command...)
+ }
+
+ _, err := c.runContainerCommand(args...)
+ return err
+}
+
+func (c *ContainerClient) RunContainer(opts CreateContainerOptions) (string, error) {
+ args := []string{"run"}
+
+ if opts.Name != "" {
+ args = append(args, "--name", opts.Name)
+ }
+
+ if opts.Detach {
+ args = append(args, "-d")
+ }
+
+ for _, env := range opts.Env {
+ args = append(args, "-e", env)
+ }
+
+ if opts.WorkingDir != "" {
+ args = append(args, "-w", opts.WorkingDir)
+ }
+
+ if opts.CPUS > 0 {
+ args = append(args, "-c", fmt.Sprintf("%d", opts.CPUS))
+ }
+
+ if opts.Memory != "" {
+ args = append(args, "-m", opts.Memory)
+ }
+
+ for _, vol := range opts.Volumes {
+ args = append(args, "-v", vol)
+ }
+
+ for _, net := range opts.Networks {
+ args = append(args, "--network", net)
+ }
+
+ for _, port := range opts.Ports {
+ args = append(args, "-p", port)
+ }
+
+ for k, v := range opts.Labels {
+ args = append(args, "-l", fmt.Sprintf("%s=%s", k, v))
+ }
+
+ if opts.Interactive {
+ args = append(args, "-i")
+ }
+
+ if opts.TTY {
+ args = append(args, "-t")
+ }
+
+ args = append(args, opts.Image)
+
+ if len(opts.Command) > 0 {
+ args = append(args, opts.Command...)
+ }
+
+ output, err := c.runContainerCommand(args...)
+ return string(output), err
+}
+
+func (c *ContainerClient) StartContainer(id string) error {
+ _, err := c.runContainerCommand("start", id)
+ return err
+}
+
+func (c *ContainerClient) StopContainer(id string) error {
+ _, err := c.runContainerCommand("stop", id)
+ return err
+}
+
+func (c *ContainerClient) RestartContainer(id string) error {
+ _, err := c.runContainerCommand("stop", id)
+ if err != nil {
+ return err
+ }
+ return c.StartContainer(id)
+}
+
+func (c *ContainerClient) KillContainer(id string, signal string) error {
+ args := []string{"kill", id}
+ if signal != "" {
+ args = []string{"kill", "--signal", signal, id}
+ }
+ _, err := c.runContainerCommand(args...)
+ return err
+}
+
+func (c *ContainerClient) RemoveContainer(id string, force bool) error {
+ args := []string{"rm", id}
+ if force {
+ args = []string{"rm", "--force", id}
+ }
+ _, err := c.runContainerCommand(args...)
+ return err
+}
+
+func (c *ContainerClient) PruneContainers() error {
+ _, err := c.runContainerCommand("prune")
+ return err
+}
+
+func (c *ContainerClient) ExecContainer(id string, opts ExecOptions) *exec.Cmd {
+ args := []string{"exec"}
+
+ for _, env := range opts.Env {
+ args = append(args, "-e", env)
+ }
+
+ if opts.WorkingDir != "" {
+ args = append(args, "-w", opts.WorkingDir)
+ }
+
+ if opts.Interactive {
+ args = append(args, "-i")
+ }
+
+ if opts.TTY {
+ args = append(args, "-t")
+ }
+
+ if opts.User != "" {
+ args = append(args, "-u", opts.User)
+ }
+
+ args = append(args, id)
+ args = append(args, opts.Command...)
+
+ return exec.Command("container", args...)
+}
+
+func (c *ContainerClient) LogsContainer(id string, follow bool, tail int) *exec.Cmd {
+ args := []string{"logs"}
+
+ if follow {
+ args = append(args, "--follow")
+ }
+
+ if tail > 0 {
+ args = append(args, "-n", fmt.Sprintf("%d", tail))
+ }
+
+ args = append(args, id)
+
+ return exec.Command("container", args...)
+}
+
+func (c *ContainerClient) StatsContainer(id string, noStream bool) ([]AppleContainerStats, error) {
+ args := []string{"stats", "--format", "json"}
+
+ if noStream {
+ args = append(args, "--no-stream")
+ }
+
+ if id != "" {
+ args = append(args, id)
+ }
+
+ output, err := c.runContainerCommand(args...)
+ if err != nil {
+ return nil, err
+ }
+
+ if len(output) == 0 {
+ return []AppleContainerStats{}, nil
+ }
+
+ var stats []AppleContainerStats
+ if err := json.Unmarshal(output, &stats); err != nil {
+ return nil, fmt.Errorf("failed to parse container stats: %w", err)
+ }
+
+ return stats, nil
+}
+
+// Images
+
+func (c *ContainerClient) ListImages() ([]AppleImage, error) {
+ c.ImageMutex.Lock()
+ defer c.ImageMutex.Unlock()
+
+ output, err := c.runContainerCommand("image", "list", "--format", "json")
+ if err != nil {
+ return nil, err
+ }
+
+ if len(output) == 0 {
+ return []AppleImage{}, nil
+ }
+
+ var images []AppleImage
+ if err := json.Unmarshal(output, &images); err != nil {
+ return nil, fmt.Errorf("failed to parse image list: %w", err)
+ }
+
+ return images, nil
+}
+
+func (c *ContainerClient) PullImage(ref string) error {
+ _, err := c.runContainerCommand("image", "pull", ref)
+ return err
+}
+
+func (c *ContainerClient) RemoveImage(id string, force bool) error {
+ args := []string{"image", "rm", id}
+ if force {
+ args = []string{"image", "rm", "--force", id}
+ }
+ _, err := c.runContainerCommand(args...)
+ return err
+}
+
+func (c *ContainerClient) PruneImages(all bool) error {
+ args := []string{"image", "prune"}
+ if all {
+ args = []string{"image", "prune", "-a"}
+ }
+ _, err := c.runContainerCommand(args...)
+ return err
+}
+
+func (c *ContainerClient) BuildImage(contextDir string, dockerfile string, tag string) error {
+ args := []string{"build"}
+
+ if dockerfile != "" {
+ args = append(args, "-f", dockerfile)
+ }
+
+ if tag != "" {
+ args = append(args, "-t", tag)
+ }
+
+ if contextDir != "" {
+ args = append(args, contextDir)
+ }
+
+ _, err := c.runContainerCommand(args...)
+ return err
+}
+
+func (c *ContainerClient) SaveImage(ids []string, output string) error {
+ args := []string{"image", "save"}
+ args = append(args, ids...)
+ if output != "" {
+ args = append(args, "--output", output)
+ }
+ _, err := c.runContainerCommand(args...)
+ return err
+}
+
+func (c *ContainerClient) LoadImage(input string) error {
+ args := []string{"image", "load"}
+ if input != "" {
+ args = append(args, "--input", input)
+ }
+ _, err := c.runContainerCommand(args...)
+ return err
+}
+
+// Volumes
+
+func (c *ContainerClient) ListVolumes() ([]AppleVolume, error) {
+ c.VolumeMutex.Lock()
+ defer c.VolumeMutex.Unlock()
+
+ output, err := c.runContainerCommand("volume", "list", "--format", "json")
+ if err != nil {
+ return nil, err
+ }
+
+ if len(output) == 0 {
+ return []AppleVolume{}, nil
+ }
+
+ var volumes []AppleVolume
+ if err := json.Unmarshal(output, &volumes); err != nil {
+ return nil, fmt.Errorf("failed to parse volume list: %w", err)
+ }
+
+ return volumes, nil
+}
+
+func (c *ContainerClient) CreateVolume(name string) error {
+ args := []string{"volume", "create"}
+ if name != "" {
+ args = append(args, name)
+ }
+ _, err := c.runContainerCommand(args...)
+ return err
+}
+
+func (c *ContainerClient) RemoveVolume(name string) error {
+ _, err := c.runContainerCommand("volume", "rm", name)
+ return err
+}
+
+func (c *ContainerClient) PruneVolumes() error {
+ _, err := c.runContainerCommand("volume", "prune")
+ return err
+}
+
+// Networks
+
+func (c *ContainerClient) ListNetworks() ([]AppleNetwork, error) {
+ c.NetworkMutex.Lock()
+ defer c.NetworkMutex.Unlock()
+
+ output, err := c.runContainerCommand("network", "list", "--format", "json")
+ if err != nil {
+ return nil, err
+ }
+
+ if len(output) == 0 {
+ return []AppleNetwork{}, nil
+ }
+
+ var networks []AppleNetwork
+ if err := json.Unmarshal(output, &networks); err != nil {
+ return nil, fmt.Errorf("failed to parse network list: %w", err)
+ }
+
+ return networks, nil
+}
+
+func (c *ContainerClient) CreateNetwork(name string) error {
+ _, err := c.runContainerCommand("network", "create", name)
+ return err
+}
+
+func (c *ContainerClient) RemoveNetwork(name string) error {
+ _, err := c.runContainerCommand("network", "rm", name)
+ return err
+}
+
+func (c *ContainerClient) PruneNetworks() error {
+ _, err := c.runContainerCommand("network", "prune")
+ return err
+}
+
+// System
+
+func (c *ContainerClient) SystemDF() (*SystemDF, error) {
+ output, err := c.runContainerCommand("system", "df", "--format", "json")
+ if err != nil {
+ return nil, err
+ }
+
+ var df SystemDF
+ if err := json.Unmarshal(output, &df); err != nil {
+ return nil, fmt.Errorf("failed to parse system df: %w", err)
+ }
+
+ return &df, nil
+}
+
+func (c *ContainerClient) SystemStatus() (*SystemStatus, error) {
+ output, err := c.runContainerCommand("system", "status")
+ if err != nil {
+ return &SystemStatus{Running: false}, nil
+ }
+
+ status := &SystemStatus{
+ Running: strings.Contains(string(output), "running"),
+ }
+
+ return status, nil
+}
+
+func (c *ContainerClient) SystemStart() error {
+ _, err := c.runContainerCommand("system", "start")
+ return err
+}
+
+func (c *ContainerClient) SystemStop() error {
+ _, err := c.runContainerCommand("system", "stop")
+ return err
+}
+
+func (c *ContainerClient) SystemVersion() (string, error) {
+ return c.runContainerCommandWithOutput("--version")
+}
+
+// Monitor stats for a container
+func (c *ContainerClient) CreateStatsMonitor(container *Container, callback func(stats AppleContainerStats)) {
+ container.MonitoringStats = true
+
+ ticker := time.NewTicker(time.Second)
+ defer ticker.Stop()
+
+ for range ticker.C {
+ if !container.MonitoringStats {
+ return
+ }
+
+ stats, err := c.StatsContainer(container.ID, true)
+ if err != nil {
+ c.Log.Error(err)
+ continue
+ }
+
+ if len(stats) > 0 {
+ callback(stats[0])
+ }
+ }
+}
+
+var _ io.Closer = &ContainerClient{}
+
+func (c *ContainerClient) Close() error {
+ return nil
+}
+
+// CommandObject is what we pass to our template resolvers
+type CommandObject struct {
+ Container *Container
+ Image *Image
+ Volume *Volume
+ Network *Network
+}
+
+func (c *ContainerClient) NewCommandObject(obj CommandObject) CommandObject {
+ return obj
+}
+
+func (c *ContainerClient) RefreshContainersAndServices(currentContainers []*Container) ([]*Container, error) {
+ return c.RefreshContainers(currentContainers), nil
+}
+
+func (c *ContainerClient) RefreshContainers(currentContainers []*Container) []*Container {
+ c.ContainerMutex.Lock()
+ defer c.ContainerMutex.Unlock()
+
+ appleContainers, err := c.ListContainers(true)
+ if err != nil {
+ c.Log.Error(err)
+ return currentContainers
+ }
+
+ containers := make([]*Container, len(appleContainers))
+
+ for i, ac := range appleContainers {
+ var existingContainer *Container
+ for _, cc := range currentContainers {
+ if cc.ID == ac.Configuration.ID {
+ existingContainer = cc
+ break
+ }
+ }
+
+ if existingContainer != nil {
+ existingContainer.AppleContainer = ac
+ existingContainer.Name = ac.Configuration.ID
+ if ac.Configuration.ID != "" {
+ existingContainer.Name = ac.Configuration.ID
+ }
+ containers[i] = existingContainer
+ } else {
+ container := &Container{
+ ID: ac.Configuration.ID,
+ Name: ac.Configuration.ID,
+ AppleContainer: ac,
+ Client: c,
+ OSCommand: c.OSCommand,
+ Log: c.Log,
+ Tr: c.Tr,
+ }
+ containers[i] = container
+ }
+ }
+
+ return containers
+}
+
+func (c *ContainerClient) GetProjectNames(containers []*Container) []string {
+ return []string{}
+}
diff --git a/pkg/commands/container_cmd.go b/pkg/commands/container_cmd.go
new file mode 100644
index 00000000..b7ad9500
--- /dev/null
+++ b/pkg/commands/container_cmd.go
@@ -0,0 +1,167 @@
+package commands
+
+import (
+ "io"
+ ogLog "log"
+ "time"
+
+ "github.com/jesseduffield/lazycontainer/pkg/config"
+ "github.com/jesseduffield/lazycontainer/pkg/i18n"
+ "github.com/jesseduffield/lazycontainer/pkg/utils"
+ "github.com/sasha-s/go-deadlock"
+ "github.com/sirupsen/logrus"
+)
+
+type ContainerCommand struct {
+ Log *logrus.Entry
+ OSCommand *OSCommand
+ Tr *i18n.TranslationSet
+ Config *config.AppConfig
+ Client *ContainerClient
+ ErrorChan chan error
+ ContainerMutex deadlock.Mutex
+ ImageMutex deadlock.Mutex
+ VolumeMutex deadlock.Mutex
+ NetworkMutex deadlock.Mutex
+
+ Closers []io.Closer
+}
+
+var _ io.Closer = &ContainerCommand{}
+
+type LimitedContainerCommand interface {
+ NewCommandObject(CommandObject) CommandObject
+}
+
+func NewContainerCommand(log *logrus.Entry, osCommand *OSCommand, tr *i18n.TranslationSet, config *config.AppConfig, errorChan chan error) (*ContainerCommand, error) {
+ client, err := NewContainerClient(log, osCommand, tr, config, errorChan)
+ if err != nil {
+ ogLog.Fatal(err)
+ }
+
+ containerCommand := &ContainerCommand{
+ Log: log,
+ OSCommand: osCommand,
+ Tr: tr,
+ Config: config,
+ Client: client,
+ ErrorChan: errorChan,
+ Closers: []io.Closer{client},
+ }
+
+ return containerCommand, nil
+}
+
+func (c *ContainerCommand) Close() error {
+ return utils.CloseMany(c.Closers)
+}
+
+func (c *ContainerCommand) NewCommandObject(obj CommandObject) CommandObject {
+ return obj
+}
+
+func (c *ContainerCommand) CreateClientStatMonitor(container *Container) {
+ container.MonitoringStats = true
+
+ var prevStats *ContainerStats
+ lastTime := time.Now()
+
+ ticker := time.NewTicker(time.Second)
+ defer ticker.Stop()
+
+ for range ticker.C {
+ if !container.MonitoringStats {
+ return
+ }
+
+ now := time.Now()
+ timeDelta := now.Sub(lastTime)
+
+ stats, err := c.Client.StatsContainer(container.ID, true)
+ if err != nil {
+ c.Log.Error(err)
+ continue
+ }
+
+ if len(stats) > 0 {
+ containerStats := ConvertAppleStatsToContainerStats(stats[0], prevStats, timeDelta)
+
+ recordedStats := &RecordedStats{
+ ClientStats: *containerStats,
+ DerivedStats: DerivedStats{
+ CPUPercentage: containerStats.CalculateContainerCPUPercentage(),
+ MemoryPercentage: containerStats.CalculateContainerMemoryUsage(),
+ },
+ RecordedAt: now,
+ }
+
+ container.appendStats(recordedStats, c.Config.UserConfig.Stats.MaxDuration)
+
+ prevStats = containerStats
+ lastTime = now
+ }
+ }
+
+ container.MonitoringStats = false
+}
+
+func (c *ContainerCommand) RefreshContainersAndServices(currentContainers []*Container) ([]*Container, error) {
+ containers := c.RefreshContainers(currentContainers)
+ return containers, nil
+}
+
+func (c *ContainerCommand) RefreshContainers(currentContainers []*Container) []*Container {
+ return c.Client.RefreshContainers(currentContainers)
+}
+
+func (c *ContainerCommand) RefreshImages() ([]*Image, error) {
+ return c.Client.RefreshImages()
+}
+
+func (c *ContainerCommand) RefreshVolumes() ([]*Volume, error) {
+ return c.Client.RefreshVolumes()
+}
+
+func (c *ContainerCommand) RefreshNetworks() ([]*Network, error) {
+ return c.Client.RefreshNetworks()
+}
+
+func (c *ContainerCommand) PruneContainers() error {
+ return c.Client.PruneContainers()
+}
+
+func (c *ContainerCommand) PruneImages() error {
+ return c.Client.PruneImages(false)
+}
+
+func (c *ContainerCommand) PruneVolumes() error {
+ return c.Client.PruneVolumes()
+}
+
+func (c *ContainerCommand) PruneNetworks() error {
+ return c.Client.PruneNetworks()
+}
+
+func (c *ContainerCommand) SystemDF() (*SystemDF, error) {
+ return c.Client.SystemDF()
+}
+
+func (c *ContainerCommand) SystemStatus() (*SystemStatus, error) {
+ return c.Client.SystemStatus()
+}
+
+func (c *ContainerCommand) SystemStart() error {
+ return c.Client.SystemStart()
+}
+
+func (c *ContainerCommand) SystemStop() error {
+ return c.Client.SystemStop()
+}
+
+func (c *ContainerCommand) BuildImage(contextDir string, dockerfile string, tag string) error {
+ return c.Client.BuildImage(contextDir, dockerfile, tag)
+}
+
+func (c *ContainerCommand) GetProjectNames(containers []*Container) []string {
+ return []string{}
+}
diff --git a/pkg/commands/container_stats.go b/pkg/commands/container_stats.go
index 419782dd..ee72f62d 100644
--- a/pkg/commands/container_stats.go
+++ b/pkg/commands/container_stats.go
@@ -5,151 +5,53 @@ import (
"time"
)
-// RecordedStats contains both the container stats we've received from docker, and our own derived stats from those container stats. When configuring a graph, you're basically specifying the path of a value in this struct
type RecordedStats struct {
ClientStats ContainerStats
DerivedStats DerivedStats
RecordedAt time.Time
}
-// DerivedStats contains some useful stats that we've calculated based on the raw container stats that we got back from docker
type DerivedStats struct {
CPUPercentage float64
MemoryPercentage float64
}
-// ContainerStats autogenerated at https://mholt.github.io/json-to-go/
type ContainerStats struct {
- Read time.Time `json:"read"`
- Preread time.Time `json:"preread"`
- PidsStats struct {
- Current int `json:"current"`
- } `json:"pids_stats"`
- BlkioStats struct {
- IoServiceBytesRecursive []struct {
- Major int `json:"major"`
- Minor int `json:"minor"`
- Op string `json:"op"`
- Value int `json:"value"`
- } `json:"io_service_bytes_recursive"`
- IoServicedRecursive []struct {
- Major int `json:"major"`
- Minor int `json:"minor"`
- Op string `json:"op"`
- Value int `json:"value"`
- } `json:"io_serviced_recursive"`
- IoQueueRecursive []interface{} `json:"io_queue_recursive"`
- IoServiceTimeRecursive []interface{} `json:"io_service_time_recursive"`
- IoWaitTimeRecursive []interface{} `json:"io_wait_time_recursive"`
- IoMergedRecursive []interface{} `json:"io_merged_recursive"`
- IoTimeRecursive []interface{} `json:"io_time_recursive"`
- SectorsRecursive []interface{} `json:"sectors_recursive"`
- } `json:"blkio_stats"`
- NumProcs int `json:"num_procs"`
- StorageStats struct{} `json:"storage_stats"`
- CPUStats struct {
- CPUUsage struct {
- TotalUsage int64 `json:"total_usage"`
- PercpuUsage []int64 `json:"percpu_usage"`
- UsageInKernelmode int64 `json:"usage_in_kernelmode"`
- UsageInUsermode int64 `json:"usage_in_usermode"`
- } `json:"cpu_usage"`
- SystemCPUUsage int64 `json:"system_cpu_usage"`
- OnlineCpus int `json:"online_cpus"`
- ThrottlingData struct {
- Periods int `json:"periods"`
- ThrottledPeriods int `json:"throttled_periods"`
- ThrottledTime int `json:"throttled_time"`
- } `json:"throttling_data"`
- } `json:"cpu_stats"`
- PrecpuStats struct {
- CPUUsage struct {
- TotalUsage int64 `json:"total_usage"`
- PercpuUsage []int64 `json:"percpu_usage"`
- UsageInKernelmode int64 `json:"usage_in_kernelmode"`
- UsageInUsermode int64 `json:"usage_in_usermode"`
- } `json:"cpu_usage"`
- SystemCPUUsage int64 `json:"system_cpu_usage"`
- OnlineCpus int `json:"online_cpus"`
- ThrottlingData struct {
- Periods int `json:"periods"`
- ThrottledPeriods int `json:"throttled_periods"`
- ThrottledTime int `json:"throttled_time"`
- } `json:"throttling_data"`
- } `json:"precpu_stats"`
- MemoryStats struct {
- Usage int `json:"usage"`
- MaxUsage int `json:"max_usage"`
- Stats struct {
- ActiveAnon int `json:"active_anon"`
- ActiveFile int `json:"active_file"`
- Cache int `json:"cache"`
- Dirty int `json:"dirty"`
- HierarchicalMemoryLimit int64 `json:"hierarchical_memory_limit"`
- HierarchicalMemswLimit int64 `json:"hierarchical_memsw_limit"`
- InactiveAnon int `json:"inactive_anon"`
- InactiveFile int `json:"inactive_file"`
- MappedFile int `json:"mapped_file"`
- Pgfault int `json:"pgfault"`
- Pgmajfault int `json:"pgmajfault"`
- Pgpgin int `json:"pgpgin"`
- Pgpgout int `json:"pgpgout"`
- Rss int `json:"rss"`
- RssHuge int `json:"rss_huge"`
- TotalActiveAnon int `json:"total_active_anon"`
- TotalActiveFile int `json:"total_active_file"`
- TotalCache int `json:"total_cache"`
- TotalDirty int `json:"total_dirty"`
- TotalInactiveAnon int `json:"total_inactive_anon"`
- TotalInactiveFile int `json:"total_inactive_file"`
- TotalMappedFile int `json:"total_mapped_file"`
- TotalPgfault int `json:"total_pgfault"`
- TotalPgmajfault int `json:"total_pgmajfault"`
- TotalPgpgin int `json:"total_pgpgin"`
- TotalPgpgout int `json:"total_pgpgout"`
- TotalRss int `json:"total_rss"`
- TotalRssHuge int `json:"total_rss_huge"`
- TotalUnevictable int `json:"total_unevictable"`
- TotalWriteback int `json:"total_writeback"`
- Unevictable int `json:"unevictable"`
- Writeback int `json:"writeback"`
- } `json:"stats"`
- Limit int64 `json:"limit"`
- } `json:"memory_stats"`
- Name string `json:"name"`
- ID string `json:"id"`
- Networks struct {
- Eth0 struct {
- RxBytes int `json:"rx_bytes"`
- RxPackets int `json:"rx_packets"`
- RxErrors int `json:"rx_errors"`
- RxDropped int `json:"rx_dropped"`
- TxBytes int `json:"tx_bytes"`
- TxPackets int `json:"tx_packets"`
- TxErrors int `json:"tx_errors"`
- TxDropped int `json:"tx_dropped"`
- } `json:"eth0"`
- } `json:"networks"`
+ ID string `json:"id"`
+ CPUUsageUsec int64 `json:"cpuUsageUsec"`
+ MemoryUsageBytes int64 `json:"memoryUsageBytes"`
+ MemoryLimitBytes int64 `json:"memoryLimitBytes"`
+ NetworkRxBytes int64 `json:"networkRxBytes"`
+ NetworkTxBytes int64 `json:"networkTxBytes"`
+ BlockReadBytes int64 `json:"blockReadBytes"`
+ BlockWriteBytes int64 `json:"blockWriteBytes"`
+ NumProcesses int `json:"numProcesses"`
+
+ PrevCPUUsageUsec int64 `json:"-"`
+ TimeDeltaUsec int64 `json:"-"`
}
-// CalculateContainerCPUPercentage calculates the cpu usage of the container as a percent of total CPU usage
-// to calculate CPU usage, we take the increase in CPU time from the container since the last poll, divide that by the total increase in CPU time since the last poll, times by the number of cores, and times by 100 to get a percentage
-// I'm not entirely sure why we need to multiply by the number of cores, but the numbers work
func (s *ContainerStats) CalculateContainerCPUPercentage() float64 {
- cpuUsageDelta := s.CPUStats.CPUUsage.TotalUsage - s.PrecpuStats.CPUUsage.TotalUsage
- cpuTotalUsageDelta := s.CPUStats.SystemCPUUsage - s.PrecpuStats.SystemCPUUsage
-
- value := float64(cpuUsageDelta*100) / float64(cpuTotalUsageDelta)
- if math.IsNaN(value) {
+ if s.TimeDeltaUsec == 0 {
+ return 0
+ }
+ cpuDelta := s.CPUUsageUsec - s.PrevCPUUsageUsec
+ if cpuDelta < 0 {
+ cpuDelta = s.CPUUsageUsec
+ }
+ value := float64(cpuDelta*1000000) / float64(s.TimeDeltaUsec)
+ if math.IsNaN(value) || math.IsInf(value, 0) {
return 0
}
return value
}
-// CalculateContainerMemoryUsage calculates the memory usage of the container as a percent of total available memory
func (s *ContainerStats) CalculateContainerMemoryUsage() float64 {
- value := float64(s.MemoryStats.Usage*100) / float64(s.MemoryStats.Limit)
- if math.IsNaN(value) {
+ if s.MemoryLimitBytes == 0 {
+ return 0
+ }
+ value := float64(s.MemoryUsageBytes*100) / float64(s.MemoryLimitBytes)
+ if math.IsNaN(value) || math.IsInf(value, 0) {
return 0
}
return value
@@ -163,7 +65,6 @@ func (c *Container) appendStats(stats *RecordedStats, maxDuration time.Duration)
c.eraseOldHistory(maxDuration)
}
-// eraseOldHistory removes any history before the user-specified max duration
func (c *Container) eraseOldHistory(maxDuration time.Duration) {
if maxDuration == 0 {
return
@@ -186,3 +87,24 @@ func (c *Container) GetLastStats() (*RecordedStats, bool) {
}
return history[len(history)-1], true
}
+
+func ConvertAppleStatsToContainerStats(appleStats AppleContainerStats, prevStats *ContainerStats, timeDelta time.Duration) *ContainerStats {
+ stats := &ContainerStats{
+ ID: appleStats.ID,
+ CPUUsageUsec: appleStats.CPUUsageUsec,
+ MemoryUsageBytes: appleStats.MemoryUsageBytes,
+ MemoryLimitBytes: appleStats.MemoryLimitBytes,
+ NetworkRxBytes: appleStats.NetworkRxBytes,
+ NetworkTxBytes: appleStats.NetworkTxBytes,
+ BlockReadBytes: appleStats.BlockReadBytes,
+ BlockWriteBytes: appleStats.BlockWriteBytes,
+ NumProcesses: appleStats.NumProcesses,
+ }
+
+ if prevStats != nil {
+ stats.PrevCPUUsageUsec = prevStats.CPUUsageUsec
+ stats.TimeDeltaUsec = int64(timeDelta.Microseconds())
+ }
+
+ return stats
+}
diff --git a/pkg/commands/container_stats_test.go b/pkg/commands/container_stats_test.go
deleted file mode 100644
index 5aaabd47..00000000
--- a/pkg/commands/container_stats_test.go
+++ /dev/null
@@ -1,17 +0,0 @@
-package commands
-
-import (
- "testing"
-
- "github.com/stretchr/testify/assert"
-)
-
-func TestCalculateContainerCPUPercentage(t *testing.T) {
- container := &ContainerStats{}
- container.CPUStats.CPUUsage.TotalUsage = 10
- container.CPUStats.SystemCPUUsage = 10
- container.PrecpuStats.CPUUsage.TotalUsage = 5
- container.PrecpuStats.SystemCPUUsage = 2
-
- assert.EqualValues(t, 62.5, container.CalculateContainerCPUPercentage())
-}
diff --git a/pkg/commands/docker.go b/pkg/commands/docker.go
deleted file mode 100644
index 0ce4fba1..00000000
--- a/pkg/commands/docker.go
+++ /dev/null
@@ -1,569 +0,0 @@
-package commands
-
-import (
- "bufio"
- "context"
- "encoding/json"
- "fmt"
- "io"
- ogLog "log"
- "os"
- "os/exec"
- "path/filepath"
- "sort"
- "strings"
- "sync"
- "time"
-
- cliconfig "github.com/docker/cli/cli/config"
- ddocker "github.com/docker/cli/cli/context/docker"
- ctxstore "github.com/docker/cli/cli/context/store"
- "github.com/docker/docker/api/types/container"
- "github.com/docker/docker/client"
- "github.com/imdario/mergo"
- "github.com/jesseduffield/lazydocker/pkg/commands/ssh"
- "github.com/jesseduffield/lazydocker/pkg/config"
- "github.com/jesseduffield/lazydocker/pkg/i18n"
- "github.com/jesseduffield/lazydocker/pkg/utils"
- "github.com/sasha-s/go-deadlock"
- "github.com/sirupsen/logrus"
-)
-
-const (
- dockerHostEnvKey = "DOCKER_HOST"
-)
-
-// DockerCommand is our main docker interface
-type DockerCommand struct {
- Log *logrus.Entry
- OSCommand *OSCommand
- Tr *i18n.TranslationSet
- Config *config.AppConfig
- Client *client.Client
- InDockerComposeProject bool
- // LocalProjectName is the compose project name for the directory where lazydocker was launched.
- LocalProjectName string
- ErrorChan chan error
- ContainerMutex deadlock.Mutex
- ServiceMutex deadlock.Mutex
-
- Closers []io.Closer
-}
-
-var _ io.Closer = &DockerCommand{}
-
-// LimitedDockerCommand is a stripped-down DockerCommand with just the methods the container/service/image might need
-type LimitedDockerCommand interface {
- NewCommandObject(CommandObject) CommandObject
-}
-
-// CommandObject is what we pass to our template resolvers when we are running a custom command. We do not guarantee that all fields will be populated: just the ones that make sense for the current context
-type CommandObject struct {
- DockerCompose string
- Service *Service
- Container *Container
- Image *Image
- Volume *Volume
- Network *Network
- Project *Project
-}
-
-// NewCommandObject takes a command object and returns a default command object with the passed command object merged in
-func (c *DockerCommand) NewCommandObject(obj CommandObject) CommandObject {
- defaultObj := CommandObject{DockerCompose: c.Config.UserConfig.CommandTemplates.DockerCompose}
- _ = mergo.Merge(&defaultObj, obj)
-
- // When operating on a specific project, include -p flag so that
- // docker compose targets the correct project.
- if obj.Service != nil && obj.Service.ProjectName != "" {
- defaultObj.DockerCompose = fmt.Sprintf("%s -p %s", defaultObj.DockerCompose, obj.Service.ProjectName)
- } else if obj.Project != nil && obj.Project.Name != "" {
- defaultObj.DockerCompose = fmt.Sprintf("%s -p %s", defaultObj.DockerCompose, obj.Project.Name)
- }
-
- return defaultObj
-}
-
-// newDockerClient creates a Docker client with the given host.
-// We avoid using client.FromEnv because it includes WithVersionFromEnv() which
-// sets manualOverride=true when DOCKER_API_VERSION is set, preventing API version
-// negotiation even when WithAPIVersionNegotiation() is specified.
-// Instead, we explicitly configure only what we need, and rely on proper
-// API version negotiation to support older Docker daemons.
-// See https://github.com/jesseduffield/lazydocker/issues/715
-func newDockerClient(dockerHost string) (*client.Client, error) {
- return client.NewClientWithOpts(
- client.WithTLSClientConfigFromEnv(),
- client.WithAPIVersionNegotiation(),
- client.WithHost(dockerHost),
- )
-}
-
-// NewDockerCommand it runs docker commands
-func NewDockerCommand(log *logrus.Entry, osCommand *OSCommand, tr *i18n.TranslationSet, config *config.AppConfig, errorChan chan error) (*DockerCommand, error) {
- dockerHost, err := determineDockerHost()
- if err != nil {
- ogLog.Printf("> could not determine host %v", err)
- }
-
- // NOTE: Inject the determined docker host to the environment. This allows the
- // `SSHHandler.HandleSSHDockerHost()` to create a local unix socket tunneled
- // over SSH to the specified ssh host.
- if strings.HasPrefix(dockerHost, "ssh://") {
- os.Setenv(dockerHostEnvKey, dockerHost)
- }
-
- tunnelCloser, err := ssh.NewSSHHandler(osCommand).HandleSSHDockerHost()
- if err != nil {
- ogLog.Fatal(err)
- }
-
- // Retrieve the docker host from the environment which could have been set by
- // the `SSHHandler.HandleSSHDockerHost()` and override `dockerHost`.
- dockerHostFromEnv := os.Getenv(dockerHostEnvKey)
- if dockerHostFromEnv != "" {
- dockerHost = dockerHostFromEnv
- }
-
- cli, err := newDockerClient(dockerHost)
- if err != nil {
- ogLog.Fatal(err)
- }
-
- dockerCommand := &DockerCommand{
- Log: log,
- OSCommand: osCommand,
- Tr: tr,
- Config: config,
- Client: cli,
- ErrorChan: errorChan,
- InDockerComposeProject: true,
- Closers: []io.Closer{tunnelCloser},
- }
-
- dockerCommand.setDockerComposeCommand(config)
-
- err = osCommand.RunCommand(
- utils.ApplyTemplate(
- config.UserConfig.CommandTemplates.CheckDockerComposeConfig,
- dockerCommand.NewCommandObject(CommandObject{}),
- ),
- )
- if err != nil {
- dockerCommand.InDockerComposeProject = false
- log.Warn(err.Error())
- }
-
- return dockerCommand, nil
-}
-
-func (c *DockerCommand) setDockerComposeCommand(config *config.AppConfig) {
- if config.UserConfig.CommandTemplates.DockerCompose != "docker compose" {
- return
- }
-
- // it's possible that a user is still using docker-compose, so we'll check if 'docker comopose' is available, and if not, we'll fall back to 'docker-compose'
- err := c.OSCommand.RunCommand("docker compose version")
- if err != nil {
- config.UserConfig.CommandTemplates.DockerCompose = "docker-compose"
- }
-}
-
-func (c *DockerCommand) Close() error {
- return utils.CloseMany(c.Closers)
-}
-
-func (c *DockerCommand) CreateClientStatMonitor(container *Container) {
- container.MonitoringStats = true
- stream, err := c.Client.ContainerStats(context.Background(), container.ID, true)
- if err != nil {
- // not creating error panel because if we've disconnected from docker we'll
- // have already created an error panel
- c.Log.Error(err)
- container.MonitoringStats = false
- return
- }
-
- defer stream.Body.Close()
-
- scanner := bufio.NewScanner(stream.Body)
- for scanner.Scan() {
- data := scanner.Bytes()
- var stats ContainerStats
- _ = json.Unmarshal(data, &stats)
-
- recordedStats := &RecordedStats{
- ClientStats: stats,
- DerivedStats: DerivedStats{
- CPUPercentage: stats.CalculateContainerCPUPercentage(),
- MemoryPercentage: stats.CalculateContainerMemoryUsage(),
- },
- RecordedAt: time.Now(),
- }
-
- container.appendStats(recordedStats, c.Config.UserConfig.Stats.MaxDuration)
- }
-
- container.MonitoringStats = false
-}
-
-func (c *DockerCommand) RefreshContainersAndServices(currentContainers []*Container) ([]*Container, []*Service, error) {
- c.ServiceMutex.Lock()
- defer c.ServiceMutex.Unlock()
-
- containers, err := c.GetContainers(currentContainers)
- if err != nil {
- return nil, nil, err
- }
-
- // Derive services from container labels (covers all projects)
- services := c.GetServicesFromContainers(containers)
-
- var composeServices []*Service
- if c.InDockerComposeProject {
- composeServices, err = c.GetServices()
- if err != nil {
- c.Log.Warn("Failed to get compose services: " + err.Error())
- }
- }
-
- // Determine the local project name before merging services, since
- // mergeServices needs it. We match compose service names against container
- // labels to handle cases where the project name differs from the directory
- // name (e.g. a `name:` directive in the compose file).
- if c.LocalProjectName == "" && c.InDockerComposeProject && composeServices != nil {
- for _, ctr := range containers {
- if ctr.ProjectName == "" || ctr.ServiceName == "" {
- continue
- }
- for _, svc := range composeServices {
- if ctr.ServiceName == svc.Name {
- c.LocalProjectName = ctr.ProjectName
- break
- }
- }
- if c.LocalProjectName != "" {
- break
- }
- }
- // Fall back to directory name
- if c.LocalProjectName == "" && c.Config.ProjectDir != "" {
- c.LocalProjectName = filepath.Base(c.Config.ProjectDir)
- }
- }
-
- // Merge compose services (which include stopped services) with
- // container-derived services from all projects
- if composeServices != nil {
- services = c.mergeServices(services, composeServices)
- }
-
- c.assignContainersToServices(containers, services)
-
- return containers, services, nil
-}
-
-// GetServicesFromContainers derives services from container labels for all projects
-func (c *DockerCommand) GetServicesFromContainers(containers []*Container) []*Service {
- // Use project+service as key to avoid duplicates
- type serviceKey struct {
- project string
- service string
- }
- seen := make(map[serviceKey]bool)
- services := make([]*Service, 0, len(containers))
-
- for _, ctr := range containers {
- if ctr.ServiceName == "" || ctr.OneOff {
- continue
- }
- key := serviceKey{project: ctr.ProjectName, service: ctr.ServiceName}
- if seen[key] {
- continue
- }
- seen[key] = true
- services = append(services, &Service{
- Name: ctr.ServiceName,
- ID: ctr.ProjectName + "-" + ctr.ServiceName,
- ProjectName: ctr.ProjectName,
- OSCommand: c.OSCommand,
- Log: c.Log,
- DockerCommand: c,
- })
- }
-
- return services
-}
-
-// mergeServices merges compose services (which may lack ProjectName) with
-// container-derived services. Compose services take priority because they
-// include services without running containers.
-func (c *DockerCommand) mergeServices(containerServices []*Service, composeServices []*Service) []*Service {
- // Set project name on compose services
- for _, svc := range composeServices {
- if svc.ProjectName == "" {
- svc.ProjectName = c.LocalProjectName
- }
- }
-
- // Build a set of compose service names for the local project
- composeServiceNames := make(map[string]bool)
- for _, svc := range composeServices {
- composeServiceNames[svc.Name] = true
- }
-
- // Start with compose services, then add container-derived services
- // that aren't already covered by compose (i.e. from other projects)
- result := make([]*Service, 0, len(composeServices)+len(containerServices))
- result = append(result, composeServices...)
-
- for _, svc := range containerServices {
- if svc.ProjectName == c.LocalProjectName && composeServiceNames[svc.Name] {
- continue // already covered by compose service
- }
- result = append(result, svc)
- }
-
- return result
-}
-
-// GetProjectNames returns all unique project names from containers
-func (c *DockerCommand) GetProjectNames(containers []*Container) []string {
- seen := make(map[string]bool)
- var names []string
- for _, ctr := range containers {
- if ctr.ProjectName != "" && !seen[ctr.ProjectName] {
- seen[ctr.ProjectName] = true
- names = append(names, ctr.ProjectName)
- }
- }
- sort.Strings(names)
- return names
-}
-
-func (c *DockerCommand) assignContainersToServices(containers []*Container, services []*Service) {
-L:
- for _, service := range services {
- for _, ctr := range containers {
- if !ctr.OneOff && ctr.ServiceName == service.Name && ctr.ProjectName == service.ProjectName {
- service.Container = ctr
- continue L
- }
- }
- service.Container = nil
- }
-}
-
-// GetContainers gets the docker containers
-func (c *DockerCommand) GetContainers(existingContainers []*Container) ([]*Container, error) {
- c.ContainerMutex.Lock()
- defer c.ContainerMutex.Unlock()
-
- containers, err := c.Client.ContainerList(context.Background(), container.ListOptions{All: true})
- if err != nil {
- return nil, err
- }
-
- ownContainers := make([]*Container, len(containers))
-
- for i, ctr := range containers {
- var newContainer *Container
-
- // check if we already have data stored against the container
- for _, existingContainer := range existingContainers {
- if existingContainer.ID == ctr.ID {
- newContainer = existingContainer
- break
- }
- }
-
- // initialise the container if it's completely new
- if newContainer == nil {
- newContainer = &Container{
- ID: ctr.ID,
- Client: c.Client,
- OSCommand: c.OSCommand,
- Log: c.Log,
- DockerCommand: c,
- Tr: c.Tr,
- }
- }
-
- newContainer.Container = ctr
- // if the container is made with a name label we will use that
- if name, ok := ctr.Labels["name"]; ok {
- newContainer.Name = name
- } else {
- if len(ctr.Names) > 0 {
- newContainer.Name = strings.TrimLeft(ctr.Names[0], "/")
- } else {
- newContainer.Name = ctr.ID
- }
- }
- newContainer.ServiceName = ctr.Labels["com.docker.compose.service"]
- newContainer.ProjectName = ctr.Labels["com.docker.compose.project"]
- newContainer.ContainerNumber = ctr.Labels["com.docker.compose.container"]
- newContainer.OneOff = ctr.Labels["com.docker.compose.oneoff"] == "True"
-
- ownContainers[i] = newContainer
- }
-
- c.SetContainerDetails(ownContainers)
-
- return ownContainers, nil
-}
-
-// GetServices gets services
-func (c *DockerCommand) GetServices() ([]*Service, error) {
- if !c.InDockerComposeProject {
- return nil, nil
- }
-
- composeCommand := c.Config.UserConfig.CommandTemplates.DockerCompose
- output, err := c.OSCommand.RunCommandWithOutput(fmt.Sprintf("%s config --services", composeCommand))
- if err != nil {
- return nil, err
- }
-
- // output looks like:
- // service1
- // service2
-
- lines := utils.SplitLines(output)
- services := make([]*Service, len(lines))
- for i, str := range lines {
- services[i] = &Service{
- Name: str,
- ID: c.LocalProjectName + "-" + str,
- ProjectName: c.LocalProjectName,
- OSCommand: c.OSCommand,
- Log: c.Log,
- DockerCommand: c,
- }
- }
-
- return services, nil
-}
-
-func (c *DockerCommand) RefreshContainerDetails(containers []*Container) error {
- c.ContainerMutex.Lock()
- defer c.ContainerMutex.Unlock()
-
- c.SetContainerDetails(containers)
-
- return nil
-}
-
-// Attaches the details returned from docker inspect to each of the containers
-// this contains a bit more info than what you get from the go-docker client
-func (c *DockerCommand) SetContainerDetails(containers []*Container) {
- wg := sync.WaitGroup{}
- for _, ctr := range containers {
- ctr := ctr
- wg.Add(1)
- go func() {
- details, err := c.Client.ContainerInspect(context.Background(), ctr.ID)
- if err != nil {
- c.Log.Error(err)
- } else {
- ctr.Details = details
- }
- wg.Done()
- }()
- }
- wg.Wait()
-}
-
-// ViewAllLogs attaches to a subprocess viewing all the logs from docker-compose
-func (c *DockerCommand) ViewAllLogs(project *Project) (*exec.Cmd, error) {
- cmd := c.OSCommand.ExecutableFromString(
- utils.ApplyTemplate(
- c.OSCommand.Config.UserConfig.CommandTemplates.ViewAllLogs,
- c.NewCommandObject(CommandObject{Project: project}),
- ),
- )
-
- c.OSCommand.PrepareForChildren(cmd)
-
- return cmd, nil
-}
-
-// DockerComposeConfig returns the result of 'docker-compose config'
-func (c *DockerCommand) DockerComposeConfig() string {
- return c.DockerComposeConfigForProject(nil)
-}
-
-// DockerComposeConfigForProject returns the result of 'docker-compose config' for a specific project
-func (c *DockerCommand) DockerComposeConfigForProject(project *Project) string {
- output, err := c.OSCommand.RunCommandWithOutput(
- utils.ApplyTemplate(
- c.OSCommand.Config.UserConfig.CommandTemplates.DockerComposeConfig,
- c.NewCommandObject(CommandObject{Project: project}),
- ),
- )
- if err != nil {
- output = err.Error()
- }
- return output
-}
-
-// determineDockerHost tries to the determine the docker host that we should connect to
-// in the following order of decreasing precedence:
-// - value of "DOCKER_HOST" environment variable
-// - host retrieved from the current context (specified via DOCKER_CONTEXT)
-// - "default docker host" for the host operating system, otherwise
-func determineDockerHost() (string, error) {
- // If the docker host is explicitly set via the "DOCKER_HOST" environment variable,
- // then its a no-brainer :shrug:
- if os.Getenv("DOCKER_HOST") != "" {
- return os.Getenv("DOCKER_HOST"), nil
- }
-
- currentContext := os.Getenv("DOCKER_CONTEXT")
- if currentContext == "" {
- cf, err := cliconfig.Load(cliconfig.Dir())
- if err != nil {
- return "", err
- }
- currentContext = cf.CurrentContext
- }
-
- // On some systems (windows) `default` is stored in the docker config as the currentContext.
- if currentContext == "" || currentContext == "default" {
- // If a docker context is neither specified via the "DOCKER_CONTEXT" environment variable nor via the
- // $HOME/.docker/config file, then we fall back to connecting to the "default docker host" meant for
- // the host operating system.
- return defaultDockerHost, nil
- }
-
- storeConfig := ctxstore.NewConfig(
- func() interface{} { return &ddocker.EndpointMeta{} },
- ctxstore.EndpointTypeGetter(ddocker.DockerEndpoint, func() interface{} { return &ddocker.EndpointMeta{} }),
- )
-
- st := ctxstore.New(cliconfig.ContextStoreDir(), storeConfig)
- md, err := st.GetMetadata(currentContext)
- if err != nil {
- return "", err
- }
- dockerEP, ok := md.Endpoints[ddocker.DockerEndpoint]
- if !ok {
- return "", err
- }
- dockerEPMeta, ok := dockerEP.(ddocker.EndpointMeta)
- if !ok {
- return "", fmt.Errorf("expected docker.EndpointMeta, got %T", dockerEP)
- }
-
- if dockerEPMeta.Host != "" {
- return dockerEPMeta.Host, nil
- }
-
- // We might end up here, if the context was created with the `host` set to an empty value (i.e. '').
- // For example:
- // ```sh
- // docker context create foo --docker "host="
- // ```
- // In such scenario, we mimic the `docker` cli and try to connect to the "default docker host".
- return defaultDockerHost, nil
-}
diff --git a/pkg/commands/docker_host_unix.go b/pkg/commands/docker_host_unix.go
deleted file mode 100644
index ee5fb7af..00000000
--- a/pkg/commands/docker_host_unix.go
+++ /dev/null
@@ -1,7 +0,0 @@
-//go:build !windows
-
-package commands
-
-const (
- defaultDockerHost = "unix:///var/run/docker.sock"
-)
diff --git a/pkg/commands/docker_host_windows.go b/pkg/commands/docker_host_windows.go
deleted file mode 100644
index 471b55a2..00000000
--- a/pkg/commands/docker_host_windows.go
+++ /dev/null
@@ -1,5 +0,0 @@
-package commands
-
-const (
- defaultDockerHost = "npipe:////./pipe/docker_engine"
-)
diff --git a/pkg/commands/docker_test.go b/pkg/commands/docker_test.go
deleted file mode 100644
index 0d459686..00000000
--- a/pkg/commands/docker_test.go
+++ /dev/null
@@ -1,63 +0,0 @@
-package commands
-
-import (
- "os"
- "testing"
-
- "github.com/docker/docker/client"
- "github.com/stretchr/testify/assert"
-)
-
-// TestNewDockerClientVersionNegotiation verifies that newDockerClient allows
-// API version negotiation even when DOCKER_API_VERSION is set.
-//
-// This is a regression test for https://github.com/jesseduffield/lazydocker/issues/715
-// where users got "client version 1.25 is too old" errors because FromEnv()
-// includes WithVersionFromEnv() which sets manualOverride=true, preventing
-// API version negotiation.
-func TestNewDockerClientVersionNegotiation(t *testing.T) {
- // Save original env var and restore after test
- originalAPIVersion := os.Getenv("DOCKER_API_VERSION")
- defer func() {
- if originalAPIVersion == "" {
- os.Unsetenv("DOCKER_API_VERSION")
- } else {
- os.Setenv("DOCKER_API_VERSION", originalAPIVersion)
- }
- }()
-
- // Set DOCKER_API_VERSION to an old version that would cause
- // "client version 1.25 is too old" errors if negotiation is disabled
- os.Setenv("DOCKER_API_VERSION", "1.25")
-
- t.Run("FromEnv locks version preventing negotiation", func(t *testing.T) {
- // This demonstrates the problematic behavior we're avoiding.
- // When using FromEnv with DOCKER_API_VERSION set, the client
- // version gets locked to 1.25 and negotiation is disabled.
- cli, err := client.NewClientWithOpts(
- client.FromEnv,
- client.WithAPIVersionNegotiation(),
- )
- assert.NoError(t, err)
- defer cli.Close()
-
- // Version is locked to the env var value
- assert.Equal(t, "1.25", cli.ClientVersion())
- })
-
- t.Run("newDockerClient allows version negotiation", func(t *testing.T) {
- // Test the actual production function.
- // Use DefaultDockerHost for cross-platform compatibility
- // (unix socket on Linux/macOS, named pipe on Windows).
- cli, err := newDockerClient(client.DefaultDockerHost)
- assert.NoError(t, err)
- defer cli.Close()
-
- // Version is NOT locked to the env var value (1.25).
- // Instead, it uses the library's default version and will negotiate
- // with the server on first request. This is the key difference that
- // fixes the "version too old" error.
- assert.NotEqual(t, "1.25", cli.ClientVersion(),
- "client version should not be locked to DOCKER_API_VERSION env var")
- })
-}
diff --git a/pkg/commands/dummies.go b/pkg/commands/dummies.go
deleted file mode 100644
index 552171ac..00000000
--- a/pkg/commands/dummies.go
+++ /dev/null
@@ -1,52 +0,0 @@
-package commands
-
-import (
- "io"
-
- "github.com/jesseduffield/lazydocker/pkg/config"
- "github.com/jesseduffield/lazydocker/pkg/i18n"
- "github.com/sirupsen/logrus"
-)
-
-// This file exports dummy constructors for use by tests in other packages
-
-// NewDummyOSCommand creates a new dummy OSCommand for testing
-func NewDummyOSCommand() *OSCommand {
- return NewOSCommand(NewDummyLog(), NewDummyAppConfig())
-}
-
-// NewDummyAppConfig creates a new dummy AppConfig for testing
-func NewDummyAppConfig() *config.AppConfig {
- appConfig := &config.AppConfig{
- Name: "lazydocker",
- Version: "unversioned",
- Commit: "",
- BuildDate: "",
- Debug: false,
- BuildSource: "",
- }
- return appConfig
-}
-
-// NewDummyLog creates a new dummy Log for testing
-func NewDummyLog() *logrus.Entry {
- log := logrus.New()
- log.Out = io.Discard
- return log.WithField("test", "test")
-}
-
-// NewDummyDockerCommand creates a new dummy DockerCommand for testing
-func NewDummyDockerCommand() *DockerCommand {
- return NewDummyDockerCommandWithOSCommand(NewDummyOSCommand())
-}
-
-// NewDummyDockerCommandWithOSCommand creates a new dummy DockerCommand for testing
-func NewDummyDockerCommandWithOSCommand(osCommand *OSCommand) *DockerCommand {
- newAppConfig := NewDummyAppConfig()
- return &DockerCommand{
- Log: NewDummyLog(),
- OSCommand: osCommand,
- Tr: i18n.NewTranslationSet(NewDummyLog(), newAppConfig.UserConfig.Gui.Language),
- Config: newAppConfig,
- }
-}
diff --git a/pkg/commands/image.go b/pkg/commands/image.go
index 789733d0..2d4504b0 100644
--- a/pkg/commands/image.go
+++ b/pkg/commands/image.go
@@ -1,46 +1,42 @@
package commands
import (
- "context"
"strings"
- "github.com/docker/docker/api/types/filters"
- "github.com/docker/docker/api/types/image"
- "github.com/docker/docker/client"
"github.com/fatih/color"
- "github.com/jesseduffield/lazydocker/pkg/utils"
- "github.com/samber/lo"
+ "github.com/jesseduffield/lazycontainer/pkg/utils"
"github.com/sirupsen/logrus"
)
-// Image : A docker Image
type Image struct {
- Name string
- Tag string
- ID string
- Image image.Summary
- Client *client.Client
- OSCommand *OSCommand
- Log *logrus.Entry
- DockerCommand LimitedDockerCommand
+ Name string
+ Tag string
+ ID string
+ AppleImage AppleImage
+ Client *ContainerClient
+ OSCommand *OSCommand
+ Log *logrus.Entry
+ ContainerCmd LimitedContainerCommand
}
-// Remove removes the image
-func (i *Image) Remove(options image.RemoveOptions) error {
- if _, err := i.Client.ImageRemove(context.Background(), i.ID, options); err != nil {
- return err
- }
-
- return nil
+func (i *Image) Remove(force bool) error {
+ return i.Client.RemoveImage(i.ID, force)
}
-func getHistoryResponseItemDisplayStrings(layer image.HistoryResponseItem) []string {
- tag := ""
- if len(layer.Tags) > 0 {
- tag = layer.Tags[0]
+func (i *Image) Pull() error {
+ return i.Client.PullImage(i.Name + ":" + i.Tag)
+}
+
+func getImageDisplayStrings(img *Image, replacements map[string]string) []string {
+ name := img.Name
+ for prefix, replacement := range replacements {
+ if strings.HasPrefix(name, prefix) {
+ name = strings.Replace(name, prefix, replacement, 1)
+ break
+ }
}
- id := strings.TrimPrefix(layer.ID, "sha256:")
+ id := strings.TrimPrefix(img.AppleImage.Descriptor.Digest, "sha256:")
if len(id) > 10 {
id = id[0:10]
}
@@ -49,95 +45,56 @@ func getHistoryResponseItemDisplayStrings(layer image.HistoryResponseItem) []str
idColor = color.FgBlue
}
- dockerFileCommandPrefix := "/bin/sh -c #(nop) "
- createdBy := layer.CreatedBy
- if strings.Contains(layer.CreatedBy, dockerFileCommandPrefix) {
- createdBy = strings.Trim(strings.TrimPrefix(layer.CreatedBy, dockerFileCommandPrefix), " ")
- split := strings.Split(createdBy, " ")
- createdBy = utils.ColoredString(split[0], color.FgYellow) + " " + strings.Join(split[1:], " ")
- }
-
- createdBy = strings.Replace(createdBy, "\t", " ", -1)
-
- size := utils.FormatBinaryBytes(int(layer.Size))
- sizeColor := color.FgWhite
- if size == "0B" {
- sizeColor = color.FgBlue
+ size := img.AppleImage.FullSize
+ if size == "" {
+ size = utils.FormatBinaryBytes(int(img.AppleImage.Descriptor.Size))
}
return []string{
utils.ColoredString(id, idColor),
- utils.ColoredString(tag, color.FgGreen),
- utils.ColoredString(size, sizeColor),
- createdBy,
+ utils.ColoredString(name, color.FgGreen),
+ utils.ColoredString(img.Tag, color.FgCyan),
+ utils.ColoredString(size, color.FgYellow),
}
}
-// RenderHistory renders the history of the image
-func (i *Image) RenderHistory() (string, error) {
- history, err := i.Client.ImageHistory(context.Background(), i.ID)
- if err != nil {
- return "", err
- }
-
- tableBody := lo.Map(history, func(layer image.HistoryResponseItem, _ int) []string {
- return getHistoryResponseItemDisplayStrings(layer)
- })
-
- headers := [][]string{{"ID", "TAG", "SIZE", "COMMAND"}}
- table := append(headers, tableBody...)
-
- return utils.RenderTable(table)
-}
-
-// RefreshImages returns a slice of docker images
-func (c *DockerCommand) RefreshImages() ([]*Image, error) {
- images, err := c.Client.ImageList(context.Background(), image.ListOptions{})
+func (c *ContainerClient) RefreshImages() ([]*Image, error) {
+ appleImages, err := c.ListImages()
if err != nil {
return nil, err
}
- ownImages := make([]*Image, len(images))
+ ownImages := make([]*Image, len(appleImages))
- for i, img := range images {
- firstTag := ""
- tags := img.RepoTags
- if len(tags) > 0 {
- firstTag = tags[0]
- }
-
- nameParts := strings.Split(firstTag, ":")
- tag := ""
- name := "none"
+ for i, img := range appleImages {
+ nameParts := strings.Split(img.Reference, ":")
+ name := ""
+ tag := "latest"
if len(nameParts) > 1 {
tag = nameParts[len(nameParts)-1]
name = strings.Join(nameParts[:len(nameParts)-1], ":")
+ } else if len(nameParts) == 1 {
+ name = nameParts[0]
+ }
- for prefix, replacement := range c.Config.UserConfig.Replacements.ImageNamePrefixes {
- if strings.HasPrefix(name, prefix) {
- name = strings.Replace(name, prefix, replacement, 1)
- break
- }
+ for prefix, replacement := range c.Config.UserConfig.Replacements.ImageNamePrefixes {
+ if strings.HasPrefix(name, prefix) {
+ name = strings.Replace(name, prefix, replacement, 1)
+ break
}
}
ownImages[i] = &Image{
- ID: img.ID,
- Name: name,
- Tag: tag,
- Image: img,
- Client: c.Client,
- OSCommand: c.OSCommand,
- Log: c.Log,
- DockerCommand: c,
+ ID: img.Descriptor.Digest,
+ Name: name,
+ Tag: tag,
+ AppleImage: img,
+ Client: c,
+ OSCommand: c.OSCommand,
+ Log: c.Log,
+ ContainerCmd: c,
}
}
return ownImages, nil
}
-
-// PruneImages prunes images
-func (c *DockerCommand) PruneImages() error {
- _, err := c.Client.ImagesPrune(context.Background(), filters.Args{})
- return err
-}
diff --git a/pkg/commands/network.go b/pkg/commands/network.go
index 74379ef0..803a8c3c 100644
--- a/pkg/commands/network.go
+++ b/pkg/commands/network.go
@@ -1,54 +1,39 @@
package commands
import (
- "context"
-
- "github.com/docker/docker/api/types/filters"
- "github.com/docker/docker/api/types/network"
- "github.com/docker/docker/client"
"github.com/sirupsen/logrus"
)
-// Network : A docker Network
type Network struct {
Name string
- Network network.Inspect
- Client *client.Client
+ AppleNetwork AppleNetwork
+ Client *ContainerClient
OSCommand *OSCommand
Log *logrus.Entry
- DockerCommand LimitedDockerCommand
+ ContainerCmd LimitedContainerCommand
}
-// RefreshNetworks gets the networks and stores them
-func (c *DockerCommand) RefreshNetworks() ([]*Network, error) {
- networks, err := c.Client.NetworkList(context.Background(), network.ListOptions{})
+func (n *Network) Remove() error {
+ return n.Client.RemoveNetwork(n.Name)
+}
+
+func (c *ContainerClient) RefreshNetworks() ([]*Network, error) {
+ appleNetworks, err := c.ListNetworks()
if err != nil {
return nil, err
}
- ownNetworks := make([]*Network, len(networks))
+ ownNetworks := make([]*Network, len(appleNetworks))
- for i, nw := range networks {
+ for i, net := range appleNetworks {
ownNetworks[i] = &Network{
- Name: nw.Name,
- Network: nw,
- Client: c.Client,
- OSCommand: c.OSCommand,
- Log: c.Log,
- DockerCommand: c,
+ Name: net.ID,
+ AppleNetwork: net,
+ Client: c,
+ OSCommand: c.OSCommand,
+ Log: c.Log,
}
}
return ownNetworks, nil
}
-
-// PruneNetworks prunes networks
-func (c *DockerCommand) PruneNetworks() error {
- _, err := c.Client.NetworksPrune(context.Background(), filters.Args{})
- return err
-}
-
-// Remove removes the network
-func (v *Network) Remove() error {
- return v.Client.NetworkRemove(context.Background(), v.Name)
-}
diff --git a/pkg/commands/os.go b/pkg/commands/os.go
index b8dec4a0..f0618c0c 100644
--- a/pkg/commands/os.go
+++ b/pkg/commands/os.go
@@ -14,8 +14,8 @@ import (
"github.com/go-errors/errors"
"github.com/jesseduffield/kill"
- "github.com/jesseduffield/lazydocker/pkg/config"
- "github.com/jesseduffield/lazydocker/pkg/utils"
+ "github.com/jesseduffield/lazycontainer/pkg/config"
+ "github.com/jesseduffield/lazycontainer/pkg/utils"
"github.com/mgutz/str"
"github.com/sirupsen/logrus"
)
diff --git a/pkg/commands/os_test.go b/pkg/commands/os_test.go
deleted file mode 100644
index 3e036db4..00000000
--- a/pkg/commands/os_test.go
+++ /dev/null
@@ -1,355 +0,0 @@
-package commands
-
-import (
- "fmt"
- "os"
- "os/exec"
- "testing"
-
- "github.com/stretchr/testify/assert"
-)
-
-// TestOSCommandRunCommandWithOutput is a function.
-func TestOSCommandRunCommandWithOutput(t *testing.T) {
- type scenario struct {
- command string
- test func(string, error)
- }
-
- scenarios := []scenario{
- {
- "echo -n '123'",
- func(output string, err error) {
- assert.NoError(t, err)
- assert.EqualValues(t, "123", output)
- },
- },
- {
- "rmdir unexisting-folder",
- func(output string, err error) {
- assert.Regexp(t, "rmdir.*unexisting-folder.*", err.Error())
- },
- },
- }
-
- for _, s := range scenarios {
- s.test(NewDummyOSCommand().RunCommandWithOutput(s.command))
- }
-}
-
-// TestOSCommandRunCommand is a function.
-func TestOSCommandRunCommand(t *testing.T) {
- type scenario struct {
- command string
- test func(error)
- }
-
- scenarios := []scenario{
- {
- "rmdir unexisting-folder",
- func(err error) {
- assert.Regexp(t, "rmdir.*unexisting-folder.*", err.Error())
- },
- },
- }
-
- for _, s := range scenarios {
- s.test(NewDummyOSCommand().RunCommand(s.command))
- }
-}
-
-// TestOSCommandEditFile is a function.
-func TestOSCommandEditFile(t *testing.T) {
- type scenario struct {
- filename string
- command func(string, ...string) *exec.Cmd
- getenv func(string) string
- test func(*exec.Cmd, error)
- }
-
- scenarios := []scenario{
- {
- "test",
- func(name string, arg ...string) *exec.Cmd {
- return exec.Command("exit", "1")
- },
- func(env string) string {
- return ""
- },
- func(cmd *exec.Cmd, err error) {
- assert.EqualError(t, err, "No editor defined in $VISUAL or $EDITOR")
- },
- },
- {
- "test",
- func(name string, arg ...string) *exec.Cmd {
- if name == "which" {
- return exec.Command("exit", "1")
- }
-
- assert.EqualValues(t, "nano", name)
-
- return exec.Command("exit", "0")
- },
- func(env string) string {
- if env == "VISUAL" {
- return "nano"
- }
-
- return ""
- },
- func(cmd *exec.Cmd, err error) {
- assert.NoError(t, err)
- },
- },
- {
- "test",
- func(name string, arg ...string) *exec.Cmd {
- if name == "which" {
- return exec.Command("exit", "1")
- }
-
- assert.EqualValues(t, "emacs", name)
-
- return exec.Command("exit", "0")
- },
- func(env string) string {
- if env == "EDITOR" {
- return "emacs"
- }
-
- return ""
- },
- func(cmd *exec.Cmd, err error) {
- assert.NoError(t, err)
- },
- },
- {
- "test",
- func(name string, arg ...string) *exec.Cmd {
- if name == "which" {
- return exec.Command("echo")
- }
-
- assert.EqualValues(t, "vi", name)
-
- return exec.Command("exit", "0")
- },
- func(env string) string {
- return ""
- },
- func(cmd *exec.Cmd, err error) {
- assert.NoError(t, err)
- },
- },
- }
-
- for _, s := range scenarios {
- OSCmd := NewDummyOSCommand()
- OSCmd.command = s.command
- OSCmd.getenv = s.getenv
-
- s.test(OSCmd.EditFile(s.filename))
- }
-}
-
-func TestOSCommandQuote(t *testing.T) {
- osCommand := NewDummyOSCommand()
-
- osCommand.Platform.os = "linux"
-
- actual := osCommand.Quote("hello `test`")
-
- expected := "\"hello \\`test\\`\""
-
- assert.EqualValues(t, expected, actual)
-}
-
-// TestOSCommandQuoteSingleQuote tests the quote function with ' quotes explicitly for Linux
-func TestOSCommandQuoteSingleQuote(t *testing.T) {
- osCommand := NewDummyOSCommand()
-
- osCommand.Platform.os = "linux"
-
- actual := osCommand.Quote("hello 'test'")
-
- expected := `"hello 'test'"`
-
- assert.EqualValues(t, expected, actual)
-}
-
-// TestOSCommandQuoteDoubleQuote tests the quote function with " quotes explicitly for Linux
-func TestOSCommandQuoteDoubleQuote(t *testing.T) {
- osCommand := NewDummyOSCommand()
-
- osCommand.Platform.os = "linux"
-
- actual := osCommand.Quote(`hello "test"`)
-
- expected := `"hello \"test\""`
-
- assert.EqualValues(t, expected, actual)
-}
-
-// TestOSCommandQuoteWindows tests the quote function for Windows
-func TestOSCommandQuoteWindows(t *testing.T) {
- osCommand := NewDummyOSCommand()
-
- osCommand.Platform.os = "windows"
-
- actual := osCommand.Quote(`hello "test" 'test2'`)
-
- expected := `\"hello "'"'"test"'"'" 'test2'\"`
-
- assert.EqualValues(t, expected, actual)
-}
-
-// TestOSCommandUnquote is a function.
-func TestOSCommandUnquote(t *testing.T) {
- osCommand := NewDummyOSCommand()
-
- actual := osCommand.Unquote(`hello "test"`)
-
- expected := "hello test"
-
- assert.EqualValues(t, expected, actual)
-}
-
-// TestOSCommandFileType is a function.
-func TestOSCommandFileType(t *testing.T) {
- type scenario struct {
- path string
- setup func()
- test func(string)
- }
-
- scenarios := []scenario{
- {
- "testFile",
- func() {
- if _, err := os.Create("testFile"); err != nil {
- panic(err)
- }
- },
- func(output string) {
- assert.EqualValues(t, "file", output)
- },
- },
- {
- "file with spaces",
- func() {
- if _, err := os.Create("file with spaces"); err != nil {
- panic(err)
- }
- },
- func(output string) {
- assert.EqualValues(t, "file", output)
- },
- },
- {
- "testDirectory",
- func() {
- if err := os.Mkdir("testDirectory", 0o644); err != nil {
- panic(err)
- }
- },
- func(output string) {
- assert.EqualValues(t, "directory", output)
- },
- },
- {
- "nonExistant",
- func() {},
- func(output string) {
- assert.EqualValues(t, "other", output)
- },
- },
- }
-
- for _, s := range scenarios {
- s.setup()
- s.test(NewDummyOSCommand().FileType(s.path))
- _ = os.RemoveAll(s.path)
- }
-}
-
-func TestOSCommandCreateTempFile(t *testing.T) {
- type scenario struct {
- testName string
- filename string
- content string
- test func(string, error)
- }
-
- scenarios := []scenario{
- {
- "valid case",
- "filename",
- "content",
- func(path string, err error) {
- assert.NoError(t, err)
-
- content, err := os.ReadFile(path)
- assert.NoError(t, err)
-
- assert.Equal(t, "content", string(content))
- },
- },
- }
-
- for _, s := range scenarios {
- t.Run(s.testName, func(t *testing.T) {
- s.test(NewDummyOSCommand().CreateTempFile(s.filename, s.content))
- })
- }
-}
-
-func TestOSCommandExecutableFromStringWithShellLinux(t *testing.T) {
- osCommand := NewDummyOSCommand()
-
- osCommand.Platform.os = "linux"
-
- tests := []struct {
- name string
- commandStr string
- want string
- }{
- {
- "success",
- "pwd",
- fmt.Sprintf("%v %v %v", osCommand.Platform.shell, osCommand.Platform.shellArg, "\"pwd\""),
- },
- }
-
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- got := osCommand.NewCommandStringWithShell(tt.commandStr)
- assert.Equal(t, tt.want, got)
- })
- }
-}
-
-func TestOSCommandNewCommandStringWithShellWindows(t *testing.T) {
- osCommand := NewDummyOSCommand()
-
- osCommand.Platform.os = "windows"
-
- tests := []struct {
- name string
- commandStr string
- want string
- }{
- {
- "success",
- "pwd",
- fmt.Sprintf("%v %v %v", osCommand.Platform.shell, osCommand.Platform.shellArg, "pwd"),
- },
- }
-
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- got := osCommand.NewCommandStringWithShell(tt.commandStr)
- assert.Equal(t, tt.want, got)
- })
- }
-}
diff --git a/pkg/commands/project.go b/pkg/commands/project.go
deleted file mode 100644
index 529ccfa7..00000000
--- a/pkg/commands/project.go
+++ /dev/null
@@ -1,5 +0,0 @@
-package commands
-
-type Project struct {
- Name string
-}
diff --git a/pkg/commands/service.go b/pkg/commands/service.go
deleted file mode 100644
index 5006eefe..00000000
--- a/pkg/commands/service.go
+++ /dev/null
@@ -1,84 +0,0 @@
-package commands
-
-import (
- "context"
- "os/exec"
-
- "github.com/docker/docker/api/types/container"
- "github.com/jesseduffield/lazydocker/pkg/utils"
- "github.com/sirupsen/logrus"
-)
-
-// Service : A docker Service
-type Service struct {
- Name string
- ID string
- ProjectName string
- OSCommand *OSCommand
- Log *logrus.Entry
- Container *Container
- DockerCommand LimitedDockerCommand
-}
-
-// Remove removes the service's containers
-func (s *Service) Remove(options container.RemoveOptions) error {
- return s.Container.Remove(options)
-}
-
-// Stop stops the service's containers
-func (s *Service) Stop() error {
- return s.runCommand(s.OSCommand.Config.UserConfig.CommandTemplates.StopService)
-}
-
-// Up up's the service
-func (s *Service) Up() error {
- return s.runCommand(s.OSCommand.Config.UserConfig.CommandTemplates.UpService)
-}
-
-// Restart restarts the service
-func (s *Service) Restart() error {
- return s.runCommand(s.OSCommand.Config.UserConfig.CommandTemplates.RestartService)
-}
-
-// Start starts the service
-func (s *Service) Start() error {
- return s.runCommand(s.OSCommand.Config.UserConfig.CommandTemplates.StartService)
-}
-
-func (s *Service) runCommand(templateCmdStr string) error {
- command := utils.ApplyTemplate(
- templateCmdStr,
- s.DockerCommand.NewCommandObject(CommandObject{Service: s}),
- )
- return s.OSCommand.RunCommand(command)
-}
-
-// Attach attaches to the service
-func (s *Service) Attach() (*exec.Cmd, error) {
- return s.Container.Attach()
-}
-
-// ViewLogs attaches to a subprocess viewing the service's logs
-func (s *Service) ViewLogs() (*exec.Cmd, error) {
- templateString := s.OSCommand.Config.UserConfig.CommandTemplates.ViewServiceLogs
- command := utils.ApplyTemplate(
- templateString,
- s.DockerCommand.NewCommandObject(CommandObject{Service: s}),
- )
-
- cmd := s.OSCommand.ExecutableFromString(command)
- s.OSCommand.PrepareForChildren(cmd)
-
- return cmd, nil
-}
-
-// RenderTop renders the process list of the service
-func (s *Service) RenderTop(ctx context.Context) (string, error) {
- templateString := s.OSCommand.Config.UserConfig.CommandTemplates.ServiceTop
- command := utils.ApplyTemplate(
- templateString,
- s.DockerCommand.NewCommandObject(CommandObject{Service: s}),
- )
-
- return s.OSCommand.RunCommandWithOutputContext(ctx, command)
-}
diff --git a/pkg/commands/ssh/ssh.go b/pkg/commands/ssh/ssh.go
deleted file mode 100644
index ecc84045..00000000
--- a/pkg/commands/ssh/ssh.go
+++ /dev/null
@@ -1,159 +0,0 @@
-package ssh
-
-import (
- "context"
- "fmt"
- "io"
- "net"
- "net/url"
- "os"
- "os/exec"
- "path"
- "time"
-)
-
-// we only need these two methods from our OSCommand struct, for killing commands
-type CmdKiller interface {
- Kill(cmd *exec.Cmd) error
- PrepareForChildren(cmd *exec.Cmd)
-}
-
-type SSHHandler struct {
- oSCommand CmdKiller
-
- dialContext func(ctx context.Context, network, addr string) (io.Closer, error)
- startCmd func(*exec.Cmd) error
- tempDir func(dir string, pattern string) (name string, err error)
- getenv func(key string) string
- setenv func(key, value string) error
-}
-
-func NewSSHHandler(oSCommand CmdKiller) *SSHHandler {
- return &SSHHandler{
- oSCommand: oSCommand,
-
- dialContext: func(ctx context.Context, network, addr string) (io.Closer, error) {
- return (&net.Dialer{}).DialContext(ctx, network, addr)
- },
- startCmd: func(cmd *exec.Cmd) error { return cmd.Start() },
- tempDir: os.MkdirTemp,
- getenv: os.Getenv,
- setenv: os.Setenv,
- }
-}
-
-// HandleSSHDockerHost overrides the DOCKER_HOST environment variable
-// to point towards a local unix socket tunneled over SSH to the specified ssh host.
-func (self *SSHHandler) HandleSSHDockerHost() (io.Closer, error) {
- const key = "DOCKER_HOST"
- ctx := context.Background()
- u, err := url.Parse(self.getenv(key))
- if err != nil {
- // if no or an invalid docker host is specified, continue nominally
- return noopCloser{}, nil
- }
-
- // if the docker host scheme is "ssh", forward the docker socket before creating the client
- if u.Scheme == "ssh" {
- tunnel, err := self.createDockerHostTunnel(ctx, u.Host)
- if err != nil {
- return noopCloser{}, fmt.Errorf("tunnel ssh docker host: %w", err)
- }
- err = self.setenv(key, tunnel.socketPath)
- if err != nil {
- return noopCloser{}, fmt.Errorf("override DOCKER_HOST to tunneled socket: %w", err)
- }
-
- return tunnel, nil
- }
- return noopCloser{}, nil
-}
-
-type noopCloser struct{}
-
-func (noopCloser) Close() error { return nil }
-
-type tunneledDockerHost struct {
- socketPath string
- cmd *exec.Cmd
- oSCommand CmdKiller
-}
-
-var _ io.Closer = (*tunneledDockerHost)(nil)
-
-func (t *tunneledDockerHost) Close() error {
- return t.oSCommand.Kill(t.cmd)
-}
-
-func (self *SSHHandler) createDockerHostTunnel(ctx context.Context, remoteHost string) (*tunneledDockerHost, error) {
- socketDir, err := self.tempDir("/tmp", "lazydocker-sshtunnel-")
- if err != nil {
- return nil, fmt.Errorf("create ssh tunnel tmp file: %w", err)
- }
- localSocket := path.Join(socketDir, "dockerhost.sock")
-
- cmd, err := self.tunnelSSH(ctx, remoteHost, localSocket)
- if err != nil {
- return nil, fmt.Errorf("tunnel docker host over ssh: %w", err)
- }
-
- // set a reasonable timeout, then wait for the socket to dial successfully
- // before attempting to create a new docker client
- const socketTunnelTimeout = 8 * time.Second
- ctx, cancel := context.WithTimeout(ctx, socketTunnelTimeout)
- defer cancel()
-
- err = self.retrySocketDial(ctx, localSocket)
- if err != nil {
- return nil, fmt.Errorf("ssh tunneled socket never became available: %w", err)
- }
-
- // construct the new DOCKER_HOST url with the proper scheme
- newDockerHostURL := url.URL{Scheme: "unix", Path: localSocket}
- return &tunneledDockerHost{
- socketPath: newDockerHostURL.String(),
- cmd: cmd,
- oSCommand: self.oSCommand,
- }, nil
-}
-
-// Attempt to dial the socket until it becomes available.
-// The retry loop will continue until the parent context is canceled.
-func (self *SSHHandler) retrySocketDial(ctx context.Context, socketPath string) error {
- t := time.NewTicker(1 * time.Second)
- defer t.Stop()
-
- for {
- select {
- case <-ctx.Done():
- return ctx.Err()
- case <-t.C:
- }
- // attempt to dial the socket, exit on success
- err := self.tryDial(ctx, socketPath)
- if err != nil {
- continue
- }
- return nil
- }
-}
-
-// Try to dial the specified unix socket, immediately close the connection if successfully created.
-func (self *SSHHandler) tryDial(ctx context.Context, socketPath string) error {
- conn, err := self.dialContext(ctx, "unix", socketPath)
- if err != nil {
- return err
- }
- defer conn.Close()
- return nil
-}
-
-func (self *SSHHandler) tunnelSSH(ctx context.Context, host, localSocket string) (*exec.Cmd, error) {
- cmd := exec.CommandContext(ctx, "ssh", "-L", localSocket+":/var/run/docker.sock", host, "-N")
- self.oSCommand.PrepareForChildren(cmd)
- err := self.startCmd(cmd)
- if err != nil {
- return nil, err
- }
- return cmd, nil
-}
diff --git a/pkg/commands/ssh/ssh_test.go b/pkg/commands/ssh/ssh_test.go
deleted file mode 100644
index f1ea1b90..00000000
--- a/pkg/commands/ssh/ssh_test.go
+++ /dev/null
@@ -1,109 +0,0 @@
-package ssh
-
-import (
- "context"
- "io"
- "os/exec"
- "testing"
-
- "github.com/stretchr/testify/assert"
-)
-
-func TestSSHHandlerHandleSSHDockerHost(t *testing.T) {
- type scenario struct {
- testName string
- envVarValue string
- expectedDialContextCount int
- expectedStartCmdCount int
- }
-
- scenarios := []scenario{
- {
- testName: "No env var set",
- envVarValue: "",
- expectedDialContextCount: 0,
- expectedStartCmdCount: 0,
- },
- {
- testName: "Env var set with https scheme",
- envVarValue: "https://myhost.com",
- expectedStartCmdCount: 0,
- expectedDialContextCount: 0,
- },
- {
- testName: "Env var set with ssh scheme",
- envVarValue: "ssh://myhost@192.168.5.178",
- expectedStartCmdCount: 1,
- expectedDialContextCount: 1,
- },
- }
-
- for _, s := range scenarios {
- s := s
- t.Run(s.testName, func(t *testing.T) {
- getenv := func(key string) string {
- if key != "DOCKER_HOST" {
- t.Errorf("Expected key to be DOCKER_HOST, got %s", key)
- }
-
- return s.envVarValue
- }
-
- tempDir := func(dir string, pattern string) (string, error) {
- assert.Equal(t, "/tmp", dir)
- assert.Equal(t, "lazydocker-sshtunnel-", pattern)
-
- return "/tmp/lazydocker-ssh-tunnel-12345", nil
- }
-
- setenv := func(key, value string) error {
- assert.Equal(t, "DOCKER_HOST", key)
- assert.Equal(t, "unix:///tmp/lazydocker-ssh-tunnel-12345/dockerhost.sock", value)
- return nil
- }
-
- startCmdCount := 0
- startCmd := func(cmd *exec.Cmd) error {
- assert.EqualValues(t, []string{"ssh", "-L", "/tmp/lazydocker-ssh-tunnel-12345/dockerhost.sock:/var/run/docker.sock", "192.168.5.178", "-N"}, cmd.Args)
-
- startCmdCount++
-
- return nil
- }
-
- dialContextCount := 0
- dialContext := func(ctx context.Context, network string, address string) (io.Closer, error) {
- assert.Equal(t, "unix", network)
- assert.Equal(t, "/tmp/lazydocker-ssh-tunnel-12345/dockerhost.sock", address)
-
- dialContextCount++
-
- return noopCloser{}, nil
- }
-
- handler := &SSHHandler{
- oSCommand: &fakeCmdKiller{},
-
- dialContext: dialContext,
- startCmd: startCmd,
- tempDir: tempDir,
- getenv: getenv,
- setenv: setenv,
- }
-
- _, err := handler.HandleSSHDockerHost()
- assert.NoError(t, err)
-
- assert.Equal(t, s.expectedDialContextCount, dialContextCount)
- assert.Equal(t, s.expectedStartCmdCount, startCmdCount)
- })
- }
-}
-
-type fakeCmdKiller struct{}
-
-func (self *fakeCmdKiller) Kill(cmd *exec.Cmd) error {
- return nil
-}
-
-func (self *fakeCmdKiller) PrepareForChildren(cmd *exec.Cmd) {}
diff --git a/pkg/commands/volume.go b/pkg/commands/volume.go
index 169ba8d5..3578f8a3 100644
--- a/pkg/commands/volume.go
+++ b/pkg/commands/volume.go
@@ -1,56 +1,39 @@
package commands
import (
- "context"
-
- "github.com/docker/docker/api/types/filters"
- "github.com/docker/docker/api/types/volume"
- "github.com/docker/docker/client"
"github.com/sirupsen/logrus"
)
-// Volume : A docker Volume
type Volume struct {
- Name string
- Volume *volume.Volume
- Client *client.Client
- OSCommand *OSCommand
- Log *logrus.Entry
- DockerCommand LimitedDockerCommand
+ Name string
+ AppleVolume AppleVolume
+ Client *ContainerClient
+ OSCommand *OSCommand
+ Log *logrus.Entry
+ ContainerCmd LimitedContainerCommand
}
-// RefreshVolumes gets the volumes and stores them
-func (c *DockerCommand) RefreshVolumes() ([]*Volume, error) {
- result, err := c.Client.VolumeList(context.Background(), volume.ListOptions{})
+func (v *Volume) Remove() error {
+ return v.Client.RemoveVolume(v.Name)
+}
+
+func (c *ContainerClient) RefreshVolumes() ([]*Volume, error) {
+ appleVolumes, err := c.ListVolumes()
if err != nil {
return nil, err
}
- volumes := result.Volumes
+ ownVolumes := make([]*Volume, len(appleVolumes))
- ownVolumes := make([]*Volume, len(volumes))
-
- for i, vol := range volumes {
+ for i, vol := range appleVolumes {
ownVolumes[i] = &Volume{
- Name: vol.Name,
- Volume: vol,
- Client: c.Client,
- OSCommand: c.OSCommand,
- Log: c.Log,
- DockerCommand: c,
+ Name: vol.Name,
+ AppleVolume: vol,
+ Client: c,
+ OSCommand: c.OSCommand,
+ Log: c.Log,
}
}
return ownVolumes, nil
}
-
-// PruneVolumes prunes volumes
-func (c *DockerCommand) PruneVolumes() error {
- _, err := c.Client.VolumesPrune(context.Background(), filters.Args{})
- return err
-}
-
-// Remove removes the volume
-func (v *Volume) Remove(force bool) error {
- return v.Client.VolumeRemove(context.Background(), v.Name, force)
-}
diff --git a/pkg/config/app_config.go b/pkg/config/app_config.go
index 0ebc1796..ce423d91 100644
--- a/pkg/config/app_config.go
+++ b/pkg/config/app_config.go
@@ -1,72 +1,26 @@
-// Package config handles all the user-configuration. The fields here are
-// all in PascalCase but in your actual config.yml they'll be in camelCase.
-// You can view the default config with `lazydocker --config`.
-// You can open your config file by going to the status panel (using left-arrow)
-// and pressing 'o'.
-// You can directly edit the file (e.g. in vim) by pressing 'e' instead.
-// To see the final config after your user-specific options have been merged
-// with the defaults, go to the 'about' tab in the status panel.
-// Because of the way we merge your user config with the defaults you may need
-// to be careful: if for example you set a `commandTemplates:` yaml key but then
-// give it no child values, it will scrap all of the defaults and the app will
-// probably crash.
package config
import (
"os"
"path/filepath"
- "strings"
"time"
"github.com/OpenPeeDeeP/xdg"
"github.com/jesseduffield/yaml"
)
-// UserConfig holds all of the user-configurable options
type UserConfig struct {
- // Gui is for configuring visual things like colors and whether we show or
- // hide things
- Gui GuiConfig `yaml:"gui,omitempty"`
-
- // ConfirmOnQuit when enabled prompts you to confirm you want to quit when you
- // hit esc or q when no confirmation panels are open
- ConfirmOnQuit bool `yaml:"confirmOnQuit,omitempty"`
-
- // Logs determines how we render/filter a container's logs
- Logs LogsConfig `yaml:"logs,omitempty"`
-
- // CommandTemplates determines what commands actually get called when we run
- // certain commands
- CommandTemplates CommandTemplatesConfig `yaml:"commandTemplates,omitempty"`
-
- // CustomCommands determines what shows up in your custom commands menu when
- // you press 'c'. You can use go templates to access three items on the
- // struct: the DockerCompose command (defaulted to 'docker-compose'), the
- // Service if present, and the Container if present. The struct types for
- // those are found in the commands package
+ Gui GuiConfig `yaml:"gui,omitempty"`
+ ConfirmOnQuit bool `yaml:"confirmOnQuit,omitempty"`
+ Logs LogsConfig `yaml:"logs,omitempty"`
CustomCommands CustomCommands `yaml:"customCommands,omitempty"`
-
- // BulkCommands are commands that apply to all items in a panel e.g.
- // killing all containers, stopping all services, or pruning all images
- BulkCommands CustomCommands `yaml:"bulkCommands,omitempty"`
-
- // OS determines what defaults are set for opening files and links
- OS OSConfig `yaml:"oS,omitempty"`
-
- // Stats determines how long lazydocker will gather container stats for, and
- // what stat info to graph
- Stats StatsConfig `yaml:"stats,omitempty"`
-
- // Replacements determines how we render an item's info
- Replacements Replacements `yaml:"replacements,omitempty"`
-
- // For demo purposes: any list item with one of these strings as a substring
- // will be filtered out and not displayed.
- // Not documented because it's subject to change
- Ignore []string `yaml:"ignore,omitempty"`
+ BulkCommands CustomCommands `yaml:"bulkCommands,omitempty"`
+ OS OSConfig `yaml:"oS,omitempty"`
+ Stats StatsConfig `yaml:"stats,omitempty"`
+ Replacements Replacements `yaml:"replacements,omitempty"`
+ Ignore []string `yaml:"ignore,omitempty"`
}
-// ThemeConfig is for setting the colors of panels and some text.
type ThemeConfig struct {
ActiveBorderColor []string `yaml:"activeBorderColor,omitempty"`
InactiveBorderColor []string `yaml:"inactiveBorderColor,omitempty"`
@@ -74,269 +28,56 @@ type ThemeConfig struct {
OptionsTextColor []string `yaml:"optionsTextColor,omitempty"`
}
-// GuiConfig is for configuring visual things like colors and whether we show or
-// hide things
type GuiConfig struct {
- // ScrollHeight determines how many characters you scroll at a time when
- // scrolling the main panel
- ScrollHeight int `yaml:"scrollHeight,omitempty"`
-
- // Language determines which language the GUI displayed.
- Language string `yaml:"language,omitempty"`
-
- // ScrollPastBottom determines whether you can scroll past the bottom of the
- // main view
- ScrollPastBottom bool `yaml:"scrollPastBottom,omitempty"`
-
- // IgnoreMouseEvents is for when you do not want to use your mouse to interact
- // with anything
- IgnoreMouseEvents bool `yaml:"mouseEvents,omitempty"`
-
- // Theme determines what colors and color attributes your panel borders have.
- // I always set inactiveBorderColor to black because in my terminal it's more
- // of a grey, but that doesn't work in your average terminal. I highly
- // recommended finding a combination that works for you
- Theme ThemeConfig `yaml:"theme,omitempty"`
-
- // ShowAllContainers determines whether the Containers panel contains all the
- // containers returned by `docker ps -a`, or just those containers that aren't
- // directly linked to a service. It is probably desirable to enable this if
- // you have multiple containers per service, but otherwise it can cause a lot
- // of clutter
- ShowAllContainers bool `yaml:"showAllContainers,omitempty"`
-
- // ReturnImmediately determines whether you get the 'press enter to return to
- // lazydocker' message after a subprocess has completed. You would set this to
- // true if you often want to see the output of subprocesses before returning
- // to lazydocker. I would default this to false but then people who want it
- // set to true won't even know the config option exists.
- ReturnImmediately bool `yaml:"returnImmediately,omitempty"`
-
- // WrapMainPanel determines whether we use word wrap on the main panel
- WrapMainPanel bool `yaml:"wrapMainPanel,omitempty"`
-
- // LegacySortContainers determines if containers should be sorted using legacy approach.
- // By default, containers are now sorted by status. This setting allows users to
- // use legacy behaviour instead.
- LegacySortContainers bool `yaml:"legacySortContainers,omitempty"`
-
- // If 0.333, then the side panels will be 1/3 of the screen's width
- SidePanelWidth float64 `yaml:"sidePanelWidth"`
-
- // Determines whether we show the bottom line (the one containing keybinding
- // info and the status of the app).
- ShowBottomLine bool `yaml:"showBottomLine"`
-
- // When true, increases vertical space used by focused side panel,
- // creating an accordion effect
- ExpandFocusedSidePanel bool `yaml:"expandFocusedSidePanel"`
-
- // ScreenMode allow user to specify which screen mode will be used on startup
- ScreenMode string `yaml:"screenMode,omitempty"`
-
- // Determines the style of the container status and container health display in the
- // containers panel. "long": full words (default), "short": one or two characters,
- // "icon": unicode emoji.
- ContainerStatusHealthStyle string `yaml:"containerStatusHealthStyle"`
-
- // Window border style.
- // One of 'rounded' (default) | 'single' | 'double' | 'hidden'
- Border string `yaml:"border"`
+ ScrollHeight int `yaml:"scrollHeight,omitempty"`
+ Language string `yaml:"language,omitempty"`
+ ScrollPastBottom bool `yaml:"scrollPastBottom,omitempty"`
+ IgnoreMouseEvents bool `yaml:"mouseEvents,omitempty"`
+ Theme ThemeConfig `yaml:"theme,omitempty"`
+ ShowAllContainers bool `yaml:"showAllContainers,omitempty"`
+ ReturnImmediately bool `yaml:"returnImmediately,omitempty"`
+ WrapMainPanel bool `yaml:"wrapMainPanel,omitempty"`
+ LegacySortContainers bool `yaml:"legacySortContainers,omitempty"`
+ SidePanelWidth float64 `yaml:"sidePanelWidth"`
+ ShowBottomLine bool `yaml:"showBottomLine"`
+ ExpandFocusedSidePanel bool `yaml:"expandFocusedSidePanel"`
+ ScreenMode string `yaml:"screenMode,omitempty"`
+ ContainerStatusHealthStyle string `yaml:"containerStatusHealthStyle"`
+ Border string `yaml:"border"`
}
-// CommandTemplatesConfig determines what commands actually get called when we
-// run certain commands
-type CommandTemplatesConfig struct {
- // RestartService is for restarting a service. docker-compose restart {{
- // .Service.Name }} works but I prefer docker-compose up --force-recreate {{
- // .Service.Name }}
- RestartService string `yaml:"restartService,omitempty"`
-
- // StartService is just like the above but for starting
- StartService string `yaml:"startService,omitempty"`
-
- // UpService ups the service (creates and starts)
- UpService string `yaml:"upService,omitempty"`
-
- // Runs "docker-compose up -d"
- Up string `yaml:"up,omitempty"`
-
- // downs everything
- Down string `yaml:"down,omitempty"`
- // downs and removes volumes
- DownWithVolumes string `yaml:"downWithVolumes,omitempty"`
-
- // DockerCompose is for your docker-compose command. You may want to combine a
- // few different docker-compose.yml files together, in which case you can set
- // this to "docker compose -f foo/docker-compose.yml -f
- // bah/docker-compose.yml". The reason that the other docker-compose command
- // templates all start with {{ .DockerCompose }} is so that they can make use
- // of whatever you've set in this value rather than you having to copy and
- // paste it to all the other commands
- DockerCompose string `yaml:"dockerCompose,omitempty"`
-
- // StopService is the command for stopping a service
- StopService string `yaml:"stopService,omitempty"`
-
- // ServiceLogs get the logs for a service. This is actually not currently
- // used; we just get the logs of the corresponding container. But we should
- // probably support explicitly returning the logs of the service when you've
- // selected the service, given that a service may have multiple containers.
- ServiceLogs string `yaml:"serviceLogs,omitempty"`
-
- // ViewServiceLogs is for when you want to view the logs of a service as a
- // subprocess. This defaults to having no filter, unlike the in-app logs
- // commands which will usually filter down to the last hour for the sake of
- // performance.
- ViewServiceLogs string `yaml:"viewServiceLogs,omitempty"`
-
- // RebuildService is the command for rebuilding a service. Defaults to
- // something along the lines of `{{ .DockerCompose }} up --build {{
- // .Service.Name }}`
- RebuildService string `yaml:"rebuildService,omitempty"`
-
- // RecreateService is for force-recreating a service. I prefer this to
- // restarting a service because it will also restart any dependent services
- // and ensure they're running before trying to run the service at hand
- RecreateService string `yaml:"recreateService,omitempty"`
-
- // AllLogs is for showing what you get from doing `docker compose logs`. It
- // combines all the logs together
- AllLogs string `yaml:"allLogs,omitempty"`
-
- // ViewAllLogs is the command we use when you want to see all logs in a subprocess with no filtering
- ViewAllLogs string `yaml:"viewAlLogs,omitempty"`
-
- // DockerComposeConfig is the command for viewing the config of your docker
- // compose. It basically prints out the yaml from your docker-compose.yml
- // file(s)
- DockerComposeConfig string `yaml:"dockerComposeConfig,omitempty"`
-
- // CheckDockerComposeConfig is what we use to check whether we are in a
- // docker-compose context. If the command returns an error then we clearly
- // aren't in a docker-compose config and we then just hide the services panel
- // and only show containers
- CheckDockerComposeConfig string `yaml:"checkDockerComposeConfig,omitempty"`
-
- // ServiceTop is the command for viewing the processes under a given service
- ServiceTop string `yaml:"serviceTop,omitempty"`
-}
-
-// OSConfig contains config on the level of the os
-type OSConfig struct {
- // OpenCommand is the command for opening a file
- OpenCommand string `yaml:"openCommand,omitempty"`
-
- // OpenCommand is the command for opening a link
- OpenLinkCommand string `yaml:"openLinkCommand,omitempty"`
-}
-
-// GraphConfig specifies how to make a graph of recorded container stats
type GraphConfig struct {
- // Min sets the minimum value that you want to display. If you want to set
- // this, you should also set MinType to "static". The reason for this is that
- // if Min == 0, it's not clear if it has not been set (given that the
- // zero-value of an int is 0) or if it's intentionally been set to 0.
- Min float64 `yaml:"min,omitempty"`
-
- // Max sets the maximum value that you want to display. If you want to set
- // this, you should also set MaxType to "static". The reason for this is that
- // if Max == 0, it's not clear if it has not been set (given that the
- // zero-value of an int is 0) or if it's intentionally been set to 0.
- Max float64 `yaml:"max,omitempty"`
-
- // Height sets the height of the graph in ascii characters
- Height int `yaml:"height,omitempty"`
-
- // Caption sets the caption of the graph. If you want to show CPU Percentage
- // you could set this to "CPU (%)"
- Caption string `yaml:"caption,omitempty"`
-
- // This is the path to the stat that you want to display. It is based on the
- // RecordedStats struct in container_stats.go, so feel free to look there to
- // see all the options available. Alternatively if you go into lazydocker and
- // go to the stats tab, you'll see that same struct in JSON format, so you can
- // just PascalCase the path and you'll have a valid path. E.g.
- // ClientStats.blkio_stats -> "ClientStats.BlkioStats"
- StatPath string `yaml:"statPath,omitempty"`
-
- // This determines the color of the graph. This can be any color attribute,
- // e.g. 'blue', 'green'
- Color string `yaml:"color,omitempty"`
-
- // MinType and MaxType are each one of "", "static". blank means the min/max
- // of the data set will be used. "static" means the min/max specified will be
- // used
- MinType string `yaml:"minType,omitempty"`
-
- // MaxType is just like MinType but for the max value
- MaxType string `yaml:"maxType,omitempty"`
+ Min float64 `yaml:"min,omitempty"`
+ Max float64 `yaml:"max,omitempty"`
+ Height int `yaml:"height,omitempty"`
+ Caption string `yaml:"caption,omitempty"`
+ StatPath string `yaml:"statPath,omitempty"`
+ Color string `yaml:"color,omitempty"`
+ MinType string `yaml:"minType,omitempty"`
+ MaxType string `yaml:"maxType,omitempty"`
}
-// StatsConfig contains the stuff relating to stats and graphs
type StatsConfig struct {
- // Graphs contains the configuration for the stats graphs we want to show in
- // the app
- Graphs []GraphConfig
-
- // MaxDuration tells us how long to collect stats for. Currently this defaults
- // to "5m" i.e. 5 minutes.
+ Graphs []GraphConfig `yaml:"graphs"`
MaxDuration time.Duration `yaml:"maxDuration,omitempty"`
}
-// CustomCommands contains the custom commands that you might want to use on any
-// given service or container
type CustomCommands struct {
- // Containers contains the custom commands for containers
Containers []CustomCommand `yaml:"containers,omitempty"`
-
- // Services contains the custom commands for services
- Services []CustomCommand `yaml:"services,omitempty"`
-
- // Images contains the custom commands for images
- Images []CustomCommand `yaml:"images,omitempty"`
-
- // Volumes contains the custom commands for volumes
- Volumes []CustomCommand `yaml:"volumes,omitempty"`
-
- // Networks contains the custom commands for networks
- Networks []CustomCommand `yaml:"networks,omitempty"`
+ Images []CustomCommand `yaml:"images,omitempty"`
+ Volumes []CustomCommand `yaml:"volumes,omitempty"`
+ Networks []CustomCommand `yaml:"networks,omitempty"`
}
-// Replacements contains the stuff relating to rendering a container's info
type Replacements struct {
- // ImageNamePrefixes tells us how to replace a prefix in the Docker image name
ImageNamePrefixes map[string]string `yaml:"imageNamePrefixes,omitempty"`
}
-// CustomCommand is a template for a command we want to run against a service or
-// container
type CustomCommand struct {
- // Name is the name of the command, purely for visual display
- Name string `yaml:"name"`
-
- // Attach tells us whether to switch to a subprocess to interact with the
- // called program, or just read its output. If Attach is set to false, the
- // command will run in the background. I'm open to the idea of having a third
- // option where the output plays in the main panel.
- Attach bool `yaml:"attach"`
-
- // Shell indicates whether to invoke the Command on a shell or not.
- // Example of a bash invoked command: `/bin/bash -c "{Command}".
- Shell bool `yaml:"shell"`
-
- // Command is the command we want to run. We can use the go templates here as
- // well. One example might be `{{ .DockerCompose }} exec {{ .Service.Name }}
- // /bin/sh`
- Command string `yaml:"command"`
-
- // ServiceNames is used to restrict this command to just one or more services.
- // An example might be 'rails migrate' for your rails api service(s). This
- // field has no effect on customcommands under the 'communications' part of
- // the customCommand config.
- ServiceNames []string `yaml:"serviceNames"`
-
- // InternalFunction is the name of a function inside lazydocker that we want to run, as opposed to a command-line command. This is only used internally and can't be configured by the user
+ Name string `yaml:"name"`
+ Attach bool `yaml:"attach"`
+ Shell bool `yaml:"shell"`
+ Command string `yaml:"command"`
InternalFunction func() error `yaml:"-"`
}
@@ -346,10 +87,11 @@ type LogsConfig struct {
Tail string `yaml:"tail,omitempty"`
}
-// GetDefaultConfig returns the application default configuration NOTE (to
-// contributors, not users): do not default a boolean to true, because false is
-// the boolean zero value and this will be ignored when parsing the user's
-// config
+type OSConfig struct {
+ OpenCommand string `yaml:"openCommand,omitempty"`
+ OpenLinkCommand string `yaml:"openLinkCommand,omitempty"`
+}
+
func GetDefaultConfig() UserConfig {
duration, err := time.ParseDuration("3m")
if err != nil {
@@ -368,11 +110,11 @@ func GetDefaultConfig() UserConfig {
SelectedLineBgColor: []string{"blue"},
OptionsTextColor: []string{"blue"},
},
- ShowAllContainers: false,
+ ShowAllContainers: true,
ReturnImmediately: false,
WrapMainPanel: true,
LegacySortContainers: false,
- SidePanelWidth: 0.3333,
+ SidePanelWidth: 0.25,
ShowBottomLine: true,
ExpandFocusedSidePanel: false,
ScreenMode: "normal",
@@ -384,76 +126,41 @@ func GetDefaultConfig() UserConfig {
Since: "60m",
Tail: "",
},
- CommandTemplates: CommandTemplatesConfig{
- DockerCompose: "docker compose",
- RestartService: "{{ .DockerCompose }} restart {{ .Service.Name }}",
- StartService: "{{ .DockerCompose }} start {{ .Service.Name }}",
- Up: "{{ .DockerCompose }} up -d",
- Down: "{{ .DockerCompose }} down",
- DownWithVolumes: "{{ .DockerCompose }} down --volumes",
- UpService: "{{ .DockerCompose }} up -d {{ .Service.Name }}",
- RebuildService: "{{ .DockerCompose }} up -d --build {{ .Service.Name }}",
- RecreateService: "{{ .DockerCompose }} up -d --force-recreate {{ .Service.Name }}",
- StopService: "{{ .DockerCompose }} stop {{ .Service.Name }}",
- ServiceLogs: "{{ .DockerCompose }} logs --since=60m --follow {{ .Service.Name }}",
- ViewServiceLogs: "{{ .DockerCompose }} logs --follow {{ .Service.Name }}",
- AllLogs: "{{ .DockerCompose }} logs --tail=300 --follow",
- ViewAllLogs: "{{ .DockerCompose }} logs",
- DockerComposeConfig: "{{ .DockerCompose }} config",
- CheckDockerComposeConfig: "{{ .DockerCompose }} config --quiet",
- ServiceTop: "{{ .DockerCompose }} top {{ .Service.Name }}",
- },
CustomCommands: CustomCommands{
Containers: []CustomCommand{},
- Services: []CustomCommand{},
Images: []CustomCommand{},
Volumes: []CustomCommand{},
+ Networks: []CustomCommand{},
},
BulkCommands: CustomCommands{
- Services: []CustomCommand{
+ Containers: []CustomCommand{
{
- Name: "up",
- Command: "{{ .DockerCompose }} up -d",
- },
- {
- Name: "up (attached)",
- Command: "{{ .DockerCompose }} up",
- Attach: true,
- },
- {
- Name: "stop",
- Command: "{{ .DockerCompose }} stop",
- },
- {
- Name: "pull",
- Command: "{{ .DockerCompose }} pull",
- Attach: true,
- },
- {
- Name: "build",
- Command: "{{ .DockerCompose }} build --parallel --force-rm",
- Attach: true,
- },
- {
- Name: "down",
- Command: "{{ .DockerCompose }} down",
- },
- {
- Name: "down with volumes",
- Command: "{{ .DockerCompose }} down --volumes",
- },
- {
- Name: "down with images",
- Command: "{{ .DockerCompose }} down --rmi all",
- },
- {
- Name: "down with volumes and images",
- Command: "{{ .DockerCompose }} down --volumes --rmi all",
+ Name: "prune stopped",
+ Command: "container prune",
+ },
+ },
+ Images: []CustomCommand{
+ {
+ Name: "prune dangling",
+ Command: "container image prune",
+ },
+ {
+ Name: "prune all",
+ Command: "container image prune -a",
+ },
+ },
+ Volumes: []CustomCommand{
+ {
+ Name: "prune",
+ Command: "container volume prune",
+ },
+ },
+ Networks: []CustomCommand{
+ {
+ Name: "prune",
+ Command: "container network prune",
},
},
- Containers: []CustomCommand{},
- Images: []CustomCommand{},
- Volumes: []CustomCommand{},
},
OS: GetPlatformDefaultConfig(),
Stats: StatsConfig{
@@ -477,13 +184,12 @@ func GetDefaultConfig() UserConfig {
}
}
-// AppConfig contains the base configuration fields required for lazydocker.
type AppConfig struct {
Debug bool `long:"debug" env:"DEBUG" default:"false"`
Version string `long:"version" env:"VERSION" default:"unversioned"`
Commit string `long:"commit" env:"COMMIT"`
BuildDate string `long:"build-date" env:"BUILD_DATE"`
- Name string `long:"name" env:"NAME" default:"lazydocker"`
+ Name string `long:"name" env:"NAME" default:"lazyapple"`
BuildSource string `long:"build-source" env:"BUILD_SOURCE" default:""`
UserConfig *UserConfig
ConfigDir string
@@ -491,7 +197,6 @@ type AppConfig struct {
ProjectName string
}
-// NewAppConfig makes a new app config
func NewAppConfig(name, version, commit, date string, buildSource string, debuggingFlag bool, composeFiles []string, projectDir string, projectName string) (*AppConfig, error) {
configDir, err := findOrCreateConfigDir(name)
if err != nil {
@@ -503,11 +208,6 @@ func NewAppConfig(name, version, commit, date string, buildSource string, debugg
return nil, err
}
- // Pass compose files as individual -f flags to docker compose
- if len(composeFiles) > 0 {
- userConfig.CommandTemplates.DockerCompose += " -f " + strings.Join(composeFiles, " -f ")
- }
-
appConfig := &AppConfig{
Name: name,
Version: version,
@@ -586,10 +286,6 @@ func loadUserConfig(configDir string, base *UserConfig) (*UserConfig, error) {
return base, nil
}
-// WriteToUserConfig allows you to set a value on the user config to be saved
-// note that if you set a zero-value, it may be ignored e.g. a false or 0 or
-// empty string this is because we are using the omitempty yaml directive so
-// that we don't write a heap of zero values to the user's config.yml
func (c *AppConfig) WriteToUserConfig(updateConfig func(*UserConfig) error) error {
userConfig, err := loadUserConfig(c.ConfigDir, &UserConfig{})
if err != nil {
@@ -608,7 +304,6 @@ func (c *AppConfig) WriteToUserConfig(updateConfig func(*UserConfig) error) erro
return yaml.NewEncoder(file).Encode(userConfig)
}
-// ConfigFilename returns the filename of the current config file
func (c *AppConfig) ConfigFilename() string {
return filepath.Join(c.ConfigDir, "config.yml")
}
diff --git a/pkg/config/app_config_test.go b/pkg/config/app_config_test.go
index cd8167e4..fc63cc25 100644
--- a/pkg/config/app_config_test.go
+++ b/pkg/config/app_config_test.go
@@ -7,50 +7,7 @@ import (
"github.com/jesseduffield/yaml"
)
-func TestDockerComposeCommandNoFiles(t *testing.T) {
- composeFiles := []string{}
- conf, err := NewAppConfig("name", "version", "commit", "date", "buildSource", false, composeFiles, "projectDir", "")
- if err != nil {
- t.Fatalf("Unexpected error: %s", err)
- }
-
- actual := conf.UserConfig.CommandTemplates.DockerCompose
- expected := "docker compose"
- if actual != expected {
- t.Fatalf("Expected %s but got %s", expected, actual)
- }
-}
-
-func TestDockerComposeCommandSingleFile(t *testing.T) {
- composeFiles := []string{"one.yml"}
- conf, err := NewAppConfig("name", "version", "commit", "date", "buildSource", false, composeFiles, "projectDir", "")
- if err != nil {
- t.Fatalf("Unexpected error: %s", err)
- }
-
- actual := conf.UserConfig.CommandTemplates.DockerCompose
- expected := "docker compose -f one.yml"
- if actual != expected {
- t.Fatalf("Expected %s but got %s", expected, actual)
- }
-}
-
-func TestDockerComposeCommandMultipleFiles(t *testing.T) {
- composeFiles := []string{"one.yml", "two.yml", "three.yml"}
- conf, err := NewAppConfig("name", "version", "commit", "date", "buildSource", false, composeFiles, "projectDir", "")
- if err != nil {
- t.Fatalf("Unexpected error: %s", err)
- }
-
- actual := conf.UserConfig.CommandTemplates.DockerCompose
- expected := "docker compose -f one.yml -f two.yml -f three.yml"
- if actual != expected {
- t.Fatalf("Expected %s but got %s", expected, actual)
- }
-}
-
func TestWritingToConfigFile(t *testing.T) {
- // init the AppConfig
emptyComposeFiles := []string{}
conf, err := NewAppConfig("name", "version", "commit", "date", "buildSource", false, emptyComposeFiles, "projectDir", "")
if err != nil {
@@ -90,9 +47,6 @@ func TestWritingToConfigFile(t *testing.T) {
}
}
- // insert value into an empty file
testFn(t, conf, true)
-
- // modifying an existing file that already has 'ConfirmOnQuit'
testFn(t, conf, false)
}
diff --git a/pkg/gui/app_status_manager.go b/pkg/gui/app_status_manager.go
index f806f72c..08d4c194 100644
--- a/pkg/gui/app_status_manager.go
+++ b/pkg/gui/app_status_manager.go
@@ -4,7 +4,7 @@ import (
"time"
"github.com/jesseduffield/gocui"
- "github.com/jesseduffield/lazydocker/pkg/utils"
+ "github.com/jesseduffield/lazycontainer/pkg/utils"
)
type appStatus struct {
diff --git a/pkg/gui/arrangement.go b/pkg/gui/arrangement.go
index 744f4467..12fc52fa 100644
--- a/pkg/gui/arrangement.go
+++ b/pkg/gui/arrangement.go
@@ -2,15 +2,12 @@ package gui
import (
"github.com/jesseduffield/lazycore/pkg/boxlayout"
- "github.com/jesseduffield/lazydocker/pkg/gui/panels"
- "github.com/jesseduffield/lazydocker/pkg/utils"
+ "github.com/jesseduffield/lazycontainer/pkg/gui/panels"
+ "github.com/jesseduffield/lazycontainer/pkg/utils"
"github.com/mattn/go-runewidth"
"github.com/samber/lo"
)
-// In this file we use the boxlayout package, along with knowledge about the app's state,
-// to arrange the windows (i.e. panels) on the screen.
-
const INFO_SECTION_PADDING = " "
func (gui *Gui) getWindowDimensions(informationStr string, appStatus string) map[string]boxlayout.Dimensions {
@@ -67,9 +64,7 @@ func (gui *Gui) getWindowDimensions(informationStr string, appStatus string) map
func (gui *Gui) getMidSectionWeights() (int, int) {
currentWindow := gui.currentStaticWindowName()
- // we originally specified this as a ratio i.e. .20 would correspond to a weight of 1 against 4
sidePanelWidthRatio := gui.Config.UserConfig.Gui.SidePanelWidth
- // we could make this better by creating ratios like 2:3 rather than always 1:something
mainSectionWeight := int(1/sidePanelWidthRatio) - 1
sideSectionWeight := 1
@@ -120,7 +115,6 @@ func (gui *Gui) infoSectionChildren(informationStr string, appStatus string) []*
},
{
Window: "information",
- // unlike appStatus, informationStr has various colors so we need to decolorise before taking the length
Size: runewidth.StringWidth(INFO_SECTION_PADDING) + runewidth.StringWidth(utils.Decolorise(informationStr)),
},
}...,
@@ -143,6 +137,10 @@ func (gui *Gui) sidePanelChildren(width int, height int) []*boxlayout.Box {
currentWindow := gui.currentSideWindowName()
sideWindowNames := gui.sideViewNames()
+ if len(sideWindowNames) == 0 {
+ return []*boxlayout.Box{}
+ }
+
if gui.State.ScreenMode == SCREEN_FULL || gui.State.ScreenMode == SCREEN_HALF {
fullHeightBox := func(window string) *boxlayout.Box {
if window == currentWindow {
@@ -162,7 +160,7 @@ func (gui *Gui) sidePanelChildren(width int, height int) []*boxlayout.Box {
return fullHeightBox(window)
})
- } else if height >= 28 {
+ } else if height >= 20 {
accordionMode := gui.Config.UserConfig.Gui.ExpandFocusedSidePanel
accordionBox := func(defaultBox *boxlayout.Box) *boxlayout.Box {
if accordionMode && defaultBox.Window == currentWindow {
@@ -175,27 +173,12 @@ func (gui *Gui) sidePanelChildren(width int, height int) []*boxlayout.Box {
return defaultBox
}
- // The project panel is compact (Size: 3) when not focused, but expands
- // when focused to show the list of projects.
- projectBox := &boxlayout.Box{
- Window: sideWindowNames[0],
- Size: 3,
- }
- if currentWindow == sideWindowNames[0] {
- projectBox = &boxlayout.Box{
- Window: sideWindowNames[0],
- Weight: 2,
- }
- }
-
- return append([]*boxlayout.Box{
- projectBox,
- }, lo.Map(sideWindowNames[1:], func(window string, _ int) *boxlayout.Box {
+ return lo.Map(sideWindowNames, func(window string, _ int) *boxlayout.Box {
return accordionBox(&boxlayout.Box{Window: window, Weight: 1})
- })...)
+ })
} else {
squashedHeight := 1
- if height >= 21 {
+ if height >= 12 {
squashedHeight = 3
}
diff --git a/pkg/gui/container_logs.go b/pkg/gui/container_logs.go
index bdb81608..bc720cb1 100644
--- a/pkg/gui/container_logs.go
+++ b/pkg/gui/container_logs.go
@@ -1,19 +1,19 @@
package gui
import (
+ "bufio"
"context"
"fmt"
"io"
"os"
+ "os/exec"
"os/signal"
"time"
- "github.com/docker/docker/api/types/container"
- "github.com/docker/docker/pkg/stdcopy"
"github.com/fatih/color"
- "github.com/jesseduffield/lazydocker/pkg/commands"
- "github.com/jesseduffield/lazydocker/pkg/tasks"
- "github.com/jesseduffield/lazydocker/pkg/utils"
+ "github.com/jesseduffield/lazycontainer/pkg/commands"
+ "github.com/jesseduffield/lazycontainer/pkg/tasks"
+ "github.com/jesseduffield/lazycontainer/pkg/utils"
)
func (gui *Gui) renderContainerLogsToMain(container *commands.Container) tasks.TaskFunc {
@@ -22,7 +22,6 @@ func (gui *Gui) renderContainerLogsToMain(container *commands.Container) tasks.T
gui.renderContainerLogsToMainAux(container, ctx, notifyStopped)
},
Duration: time.Millisecond * 200,
- // TODO: see why this isn't working (when switching from Top tab to Logs tab in the services panel, the tops tab's content isn't removed)
Before: func(ctx context.Context) { gui.clearMainView() },
Wrap: gui.Config.UserConfig.Gui.WrapMainPanel,
Autoscroll: true,
@@ -41,8 +40,6 @@ func (gui *Gui) renderContainerLogsToMainAux(container *commands.Container, ctx
gui.Log.Error(err)
}
- // if we are here because the task has been stopped, we should return
- // if we are here then the container must have exited, meaning we should wait until it's back again before
ticker := time.NewTicker(time.Millisecond * 100)
defer ticker.Stop()
for {
@@ -52,11 +49,10 @@ func (gui *Gui) renderContainerLogsToMainAux(container *commands.Container, ctx
case <-ticker.C:
result, err := container.Inspect()
if err != nil {
- // if we get an error, then the container has probably been removed so we'll get out of here
gui.Log.Error(err)
return
}
- if result.State.Running {
+ if result.Status == "running" {
return
}
}
@@ -97,7 +93,6 @@ func (gui *Gui) promptToReturn() {
if !gui.Config.UserConfig.Gui.ReturnImmediately {
fmt.Fprintf(os.Stdout, "\n\n%s", utils.ColoredString(gui.Tr.PressEnterToReturn, color.FgGreen))
- // wait for enter press
if _, err := fmt.Scanln(); err != nil {
gui.Log.Error(err)
}
@@ -105,48 +100,71 @@ func (gui *Gui) promptToReturn() {
}
func (gui *Gui) writeContainerLogs(ctr *commands.Container, ctx context.Context, writer io.Writer) error {
- readCloser, err := gui.DockerCommand.Client.ContainerLogs(ctx, ctr.ID, container.LogsOptions{
- ShowStdout: true,
- ShowStderr: true,
- Timestamps: gui.Config.UserConfig.Logs.Timestamps,
- Since: gui.Config.UserConfig.Logs.Since,
- Tail: gui.Config.UserConfig.Logs.Tail,
- Follow: true,
- })
+ tail := 100
+ if gui.Config.UserConfig.Logs.Tail != "" {
+ fmt.Sscanf(gui.Config.UserConfig.Logs.Tail, "%d", &tail)
+ }
+
+ cmd := gui.ContainerCmd.Client.LogsContainer(ctr.ID, true, tail)
+
+ stdoutPipe, err := cmd.StdoutPipe()
+ if err != nil {
+ gui.Log.Error(err)
+ return err
+ }
+ stderrPipe, err := cmd.StderrPipe()
if err != nil {
gui.Log.Error(err)
return err
}
- defer readCloser.Close()
- if !ctr.DetailsLoaded() {
- // loop until the details load or context is cancelled, using timer
- ticker := time.NewTicker(time.Millisecond * 100)
- defer ticker.Stop()
- outer:
- for {
+ if err := cmd.Start(); err != nil {
+ gui.Log.Error(err)
+ return err
+ }
+
+ done := make(chan struct{})
+
+ go func() {
+ scanner := bufio.NewScanner(stdoutPipe)
+ for scanner.Scan() {
select {
case <-ctx.Done():
- return nil
- case <-ticker.C:
- if ctr.DetailsLoaded() {
- break outer
- }
+ return
+ default:
+ fmt.Fprintln(writer, scanner.Text())
}
}
- }
+ }()
- if ctr.Details.Config.Tty {
- _, err = io.Copy(writer, readCloser)
- if err != nil {
- return err
+ go func() {
+ scanner := bufio.NewScanner(stderrPipe)
+ for scanner.Scan() {
+ select {
+ case <-ctx.Done():
+ return
+ default:
+ fmt.Fprintln(writer, scanner.Text())
+ }
}
- } else {
- _, err = stdcopy.StdCopy(writer, writer, readCloser)
- if err != nil {
- return err
- }
- }
+ }()
- return nil
+ go func() {
+ cmd.Wait()
+ close(done)
+ }()
+
+ select {
+ case <-ctx.Done():
+ killCmd(cmd)
+ return nil
+ case <-done:
+ return nil
+ }
+}
+
+func killCmd(cmd *exec.Cmd) {
+ if cmd.Process != nil {
+ cmd.Process.Kill()
+ }
}
diff --git a/pkg/gui/containers_panel.go b/pkg/gui/containers_panel.go
index 4bde93ec..cc41b45e 100644
--- a/pkg/gui/containers_panel.go
+++ b/pkg/gui/containers_panel.go
@@ -6,31 +6,16 @@ import (
"strings"
"time"
- "github.com/docker/docker/api/types/container"
"github.com/fatih/color"
"github.com/jesseduffield/gocui"
- "github.com/jesseduffield/lazydocker/pkg/commands"
- "github.com/jesseduffield/lazydocker/pkg/config"
- "github.com/jesseduffield/lazydocker/pkg/gui/panels"
- "github.com/jesseduffield/lazydocker/pkg/gui/presentation"
- "github.com/jesseduffield/lazydocker/pkg/gui/types"
- "github.com/jesseduffield/lazydocker/pkg/tasks"
- "github.com/jesseduffield/lazydocker/pkg/utils"
- "github.com/samber/lo"
+ "github.com/jesseduffield/lazycontainer/pkg/commands"
+ "github.com/jesseduffield/lazycontainer/pkg/gui/panels"
+ "github.com/jesseduffield/lazycontainer/pkg/gui/presentation"
+ "github.com/jesseduffield/lazycontainer/pkg/tasks"
+ "github.com/jesseduffield/lazycontainer/pkg/utils"
)
func (gui *Gui) getContainersPanel() *panels.SideListPanel[*commands.Container] {
- // Standalone containers are containers which are either one-off containers, or whose service is not part of this docker-compose context.
- isStandaloneContainer := func(container *commands.Container) bool {
- if container.OneOff || container.ServiceName == "" {
- return true
- }
-
- return !lo.SomeBy(gui.Panels.Services.List.GetAllItems(), func(service *commands.Service) bool {
- return service.Name == container.ServiceName && service.ProjectName == container.ProjectName
- })
- }
-
return &panels.SideListPanel[*commands.Container]{
ContextState: &panels.ContextState[*commands.Container]{
GetMainTabs: func() []panels.MainTab[*commands.Container] {
@@ -63,12 +48,7 @@ func (gui *Gui) getContainersPanel() *panels.SideListPanel[*commands.Container]
}
},
GetItemContextCacheKey: func(container *commands.Container) string {
- // Including the container state in the cache key so that if the container
- // restarts we re-read the logs. In the past we've had some glitchiness
- // where a container restarts but the new logs don't get read.
- // Note that this might be jarring if we have a lot of logs and the container
- // restarts a lot, so let's keep an eye on it.
- return "containers-" + container.ID + "-" + container.Container.State
+ return "containers-" + container.ID + "-" + container.GetStatus()
},
},
ListPanel: panels.ListPanel[*commands.Container]{
@@ -77,33 +57,13 @@ func (gui *Gui) getContainersPanel() *panels.SideListPanel[*commands.Container]
},
NoItemsMessage: gui.Tr.NoContainers,
Gui: gui.intoInterface(),
- // sortedContainers returns containers sorted by state if c.SortContainersByState is true (follows 1- running, 2- exited, 3- created)
- // and sorted by name if c.SortContainersByState is false
Sort: func(a *commands.Container, b *commands.Container) bool {
return sortContainers(a, b, gui.Config.UserConfig.Gui.LegacySortContainers)
},
Filter: func(container *commands.Container) bool {
- // Note that this is O(N*M) time complexity where N is the number of services
- // and M is the number of containers. We expect N to be small but M may be large,
- // so we will need to keep an eye on this.
- if !gui.Config.UserConfig.Gui.ShowAllContainers && !isStandaloneContainer(container) {
+ if !gui.State.ShowExitedContainers && container.GetStatus() != "running" {
return false
}
-
- if !gui.State.ShowExitedContainers && container.Container.State == "exited" {
- return false
- }
-
- // Filter by selected project. Containers with no project (truly
- // standalone, not from any compose project) are always shown.
- selectedProject := gui.getSelectedProjectName()
- if selectedProject == "" {
- selectedProject = gui.DockerCommand.LocalProjectName
- }
- if selectedProject != "" && container.ProjectName != "" && container.ProjectName != selectedProject {
- return false
- }
-
return true
},
GetTableCells: func(container *commands.Container) []string {
@@ -114,6 +74,7 @@ func (gui *Gui) getContainersPanel() *panels.SideListPanel[*commands.Container]
var containerStates = map[string]int{
"running": 1,
+ "stopped": 2,
"exited": 2,
"created": 3,
}
@@ -123,13 +84,13 @@ func sortContainers(a *commands.Container, b *commands.Container, legacySort boo
return a.Name < b.Name
}
- stateLeft := containerStates[a.Container.State]
- stateRight := containerStates[b.Container.State]
+ stateLeft := containerStates[a.GetStatus()]
+ stateRight := containerStates[b.GetStatus()]
if stateLeft == stateRight {
return a.Name < b.Name
}
- return containerStates[a.Container.State] < containerStates[b.Container.State]
+ return stateLeft < stateRight
}
func (gui *Gui) renderContainerEnv(container *commands.Container) tasks.TaskFunc {
@@ -137,105 +98,113 @@ func (gui *Gui) renderContainerEnv(container *commands.Container) tasks.TaskFunc
}
func (gui *Gui) containerEnv(container *commands.Container) string {
- if !container.DetailsLoaded() {
- return gui.Tr.WaitingForContainerInfo
- }
-
- if len(container.Details.Config.Env) == 0 {
+ env := container.GetEnv()
+ if len(env) == 0 {
return gui.Tr.NothingToDisplay
}
- envVarsList := lo.Map(container.Details.Config.Env, func(envVar string, _ int) []string {
+ var lines []string
+ for _, envVar := range env {
splitEnv := strings.SplitN(envVar, "=", 2)
key := splitEnv[0]
value := ""
if len(splitEnv) > 1 {
value = splitEnv[1]
}
- return []string{
- utils.ColoredString(key+":", color.FgGreen),
+ lines = append(lines, fmt.Sprintf("%s=%s",
+ utils.ColoredString(key, color.FgGreen),
utils.ColoredString(value, color.FgYellow),
- }
- })
-
- output, err := utils.RenderTable(envVarsList)
- if err != nil {
- gui.Log.Error(err)
- return gui.Tr.CannotDisplayEnvVariables
+ ))
}
- return output
+ return strings.Join(lines, "\n")
}
func (gui *Gui) renderContainerConfig(container *commands.Container) tasks.TaskFunc {
- return gui.NewSimpleRenderStringTask(func() string { return gui.containerConfigStr(container) })
+ return gui.NewSimpleRenderStringTask(func() string { return gui.containerConfig(container) })
}
-func (gui *Gui) containerConfigStr(container *commands.Container) string {
- if !container.DetailsLoaded() {
- return gui.Tr.WaitingForContainerInfo
+func (gui *Gui) containerConfig(container *commands.Container) string {
+ var lines []string
+
+ lines = append(lines, fmt.Sprintf("%s: %s",
+ utils.ColoredString("ID", color.FgCyan),
+ container.ID,
+ ))
+ lines = append(lines, fmt.Sprintf("%s: %s",
+ utils.ColoredString("Name", color.FgCyan),
+ container.Name,
+ ))
+ lines = append(lines, fmt.Sprintf("%s: %s",
+ utils.ColoredString("Image", color.FgCyan),
+ container.GetImage(),
+ ))
+ lines = append(lines, fmt.Sprintf("%s: %s",
+ utils.ColoredString("Status", color.FgCyan),
+ container.GetStatus(),
+ ))
+ lines = append(lines, fmt.Sprintf("%s: %s",
+ utils.ColoredString("IP", color.FgCyan),
+ container.GetIP(),
+ ))
+ lines = append(lines, fmt.Sprintf("%s: %d",
+ utils.ColoredString("CPUs", color.FgCyan),
+ container.GetCPUs(),
+ ))
+ lines = append(lines, fmt.Sprintf("%s: %s",
+ utils.ColoredString("Memory", color.FgCyan),
+ utils.FormatBinaryBytes(int(container.GetMemory())),
+ ))
+
+ ports := container.GetPorts()
+ if len(ports) > 0 {
+ lines = append(lines, fmt.Sprintf("%s: %s",
+ utils.ColoredString("Ports", color.FgCyan),
+ strings.Join(ports, ", "),
+ ))
}
- padding := 10
- output := ""
- output += utils.WithPadding("ID: ", padding) + container.ID + "\n"
- output += utils.WithPadding("Name: ", padding) + container.Name + "\n"
- output += utils.WithPadding("Image: ", padding) + container.Details.Config.Image + "\n"
- output += utils.WithPadding("Command: ", padding) + strings.Join(append([]string{container.Details.Path}, container.Details.Args...), " ") + "\n"
- output += utils.WithPadding("Labels: ", padding) + utils.FormatMap(padding, container.Details.Config.Labels)
- output += "\n"
-
- output += utils.WithPadding("Mounts: ", padding)
- if len(container.Details.Mounts) > 0 {
- output += "\n"
- for _, mount := range container.Details.Mounts {
- if mount.Type == "volume" {
- output += fmt.Sprintf("%s%s %s\n", strings.Repeat(" ", padding), utils.ColoredString(string(mount.Type)+":", color.FgYellow), mount.Name)
- } else {
- output += fmt.Sprintf("%s%s %s:%s\n", strings.Repeat(" ", padding), utils.ColoredString(string(mount.Type)+":", color.FgYellow), mount.Source, mount.Destination)
- }
- }
- } else {
- output += "none\n"
- }
-
- output += utils.WithPadding("Ports: ", padding)
- if len(container.Details.NetworkSettings.Ports) > 0 {
- output += "\n"
- for k, v := range container.Details.NetworkSettings.Ports {
- for _, host := range v {
- output += fmt.Sprintf("%s%s %s\n", strings.Repeat(" ", padding), utils.ColoredString(host.HostPort+":", color.FgYellow), k)
- }
- }
- } else {
- output += "none\n"
- }
-
- data, err := utils.MarshalIntoYaml(&container.Details)
- if err != nil {
- return fmt.Sprintf("Error marshalling container details: %v", err)
- }
-
- output += fmt.Sprintf("\nFull details:\n\n%s", utils.ColoredYamlString(string(data)))
-
- return output
+ return strings.Join(lines, "\n")
}
func (gui *Gui) renderContainerStats(container *commands.Container) tasks.TaskFunc {
- return gui.NewTickerTask(TickerTaskOpts{
- Func: func(ctx context.Context, notifyStopped chan struct{}) {
- contents, err := presentation.RenderStats(gui.Config.UserConfig, container, gui.Views.Main.Width())
- if err != nil {
- _ = gui.createErrorPanel(err.Error())
- }
+ return gui.NewSimpleRenderStringTask(func() string { return gui.containerStats(container) })
+}
- gui.reRenderStringMain(contents)
- },
- Duration: time.Second,
- Before: func(ctx context.Context) { gui.clearMainView() },
- Wrap: false, // wrapping looks bad here so we're overriding the config value
- Autoscroll: false,
- })
+func (gui *Gui) containerStats(container *commands.Container) string {
+ stats, ok := container.GetLastStats()
+ if !ok {
+ return gui.Tr.NoStats
+ }
+
+ var lines []string
+ lines = append(lines, fmt.Sprintf("%s: %.1f%%",
+ utils.ColoredString("CPU", color.FgCyan),
+ stats.DerivedStats.CPUPercentage,
+ ))
+ lines = append(lines, fmt.Sprintf("%s: %.1f%% (%s / %s)",
+ utils.ColoredString("Memory",
+ color.FgCyan),
+ stats.DerivedStats.MemoryPercentage,
+ utils.FormatBinaryBytes(int(stats.ClientStats.MemoryUsageBytes)),
+ utils.FormatBinaryBytes(int(stats.ClientStats.MemoryLimitBytes)),
+ ))
+ lines = append(lines, fmt.Sprintf("%s: %s / %s",
+ utils.ColoredString("Network I/O", color.FgCyan),
+ utils.FormatBinaryBytes(int(stats.ClientStats.NetworkRxBytes)),
+ utils.FormatBinaryBytes(int(stats.ClientStats.NetworkTxBytes)),
+ ))
+ lines = append(lines, fmt.Sprintf("%s: %s / %s",
+ utils.ColoredString("Block I/O", color.FgCyan),
+ utils.FormatBinaryBytes(int(stats.ClientStats.BlockReadBytes)),
+ utils.FormatBinaryBytes(int(stats.ClientStats.BlockWriteBytes)),
+ ))
+ lines = append(lines, fmt.Sprintf("%s: %d",
+ utils.ColoredString("Processes", color.FgCyan),
+ stats.ClientStats.NumProcesses,
+ ))
+
+ return strings.Join(lines, "\n")
}
func (gui *Gui) renderContainerTop(container *commands.Container) tasks.TaskFunc {
@@ -255,176 +224,94 @@ func (gui *Gui) renderContainerTop(container *commands.Container) tasks.TaskFunc
})
}
-func (gui *Gui) refreshContainersAndServices() error {
- if gui.Views.Containers == nil {
- // if the containersView hasn't been instantiated yet we just return
- return nil
- }
-
- // keep track of current service selected so that we can reposition our cursor if it moves position in the list
- originalSelectedLineIdx := gui.Panels.Services.SelectedIdx
- selectedService, isServiceSelected := gui.Panels.Services.List.TryGet(originalSelectedLineIdx)
-
- containers, services, err := gui.DockerCommand.RefreshContainersAndServices(
- gui.Panels.Containers.List.GetAllItems(),
- )
- if err != nil {
- return err
- }
-
- gui.Panels.Services.SetItems(services)
+func (gui *Gui) refreshContainers() error {
+ containers := gui.ContainerCmd.RefreshContainers(gui.Panels.Containers.List.GetAllItems())
gui.Panels.Containers.SetItems(containers)
-
- // see if our selected service has moved
- if isServiceSelected {
- for i, service := range gui.Panels.Services.List.GetItems() {
- if service.ID == selectedService.ID {
- if i == originalSelectedLineIdx {
- break
- }
- gui.Panels.Services.SetSelectedLineIdx(i)
- gui.Panels.Services.Refocus()
- }
- }
- }
-
- return gui.renderContainersAndServices()
-}
-
-func (gui *Gui) renderContainersAndServices() error {
- if err := gui.Panels.Services.RerenderList(); err != nil {
- return err
- }
-
- if err := gui.Panels.Containers.RerenderList(); err != nil {
- return err
- }
-
- return nil
+ return gui.Panels.Containers.RerenderList()
}
func (gui *Gui) handleHideStoppedContainers(g *gocui.Gui, v *gocui.View) error {
gui.State.ShowExitedContainers = !gui.State.ShowExitedContainers
-
- return gui.Panels.Containers.RerenderList()
-}
-
-func (gui *Gui) handleContainersRemoveMenu(g *gocui.Gui, v *gocui.View) error {
- ctr, err := gui.Panels.Containers.GetSelectedItem()
- if err != nil {
- return nil
- }
-
- handleMenuPress := func(configOptions container.RemoveOptions) error {
- return gui.WithWaitingStatus(gui.Tr.RemovingStatus, func() error {
- if err := ctr.Remove(configOptions); err != nil {
- if commands.HasErrorCode(err, commands.MustStopContainer) {
- return gui.createConfirmationPanel(gui.Tr.Confirm, gui.Tr.MustForceToRemoveContainer, func(g *gocui.Gui, v *gocui.View) error {
- return gui.WithWaitingStatus(gui.Tr.RemovingStatus, func() error {
- configOptions.Force = true
- return ctr.Remove(configOptions)
- })
- }, nil)
- }
- return gui.createErrorPanel(err.Error())
- }
- return nil
- })
- }
-
- menuItems := []*types.MenuItem{
- {
- LabelColumns: []string{gui.Tr.Remove, "docker rm " + ctr.ID[1:10]},
- OnPress: func() error { return handleMenuPress(container.RemoveOptions{}) },
- },
- {
- LabelColumns: []string{gui.Tr.RemoveWithVolumes, "docker rm --volumes " + ctr.ID[1:10]},
- OnPress: func() error { return handleMenuPress(container.RemoveOptions{RemoveVolumes: true}) },
- },
- }
-
- return gui.Menu(CreateMenuOptions{
- Title: "",
- Items: menuItems,
- })
-}
-
-func (gui *Gui) PauseContainer(container *commands.Container) error {
- return gui.WithWaitingStatus(gui.Tr.PausingStatus, func() (err error) {
- if container.Details.State.Paused {
- err = container.Unpause()
- } else {
- err = container.Pause()
- }
-
- if err != nil {
- return gui.createErrorPanel(err.Error())
- }
-
- return gui.refreshContainersAndServices()
- })
-}
-
-func (gui *Gui) handleContainerPause(g *gocui.Gui, v *gocui.View) error {
- ctr, err := gui.Panels.Containers.GetSelectedItem()
- if err != nil {
- return nil
- }
-
- return gui.PauseContainer(ctr)
+ return gui.refreshContainers()
}
func (gui *Gui) handleContainerStop(g *gocui.Gui, v *gocui.View) error {
- ctr, err := gui.Panels.Containers.GetSelectedItem()
+ container, err := gui.Panels.Containers.GetSelectedItem()
if err != nil {
return nil
}
return gui.createConfirmationPanel(gui.Tr.Confirm, gui.Tr.StopContainer, func(g *gocui.Gui, v *gocui.View) error {
return gui.WithWaitingStatus(gui.Tr.StoppingStatus, func() error {
- if err := ctr.Stop(); err != nil {
+ if err := container.Stop(); err != nil {
return gui.createErrorPanel(err.Error())
}
-
return nil
})
}, nil)
}
func (gui *Gui) handleContainerRestart(g *gocui.Gui, v *gocui.View) error {
- ctr, err := gui.Panels.Containers.GetSelectedItem()
+ container, err := gui.Panels.Containers.GetSelectedItem()
if err != nil {
return nil
}
return gui.WithWaitingStatus(gui.Tr.RestartingStatus, func() error {
- if err := ctr.Restart(); err != nil {
+ if err := container.Restart(); err != nil {
return gui.createErrorPanel(err.Error())
}
-
return nil
})
}
func (gui *Gui) handleContainerAttach(g *gocui.Gui, v *gocui.View) error {
- ctr, err := gui.Panels.Containers.GetSelectedItem()
+ container, err := gui.Panels.Containers.GetSelectedItem()
if err != nil {
return nil
}
- c, err := ctr.Attach()
+ c, err := container.Attach()
if err != nil {
return gui.createErrorPanel(err.Error())
}
- return gui.runSubprocessWithMessage(c, gui.Tr.DetachFromContainerShortCut)
+ return gui.runSubprocess(c)
}
-func (gui *Gui) handlePruneContainers() error {
- return gui.createConfirmationPanel(gui.Tr.Confirm, gui.Tr.ConfirmPruneContainers, func(g *gocui.Gui, v *gocui.View) error {
- return gui.WithWaitingStatus(gui.Tr.PruningStatus, func() error {
- err := gui.DockerCommand.PruneContainers()
- if err != nil {
+func (gui *Gui) handleContainerViewLogs(g *gocui.Gui, v *gocui.View) error {
+ container, err := gui.Panels.Containers.GetSelectedItem()
+ if err != nil {
+ return nil
+ }
+
+ cmd := gui.ContainerCmd.Client.LogsContainer(container.ID, true, 0)
+ return gui.runSubprocess(cmd)
+}
+
+func (gui *Gui) handleContainerKill(g *gocui.Gui, v *gocui.View) error {
+ container, err := gui.Panels.Containers.GetSelectedItem()
+ if err != nil {
+ return nil
+ }
+
+ return gui.WithWaitingStatus(gui.Tr.KillingStatus, func() error {
+ if err := container.Kill(""); err != nil {
+ return gui.createErrorPanel(err.Error())
+ }
+ return nil
+ })
+}
+
+func (gui *Gui) handleContainersRemoveMenu(g *gocui.Gui, v *gocui.View) error {
+ container, err := gui.Panels.Containers.GetSelectedItem()
+ if err != nil {
+ return nil
+ }
+
+ return gui.createConfirmationPanel(gui.Tr.Confirm, gui.Tr.RemoveContainer, func(g *gocui.Gui, v *gocui.View) error {
+ return gui.WithWaitingStatus(gui.Tr.RemovingStatus, func() error {
+ if err := container.Remove(true); err != nil {
return gui.createErrorPanel(err.Error())
}
return nil
@@ -432,46 +319,33 @@ func (gui *Gui) handlePruneContainers() error {
}, nil)
}
-func (gui *Gui) handleContainerViewLogs(g *gocui.Gui, v *gocui.View) error {
- ctr, err := gui.Panels.Containers.GetSelectedItem()
- if err != nil {
- return nil
- }
-
- gui.renderLogsToStdout(ctr)
-
- return nil
-}
-
func (gui *Gui) handleContainersExecShell(g *gocui.Gui, v *gocui.View) error {
- ctr, err := gui.Panels.Containers.GetSelectedItem()
+ container, err := gui.Panels.Containers.GetSelectedItem()
if err != nil {
return nil
}
- return gui.containerExecShell(ctr)
-}
+ if container.GetStatus() != "running" {
+ return gui.createErrorPanel(gui.Tr.ContainerNotRunning)
+ }
-func (gui *Gui) containerExecShell(container *commands.Container) error {
- commandObject := gui.DockerCommand.NewCommandObject(commands.CommandObject{
- Container: container,
+ cmd := gui.ContainerCmd.Client.ExecContainer(container.ID, commands.ExecOptions{
+ Command: []string{"/bin/sh"},
+ Interactive: true,
+ TTY: true,
})
- // TODO: use SDK
- resolvedCommand := utils.ApplyTemplate("docker exec -it {{ .Container.ID }} /bin/sh -c 'eval $(grep ^$(id -un): /etc/passwd | cut -d : -f 7-)'", commandObject)
- // attach and return the subprocess error
- cmd := gui.OSCommand.ExecutableFromString(resolvedCommand)
return gui.runSubprocess(cmd)
}
func (gui *Gui) handleContainersCustomCommand(g *gocui.Gui, v *gocui.View) error {
- ctr, err := gui.Panels.Containers.GetSelectedItem()
+ container, err := gui.Panels.Containers.GetSelectedItem()
if err != nil {
return nil
}
- commandObject := gui.DockerCommand.NewCommandObject(commands.CommandObject{
- Container: ctr,
+ commandObject := gui.ContainerCmd.NewCommandObject(commands.CommandObject{
+ Container: container,
})
customCommands := gui.Config.UserConfig.CustomCommands.Containers
@@ -479,80 +353,41 @@ func (gui *Gui) handleContainersCustomCommand(g *gocui.Gui, v *gocui.View) error
return gui.createCustomCommandMenu(customCommands, commandObject)
}
-func (gui *Gui) handleStopContainers() error {
- return gui.createConfirmationPanel(gui.Tr.Confirm, gui.Tr.ConfirmStopContainers, func(g *gocui.Gui, v *gocui.View) error {
- return gui.WithWaitingStatus(gui.Tr.StoppingStatus, func() error {
- for _, ctr := range gui.Panels.Containers.List.GetAllItems() {
- if err := ctr.Stop(); err != nil {
- gui.Log.Error(err)
- }
- }
-
- return nil
- })
- }, nil)
-}
-
-func (gui *Gui) handleRemoveContainers() error {
- return gui.createConfirmationPanel(gui.Tr.Confirm, gui.Tr.ConfirmRemoveContainers, func(g *gocui.Gui, v *gocui.View) error {
- return gui.WithWaitingStatus(gui.Tr.RemovingStatus, func() error {
- for _, ctr := range gui.Panels.Containers.List.GetAllItems() {
- if err := ctr.Remove(container.RemoveOptions{Force: true}); err != nil {
- gui.Log.Error(err)
- }
- }
-
- return nil
- })
- }, nil)
-}
-
func (gui *Gui) handleContainersBulkCommand(g *gocui.Gui, v *gocui.View) error {
- baseBulkCommands := []config.CustomCommand{
- {
- Name: gui.Tr.StopAllContainers,
- InternalFunction: gui.handleStopContainers,
- },
- {
- Name: gui.Tr.RemoveAllContainers,
- InternalFunction: gui.handleRemoveContainers,
- },
- {
- Name: gui.Tr.PruneContainers,
- InternalFunction: gui.handlePruneContainers,
- },
- }
-
- bulkCommands := append(baseBulkCommands, gui.Config.UserConfig.BulkCommands.Containers...)
- commandObject := gui.DockerCommand.NewCommandObject(commands.CommandObject{})
+ bulkCommands := gui.Config.UserConfig.BulkCommands.Containers
+ commandObject := gui.ContainerCmd.NewCommandObject(commands.CommandObject{})
return gui.createBulkCommandMenu(bulkCommands, commandObject)
}
-// Open first port in browser
func (gui *Gui) handleContainersOpenInBrowserCommand(g *gocui.Gui, v *gocui.View) error {
- ctr, err := gui.Panels.Containers.GetSelectedItem()
+ container, err := gui.Panels.Containers.GetSelectedItem()
if err != nil {
return nil
}
- return gui.openContainerInBrowser(ctr)
+ ip := container.GetIP()
+ if ip == "" {
+ return gui.createErrorPanel(gui.Tr.NoIp)
+ }
+
+ ports := container.GetPorts()
+ if len(ports) == 0 {
+ return gui.createErrorPanel(gui.Tr.NoPorts)
+ }
+
+ for _, port := range ports {
+ parts := strings.Split(port, ":")
+ if len(parts) >= 2 {
+ hostPort := strings.Split(parts[0], "->")[0]
+ url := fmt.Sprintf("http://%s:%s", ip, hostPort)
+ return gui.OSCommand.OpenLink(url)
+ }
+ }
+
+ return nil
}
-func (gui *Gui) openContainerInBrowser(ctr *commands.Container) error {
- // skip if no any ports
- if len(ctr.Container.Ports) == 0 {
- return nil
- }
- // skip if the first port is not published
- port := ctr.Container.Ports[0]
- if port.IP == "" {
- return nil
- }
- ip := port.IP
- if ip == "0.0.0.0" {
- ip = "localhost"
- }
- link := fmt.Sprintf("http://%s:%d/", ip, port.PublicPort)
- return gui.OSCommand.OpenLink(link)
+func (gui *Gui) PauseContainer(container *commands.Container) error {
+ return gui.createErrorPanel("Pause is not supported by Apple Container")
}
diff --git a/pkg/gui/custom_commands.go b/pkg/gui/custom_commands.go
index f9981607..82de9185 100644
--- a/pkg/gui/custom_commands.go
+++ b/pkg/gui/custom_commands.go
@@ -2,10 +2,10 @@ package gui
import (
"github.com/fatih/color"
- "github.com/jesseduffield/lazydocker/pkg/commands"
- "github.com/jesseduffield/lazydocker/pkg/config"
- "github.com/jesseduffield/lazydocker/pkg/gui/types"
- "github.com/jesseduffield/lazydocker/pkg/utils"
+ "github.com/jesseduffield/lazycontainer/pkg/commands"
+ "github.com/jesseduffield/lazycontainer/pkg/config"
+ "github.com/jesseduffield/lazycontainer/pkg/gui/types"
+ "github.com/jesseduffield/lazycontainer/pkg/utils"
"github.com/samber/lo"
)
diff --git a/pkg/gui/gocui.go b/pkg/gui/gocui.go
index e4c0b155..a58e865f 100644
--- a/pkg/gui/gocui.go
+++ b/pkg/gui/gocui.go
@@ -3,7 +3,7 @@ package gui
import (
"github.com/gookit/color"
"github.com/jesseduffield/gocui"
- "github.com/jesseduffield/lazydocker/pkg/utils"
+ "github.com/jesseduffield/lazycontainer/pkg/utils"
)
var gocuiColorMap = map[string]gocui.Attribute{
diff --git a/pkg/gui/gui.go b/pkg/gui/gui.go
index e7b55463..55340d9e 100644
--- a/pkg/gui/gui.go
+++ b/pkg/gui/gui.go
@@ -6,40 +6,32 @@ import (
"strings"
"time"
- "github.com/docker/docker/api/types/events"
-
- "github.com/go-errors/errors"
-
throttle "github.com/boz/go-throttle"
"github.com/jesseduffield/gocui"
lcUtils "github.com/jesseduffield/lazycore/pkg/utils"
- "github.com/jesseduffield/lazydocker/pkg/commands"
- "github.com/jesseduffield/lazydocker/pkg/config"
- "github.com/jesseduffield/lazydocker/pkg/gui/panels"
- "github.com/jesseduffield/lazydocker/pkg/gui/types"
- "github.com/jesseduffield/lazydocker/pkg/i18n"
- "github.com/jesseduffield/lazydocker/pkg/tasks"
+ "github.com/jesseduffield/lazycontainer/pkg/commands"
+ "github.com/jesseduffield/lazycontainer/pkg/config"
+ "github.com/jesseduffield/lazycontainer/pkg/gui/panels"
+ "github.com/jesseduffield/lazycontainer/pkg/gui/types"
+ "github.com/jesseduffield/lazycontainer/pkg/i18n"
+ "github.com/jesseduffield/lazycontainer/pkg/tasks"
"github.com/sasha-s/go-deadlock"
"github.com/sirupsen/logrus"
)
-// Gui wraps the gocui Gui object which handles rendering and events
type Gui struct {
- g *gocui.Gui
- Log *logrus.Entry
- DockerCommand *commands.DockerCommand
- OSCommand *commands.OSCommand
- State guiState
- Config *config.AppConfig
- Tr *i18n.TranslationSet
- statusManager *statusManager
- taskManager *tasks.TaskManager
- ErrorChan chan error
- Views Views
+ g *gocui.Gui
+ Log *logrus.Entry
+ ContainerCmd *commands.ContainerCommand
+ OSCommand *commands.OSCommand
+ State guiState
+ Config *config.AppConfig
+ Tr *i18n.TranslationSet
+ statusManager *statusManager
+ taskManager *tasks.TaskManager
+ ErrorChan chan error
+ Views Views
- // if we've suspended the gui (e.g. because we've switched to a subprocess)
- // we typically want to pause some things that are running like background
- // file refreshes
PauseBackgroundThreads bool
Mutexes
@@ -48,8 +40,6 @@ type Gui struct {
}
type Panels struct {
- Projects *panels.SideListPanel[*commands.Project]
- Services *panels.SideListPanel[*commands.Service]
Containers *panels.SideListPanel[*commands.Container]
Images *panels.SideListPanel[*commands.Image]
Volumes *panels.SideListPanel[*commands.Volume]
@@ -63,7 +53,6 @@ type Mutexes struct {
}
type mainPanelState struct {
- // ObjectKey tells us what context we are in. For example, if we are looking at the logs of a particular service in the services panel this key might be 'services--logs'. The key is made so that if something changes which might require us to re-run the logs command or run a different command, the key will be different, and we'll then know to do whatever is required. Object key probably isn't the best name for this but Context is already used to refer to tabs. Maybe I should just call them tabs.
ObjectKey string
}
@@ -72,37 +61,25 @@ type panelStates struct {
}
type guiState struct {
- // the names of views in the current focus stack (last item is the current view)
ViewStack []string
Platform commands.Platform
Panels *panelStates
SubProcessOutput string
Stats map[string]commands.ContainerStats
- // if true, we show containers with an 'exited' status in the containers panel
ShowExitedContainers bool
ScreenMode WindowMaximisation
- // Maintains the state of manual filtering i.e. typing in a substring
- // to filter on in the current panel.
Filter filterState
}
type filterState struct {
- // If true then we're either currently inside the filter view
- // or we've committed the filter and we're back in the list view
active bool
- // The panel that we're filtering.
- panel panels.ISideListPanel
- // The string that we're filtering on
+ panel panels.ISideListPanel
needle string
}
-// screen sizing determines how much space your selected window takes up (window
-// as in panel, not your terminal's window). Sometimes you want a bit more space
-// to see the contents of a panel, and this keeps track of how much maximisation
-// you've set
type WindowMaximisation int
const (
@@ -124,8 +101,7 @@ func getScreenMode(config *config.AppConfig) WindowMaximisation {
}
}
-// NewGui builds a new gui handler
-func NewGui(log *logrus.Entry, dockerCommand *commands.DockerCommand, oSCommand *commands.OSCommand, tr *i18n.TranslationSet, config *config.AppConfig, errorChan chan error) (*Gui, error) {
+func NewGui(log *logrus.Entry, containerCmd *commands.ContainerCommand, oSCommand *commands.OSCommand, tr *i18n.TranslationSet, config *config.AppConfig, errorChan chan error) (*Gui, error) {
initialState := guiState{
Platform: *oSCommand.Platform,
Panels: &panelStates{
@@ -141,7 +117,7 @@ func NewGui(log *logrus.Entry, dockerCommand *commands.DockerCommand, oSCommand
gui := &Gui{
Log: log,
- DockerCommand: dockerCommand,
+ ContainerCmd: containerCmd,
OSCommand: oSCommand,
State: initialState,
Config: config,
@@ -168,7 +144,7 @@ func (gui *Gui) renderGlobalOptions() error {
}
func (gui *Gui) goEvery(interval time.Duration, function func() error) {
- _ = function() // time.Tick doesn't run immediately so we'll do that here // TODO: maybe change
+ _ = function()
go func() {
ticker := time.NewTicker(interval)
defer ticker.Stop()
@@ -180,9 +156,7 @@ func (gui *Gui) goEvery(interval time.Duration, function func() error) {
}()
}
-// Run setup the gui with keybindings and start the mainloop
func (gui *Gui) Run() error {
- // closing our task manager which in turn closes the current task if there is any, so we aren't leaving processes lying around after closing lazydocker
defer gui.taskManager.Close()
g, err := gocui.NewGui(gocui.NewGuiOpts{
@@ -194,15 +168,12 @@ func (gui *Gui) Run() error {
}
defer g.Close()
- // forgive the double-negative, this is because of my yaml `omitempty` woes
if !gui.Config.UserConfig.Gui.IgnoreMouseEvents {
g.Mouse = true
}
- gui.g = g // TODO: always use gui.g rather than passing g around everywhere
+ gui.g = g
- // if the deadlock package wants to report a deadlock, we first need to
- // close the gui so that we can actually read what it prints.
deadlock.Opts.LogBuf = lcUtils.NewOnceWriter(os.Stderr, func() {
gui.g.Close()
})
@@ -220,7 +191,6 @@ func (gui *Gui) Run() error {
continue
}
if strings.Contains(err.Error(), "No such container") {
- // this happens all the time when e.g. restarting containers so we won't worry about it
gui.Log.Warn(err)
continue
}
@@ -237,7 +207,6 @@ func (gui *Gui) Run() error {
return err
}
- // TODO: see if we can avoid the circular dependency
gui.setPanels()
if err = gui.keybindings(g); err != nil {
@@ -259,17 +228,14 @@ func (gui *Gui) Run() error {
ctx, finish := context.WithCancel(context.Background())
defer finish()
- go gui.listenForEvents(ctx, throttledRefresh.Trigger)
go gui.monitorContainerStats(ctx)
go func() {
throttledRefresh.Trigger()
gui.goEvery(time.Millisecond*30, gui.reRenderMain)
- gui.goEvery(time.Millisecond*1000, gui.updateContainerDetails)
+ gui.goEvery(time.Millisecond*1000, gui.renderContainers)
gui.goEvery(time.Millisecond*1000, gui.checkForContextChange)
- // we need to regularly re-render these because their stats will be changed in the background
- gui.goEvery(time.Millisecond*1000, gui.renderContainersAndServices)
}()
err = g.MainLoop()
@@ -281,8 +247,6 @@ func (gui *Gui) Run() error {
func (gui *Gui) setPanels() {
gui.Panels = Panels{
- Projects: gui.getProjectPanel(),
- Services: gui.getServicesPanel(),
Containers: gui.getContainersPanel(),
Images: gui.getImagesPanel(),
Volumes: gui.getVolumesPanel(),
@@ -291,18 +255,13 @@ func (gui *Gui) setPanels() {
}
}
-func (gui *Gui) updateContainerDetails() error {
- return gui.DockerCommand.RefreshContainerDetails(gui.Panels.Containers.List.GetAllItems())
+func (gui *Gui) renderContainers() error {
+ return gui.Panels.Containers.RerenderList()
}
func (gui *Gui) refresh() {
go func() {
- // Refresh containers/services first, then projects (which depend on
- // container labels to discover projects).
- if err := gui.refreshContainersAndServices(); err != nil {
- gui.Log.Error(err)
- }
- if err := gui.refreshProject(); err != nil {
+ if err := gui.refreshContainers(); err != nil {
gui.Log.Error(err)
}
}()
@@ -323,61 +282,6 @@ func (gui *Gui) refresh() {
}()
}
-func (gui *Gui) listenForEvents(ctx context.Context, refresh func()) {
- errorCount := 0
-
- onError := func(err error) {
- if err != nil {
- gui.ErrorChan <- errors.Errorf("Docker event stream returned error: %s\nRetry count: %d", err.Error(), errorCount)
- }
- errorCount++
- time.Sleep(time.Second * 2)
- }
-
-outer:
- for {
- messageChan, errChan := gui.DockerCommand.Client.Events(context.Background(), events.ListOptions{})
-
- if errorCount > 0 {
- select {
- case <-ctx.Done():
- return
- case err := <-errChan:
- onError(err)
- continue outer
- default:
- // If we're here then we lost connection to docker and we just got it back.
- // The reason we do this refresh explicitly is because successfully
- // reconnecting with docker does not mean it's going to send us a new
- // event any time soon.
-
- // Assuming the confirmation prompt currently holds the given error
- _ = gui.closeConfirmationPrompt()
- refresh()
- errorCount = 0
- }
- }
-
- for {
- select {
- case <-ctx.Done():
- return
- case message := <-messageChan:
- // We could be more granular about what events should trigger which refreshes.
- // At the moment it's pretty efficient though, and it might not be worth
- // the maintenance burden of mapping specific events to specific refreshes
- refresh()
-
- gui.Log.Infof("received event of type: %s", message.Type)
- case err := <-errChan:
- onError(err)
- continue outer
- }
- }
- }
-}
-
-// checkForContextChange runs the currently focused panel's 'select' function, simulating the current item having just been selected. This will then trigger a check to see if anything's changed (e.g. a service has a new container) and if so, the appropriate code will run. For example, if you're reading logs from a service and all of a sudden its container changes, this will trigger the 'select' function, which will work out that the context is not different because of the new container, and then it will re-attempt to get the logs, this time for the correct container. This 'context' is stored in the main panel's ObjectKey. I'm using the term 'context' here more broadly than just the different tabs you can view in a panel.
func (gui *Gui) checkForContextChange() error {
return gui.newLineFocused(gui.g.CurrentView())
}
@@ -404,8 +308,6 @@ func (gui *Gui) quit(g *gocui.Gui, v *gocui.View) error {
return gocui.ErrQuit
}
-// this handler is executed when we press escape when there is only one view
-// on the stack.
func (gui *Gui) escape() error {
if gui.State.Filter.active {
return gui.clearFilter()
@@ -459,9 +361,6 @@ func (gui *Gui) ShouldRefresh(key string) bool {
}
func (gui *Gui) initiallyFocusedViewName() string {
- if gui.DockerCommand.InDockerComposeProject {
- return "services"
- }
return "containers"
}
@@ -474,8 +373,6 @@ func (gui *Gui) Update(f func() error) {
}
func (gui *Gui) monitorContainerStats(ctx context.Context) {
- // periodically loop through running containers and see if we need to create a monitor goroutine for any
- // every second we check if we need to spawn a new goroutine
ticker := time.NewTicker(time.Second)
defer ticker.Stop()
for {
@@ -485,16 +382,13 @@ func (gui *Gui) monitorContainerStats(ctx context.Context) {
case <-ticker.C:
for _, container := range gui.Panels.Containers.List.GetAllItems() {
if !container.MonitoringStats {
- go gui.DockerCommand.CreateClientStatMonitor(container)
+ go gui.ContainerCmd.CreateClientStatMonitor(container)
}
}
}
}
}
-// this is used by our cheatsheet code to generate keybindings. We need some views
-// and panels to exist for us to know what keybindings there are, so we invoke
-// gocui in headless mode and create them.
func (gui *Gui) SetupFakeGui() {
g, err := gocui.NewGui(gocui.NewGuiOpts{
OutputMode: gocui.OutputTrue,
diff --git a/pkg/gui/images_panel.go b/pkg/gui/images_panel.go
index 7b0d5643..2f0cc48a 100644
--- a/pkg/gui/images_panel.go
+++ b/pkg/gui/images_panel.go
@@ -1,21 +1,14 @@
package gui
import (
- "fmt"
- "strings"
- "time"
-
- "github.com/docker/docker/api/types/image"
"github.com/fatih/color"
"github.com/jesseduffield/gocui"
- "github.com/jesseduffield/lazydocker/pkg/commands"
- "github.com/jesseduffield/lazydocker/pkg/config"
- "github.com/jesseduffield/lazydocker/pkg/gui/panels"
- "github.com/jesseduffield/lazydocker/pkg/gui/presentation"
- "github.com/jesseduffield/lazydocker/pkg/gui/types"
- "github.com/jesseduffield/lazydocker/pkg/tasks"
- "github.com/jesseduffield/lazydocker/pkg/utils"
- "github.com/samber/lo"
+ "github.com/jesseduffield/lazycontainer/pkg/commands"
+ "github.com/jesseduffield/lazycontainer/pkg/gui/panels"
+ "github.com/jesseduffield/lazycontainer/pkg/gui/presentation"
+ "github.com/jesseduffield/lazycontainer/pkg/gui/types"
+ "github.com/jesseduffield/lazycontainer/pkg/tasks"
+ "github.com/jesseduffield/lazycontainer/pkg/utils"
)
func (gui *Gui) getImagesPanel() *panels.SideListPanel[*commands.Image] {
@@ -42,23 +35,19 @@ func (gui *Gui) getImagesPanel() *panels.SideListPanel[*commands.Image] {
},
NoItemsMessage: gui.Tr.NoImages,
Gui: gui.intoInterface(),
- Sort: func(a *commands.Image, b *commands.Image) bool {
+ Sort: func(a, b *commands.Image) bool {
if a.Name == noneLabel && b.Name != noneLabel {
return false
}
-
if a.Name != noneLabel && b.Name == noneLabel {
return true
}
-
if a.Name != b.Name {
return a.Name < b.Name
}
-
if a.Tag != b.Tag {
return a.Tag < b.Tag
}
-
return a.ID < b.ID
},
GetTableCells: presentation.GetImageDisplayStrings,
@@ -69,7 +58,7 @@ func (gui *Gui) renderImageConfigTask(image *commands.Image) tasks.TaskFunc {
return gui.NewRenderStringTask(RenderStringTaskOpts{
GetStrContent: func() string { return gui.imageConfigStr(image) },
Autoscroll: false,
- Wrap: false, // don't care what your config is this page is ugly without wrapping
+ Wrap: false,
})
}
@@ -77,18 +66,11 @@ func (gui *Gui) imageConfigStr(image *commands.Image) string {
padding := 10
output := ""
output += utils.WithPadding("Name: ", padding) + image.Name + "\n"
- output += utils.WithPadding("ID: ", padding) + image.Image.ID + "\n"
- output += utils.WithPadding("Tags: ", padding) + utils.ColoredString(strings.Join(image.Image.RepoTags, ", "), color.FgGreen) + "\n"
- output += utils.WithPadding("Size: ", padding) + utils.FormatDecimalBytes(int(image.Image.Size)) + "\n"
- output += utils.WithPadding("Created: ", padding) + fmt.Sprintf("%v", time.Unix(image.Image.Created, 0).Format(time.RFC1123)) + "\n"
-
- history, err := image.RenderHistory()
- if err != nil {
- gui.Log.Error(err)
- }
-
- output += "\n\n" + history
-
+ output += utils.WithPadding("Tag: ", padding) + image.Tag + "\n"
+ output += utils.WithPadding("ID: ", padding) + image.ID + "\n"
+ output += utils.WithPadding("Reference: ", padding) + image.AppleImage.Reference + "\n"
+ output += utils.WithPadding("Digest: ", padding) + image.AppleImage.Descriptor.Digest + "\n"
+ output += utils.WithPadding("Size: ", padding) + image.AppleImage.FullSize + "\n"
return output
}
@@ -96,82 +78,51 @@ func (gui *Gui) reloadImages() error {
if err := gui.refreshStateImages(); err != nil {
return err
}
-
return gui.Panels.Images.RerenderList()
}
func (gui *Gui) refreshStateImages() error {
- images, err := gui.DockerCommand.RefreshImages()
+ images, err := gui.ContainerCmd.RefreshImages()
if err != nil {
return err
}
-
gui.Panels.Images.SetItems(images)
-
return nil
}
-func (gui *Gui) FilterString(view *gocui.View) string {
- if gui.State.Filter.panel != nil && gui.State.Filter.panel.GetView() != view {
- return ""
- }
-
- return gui.State.Filter.needle
-}
-
func (gui *Gui) handleImagesRemoveMenu(g *gocui.Gui, v *gocui.View) error {
- type removeImageOption struct {
- description string
- command string
- configOptions image.RemoveOptions
- }
-
img, err := gui.Panels.Images.GetSelectedItem()
if err != nil {
return nil
}
- shortSha := img.ID[7:17]
-
- // TODO: have a way of toggling in a menu instead of showing each permutation as a separate menu item
- options := []*removeImageOption{
- {
- description: gui.Tr.Remove,
- command: "docker image rm " + shortSha,
- configOptions: image.RemoveOptions{PruneChildren: true, Force: false},
- },
- {
- description: gui.Tr.RemoveWithoutPrune,
- command: "docker image rm --no-prune " + shortSha,
- configOptions: image.RemoveOptions{PruneChildren: false, Force: false},
- },
- {
- description: gui.Tr.RemoveWithForce,
- command: "docker image rm --force " + shortSha,
- configOptions: image.RemoveOptions{PruneChildren: true, Force: true},
- },
- {
- description: gui.Tr.RemoveWithoutPruneWithForce,
- command: "docker image rm --no-prune --force " + shortSha,
- configOptions: image.RemoveOptions{PruneChildren: false, Force: true},
- },
+ shortSha := img.ID
+ if len(img.ID) > 12 {
+ shortSha = img.ID[:12]
}
- menuItems := lo.Map(options, func(option *removeImageOption, _ int) *types.MenuItem {
- return &types.MenuItem{
- LabelColumns: []string{
- option.description,
- color.New(color.FgRed).Sprint(option.command),
- },
+ options := []struct {
+ description string
+ command string
+ force bool
+ }{
+ {description: gui.Tr.Remove, command: "container image rm " + shortSha, force: false},
+ {description: gui.Tr.RemoveWithForce, command: "container image rm --force " + shortSha, force: true},
+ }
+
+ menuItems := make([]*types.MenuItem, len(options))
+ for i, opt := range options {
+ opt := opt
+ menuItems[i] = &types.MenuItem{
+ LabelColumns: []string{opt.description, color.New(color.FgRed).Sprint(opt.command)},
OnPress: func() error {
- if err := img.Remove(option.configOptions); err != nil {
+ if err := img.Remove(opt.force); err != nil {
return gui.createErrorPanel(err.Error())
}
-
return nil
},
}
- })
+ }
return gui.Menu(CreateMenuOptions{
Title: "",
@@ -179,10 +130,11 @@ func (gui *Gui) handleImagesRemoveMenu(g *gocui.Gui, v *gocui.View) error {
})
}
-func (gui *Gui) handlePruneImages() error {
- return gui.createConfirmationPanel(gui.Tr.Confirm, gui.Tr.ConfirmPruneImages, func(g *gocui.Gui, v *gocui.View) error {
+func (gui *Gui) handlePruneImages(all bool) error {
+ title := gui.Tr.ConfirmPruneImages
+ return gui.createConfirmationPanel(gui.Tr.Confirm, title, func(g *gocui.Gui, v *gocui.View) error {
return gui.WithWaitingStatus(gui.Tr.PruningStatus, func() error {
- err := gui.DockerCommand.PruneImages()
+ err := gui.ContainerCmd.Client.PruneImages(all)
if err != nil {
return gui.createErrorPanel(err.Error())
}
@@ -197,25 +149,13 @@ func (gui *Gui) handleImagesCustomCommand(g *gocui.Gui, v *gocui.View) error {
return nil
}
- commandObject := gui.DockerCommand.NewCommandObject(commands.CommandObject{
- Image: img,
- })
-
+ commandObject := gui.ContainerCmd.NewCommandObject(commands.CommandObject{Image: img})
customCommands := gui.Config.UserConfig.CustomCommands.Images
-
return gui.createCustomCommandMenu(customCommands, commandObject)
}
func (gui *Gui) handleImagesBulkCommand(g *gocui.Gui, v *gocui.View) error {
- baseBulkCommands := []config.CustomCommand{
- {
- Name: gui.Tr.PruneImages,
- InternalFunction: gui.handlePruneImages,
- },
- }
-
- bulkCommands := append(baseBulkCommands, gui.Config.UserConfig.BulkCommands.Images...)
- commandObject := gui.DockerCommand.NewCommandObject(commands.CommandObject{})
-
+ bulkCommands := gui.Config.UserConfig.BulkCommands.Images
+ commandObject := gui.ContainerCmd.NewCommandObject(commands.CommandObject{})
return gui.createBulkCommandMenu(bulkCommands, commandObject)
}
diff --git a/pkg/gui/keybindings.go b/pkg/gui/keybindings.go
index df706024..794bc83c 100644
--- a/pkg/gui/keybindings.go
+++ b/pkg/gui/keybindings.go
@@ -6,18 +6,14 @@ import (
"github.com/jesseduffield/gocui"
)
-// Binding - a keybinding mapping a key and modifier to a handler. The keypress
-// is only handled if the given view has focus, or handled globally if the view
-// is ""
type Binding struct {
ViewName string
Handler func(*gocui.Gui, *gocui.View) error
- Key interface{} // FIXME: find out how to get `gocui.Key | rune`
+ Key interface{}
Modifier gocui.Modifier
Description string
}
-// GetKey is a function.
func (b *Binding) GetKey() string {
key := 0
@@ -28,7 +24,6 @@ func (b *Binding) GetKey() string {
key = int(b.Key.(gocui.Key))
}
- // special keys
switch key {
case 27:
return "esc"
@@ -53,7 +48,6 @@ func (b *Binding) GetKey() string {
return fmt.Sprintf("%c", key)
}
-// GetInitialKeybindings is a function.
func (gui *Gui) GetInitialKeybindings() []*Binding {
bindings := []*Binding{
{
@@ -128,27 +122,6 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
Modifier: gocui.ModNone,
Handler: gui.handleCustomCommand,
},
- {
- ViewName: "project",
- Key: 'e',
- Modifier: gocui.ModNone,
- Handler: gui.handleEditConfig,
- Description: gui.Tr.EditConfig,
- },
- {
- ViewName: "project",
- Key: 'o',
- Modifier: gocui.ModNone,
- Handler: gui.handleOpenConfig,
- Description: gui.Tr.OpenConfig,
- },
- {
- ViewName: "project",
- Key: 'm',
- Modifier: gocui.ModNone,
- Handler: gui.handleViewAllLogs,
- Description: gui.Tr.ViewLogs,
- },
{
ViewName: "menu",
Key: gocui.KeyEsc,
@@ -199,13 +172,6 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
Handler: gui.handleHideStoppedContainers,
Description: gui.Tr.HideStopped,
},
- {
- ViewName: "containers",
- Key: 'p',
- Modifier: gocui.ModNone,
- Handler: gui.handleContainerPause,
- Description: gui.Tr.Pause,
- },
{
ViewName: "containers",
Key: 's',
@@ -263,109 +229,11 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
Description: gui.Tr.OpenInBrowser,
},
{
- ViewName: "services",
- Key: 'u',
+ ViewName: "containers",
+ Key: 'k',
Modifier: gocui.ModNone,
- Handler: gui.handleServiceUp,
- Description: gui.Tr.UpService,
- },
- {
- ViewName: "services",
- Key: 'd',
- Modifier: gocui.ModNone,
- Handler: gui.handleServiceRemoveMenu,
- Description: gui.Tr.RemoveService,
- },
- {
- ViewName: "services",
- Key: 's',
- Modifier: gocui.ModNone,
- Handler: gui.handleServiceStop,
- Description: gui.Tr.Stop,
- },
- {
- ViewName: "services",
- Key: 'p',
- Modifier: gocui.ModNone,
- Handler: gui.handleServicePause,
- Description: gui.Tr.Pause,
- },
- {
- ViewName: "services",
- Key: 'r',
- Modifier: gocui.ModNone,
- Handler: gui.handleServiceRestart,
- Description: gui.Tr.Restart,
- },
- {
- ViewName: "services",
- Key: 'S',
- Modifier: gocui.ModNone,
- Handler: gui.handleServiceStart,
- Description: gui.Tr.Start,
- },
- {
- ViewName: "services",
- Key: 'a',
- Modifier: gocui.ModNone,
- Handler: gui.handleServiceAttach,
- Description: gui.Tr.Attach,
- },
- {
- ViewName: "services",
- Key: 'm',
- Modifier: gocui.ModNone,
- Handler: gui.handleServiceRenderLogsToMain,
- Description: gui.Tr.ViewLogs,
- },
- {
- ViewName: "services",
- Key: 'U',
- Modifier: gocui.ModNone,
- Handler: gui.handleProjectUp,
- Description: gui.Tr.UpProject,
- },
- {
- ViewName: "services",
- Key: 'D',
- Modifier: gocui.ModNone,
- Handler: gui.handleProjectDown,
- Description: gui.Tr.DownProject,
- },
- {
- ViewName: "services",
- Key: 'R',
- Modifier: gocui.ModNone,
- Handler: gui.handleServiceRestartMenu,
- Description: gui.Tr.ViewRestartOptions,
- },
- {
- ViewName: "services",
- Key: 'c',
- Modifier: gocui.ModNone,
- Handler: gui.handleServicesCustomCommand,
- Description: gui.Tr.RunCustomCommand,
- },
- {
- ViewName: "services",
- Key: 'b',
- Modifier: gocui.ModNone,
- Handler: gui.handleServicesBulkCommand,
- Description: gui.Tr.ViewBulkCommands,
- },
- {
- ViewName: "services",
- Key: 'E',
- Modifier: gocui.ModNone,
- Handler: gui.handleServicesExecShell,
- Description: gui.Tr.ExecShell,
- },
- {
- ViewName: "services",
- Key: 'w',
- Modifier: gocui.ModNone,
- Handler: gui.handleServicesOpenInBrowserCommand,
- Description: gui.Tr.OpenInBrowser,
+ Handler: gui.handleContainerKill,
+ Description: gui.Tr.Kill,
},
{
ViewName: "images",
@@ -535,12 +403,10 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
}
bindings = append(bindings, []*Binding{
- {Handler: gui.handleGoTo(gui.Panels.Projects.View), Key: '1', Description: gui.Tr.FocusProjects},
- {Handler: gui.handleGoTo(gui.Panels.Services.View), Key: '2', Description: gui.Tr.FocusServices},
- {Handler: gui.handleGoTo(gui.Panels.Containers.View), Key: '3', Description: gui.Tr.FocusContainers},
- {Handler: gui.handleGoTo(gui.Panels.Images.View), Key: '4', Description: gui.Tr.FocusImages},
- {Handler: gui.handleGoTo(gui.Panels.Volumes.View), Key: '5', Description: gui.Tr.FocusVolumes},
- {Handler: gui.handleGoTo(gui.Panels.Networks.View), Key: '6', Description: gui.Tr.FocusNetworks},
+ {Handler: gui.handleGoTo(gui.Panels.Containers.View), Key: '1', Description: gui.Tr.FocusContainers},
+ {Handler: gui.handleGoTo(gui.Panels.Images.View), Key: '2', Description: gui.Tr.FocusImages},
+ {Handler: gui.handleGoTo(gui.Panels.Volumes.View), Key: '3', Description: gui.Tr.FocusVolumes},
+ {Handler: gui.handleGoTo(gui.Panels.Networks.View), Key: '4', Description: gui.Tr.FocusNetworks},
}...)
for _, panel := range gui.allListPanels() {
diff --git a/pkg/gui/menu_panel.go b/pkg/gui/menu_panel.go
index 99c65807..132ff489 100644
--- a/pkg/gui/menu_panel.go
+++ b/pkg/gui/menu_panel.go
@@ -1,10 +1,10 @@
package gui
import (
- "github.com/jesseduffield/lazydocker/pkg/gui/panels"
- "github.com/jesseduffield/lazydocker/pkg/gui/presentation"
- "github.com/jesseduffield/lazydocker/pkg/gui/types"
- "github.com/jesseduffield/lazydocker/pkg/utils"
+ "github.com/jesseduffield/lazycontainer/pkg/gui/panels"
+ "github.com/jesseduffield/lazycontainer/pkg/gui/presentation"
+ "github.com/jesseduffield/lazycontainer/pkg/gui/types"
+ "github.com/jesseduffield/lazycontainer/pkg/utils"
)
type CreateMenuOptions struct {
diff --git a/pkg/gui/networks_panel.go b/pkg/gui/networks_panel.go
index 4702d35d..ea3648b0 100644
--- a/pkg/gui/networks_panel.go
+++ b/pkg/gui/networks_panel.go
@@ -1,17 +1,15 @@
package gui
import (
- "strconv"
-
"github.com/fatih/color"
"github.com/jesseduffield/gocui"
- "github.com/jesseduffield/lazydocker/pkg/commands"
- "github.com/jesseduffield/lazydocker/pkg/config"
- "github.com/jesseduffield/lazydocker/pkg/gui/panels"
- "github.com/jesseduffield/lazydocker/pkg/gui/presentation"
- "github.com/jesseduffield/lazydocker/pkg/gui/types"
- "github.com/jesseduffield/lazydocker/pkg/tasks"
- "github.com/jesseduffield/lazydocker/pkg/utils"
+ "github.com/jesseduffield/lazycontainer/pkg/commands"
+ "github.com/jesseduffield/lazycontainer/pkg/config"
+ "github.com/jesseduffield/lazycontainer/pkg/gui/panels"
+ "github.com/jesseduffield/lazycontainer/pkg/gui/presentation"
+ "github.com/jesseduffield/lazycontainer/pkg/gui/types"
+ "github.com/jesseduffield/lazycontainer/pkg/tasks"
+ "github.com/jesseduffield/lazycontainer/pkg/utils"
"github.com/samber/lo"
)
@@ -37,9 +35,6 @@ func (gui *Gui) getNetworksPanel() *panels.SideListPanel[*commands.Network] {
},
NoItemsMessage: gui.Tr.NoNetworks,
Gui: gui.intoInterface(),
- // we're sorting these networks based on whether they have labels defined,
- // because those are the ones you typically care about.
- // Within that, we also sort them alphabetically
Sort: func(a *commands.Network, b *commands.Network) bool {
return a.Name < b.Name
},
@@ -54,28 +49,14 @@ func (gui *Gui) renderNetworkConfig(network *commands.Network) tasks.TaskFunc {
func (gui *Gui) networkConfigStr(network *commands.Network) string {
padding := 15
output := ""
- output += utils.WithPadding("ID: ", padding) + network.Network.ID + "\n"
+ output += utils.WithPadding("ID: ", padding) + network.AppleNetwork.ID + "\n"
output += utils.WithPadding("Name: ", padding) + network.Name + "\n"
- output += utils.WithPadding("Driver: ", padding) + network.Network.Driver + "\n"
- output += utils.WithPadding("Scope: ", padding) + network.Network.Scope + "\n"
- output += utils.WithPadding("EnabledIPV6: ", padding) + strconv.FormatBool(network.Network.EnableIPv6) + "\n"
- output += utils.WithPadding("Internal: ", padding) + strconv.FormatBool(network.Network.Internal) + "\n"
- output += utils.WithPadding("Attachable: ", padding) + strconv.FormatBool(network.Network.Attachable) + "\n"
- output += utils.WithPadding("Ingress: ", padding) + strconv.FormatBool(network.Network.Ingress) + "\n"
-
- output += utils.WithPadding("Containers: ", padding)
- if len(network.Network.Containers) > 0 {
- output += "\n"
- for _, v := range network.Network.Containers {
- output += utils.FormatMapItem(padding, v.Name, v.EndpointID)
- }
- } else {
- output += "none\n"
- }
-
+ output += utils.WithPadding("Mode: ", padding) + network.AppleNetwork.Config.Mode + "\n"
+ output += utils.WithPadding("State: ", padding) + network.AppleNetwork.State + "\n"
+ output += utils.WithPadding("IPv4 Subnet: ", padding) + network.AppleNetwork.Status.IPv4Subnet + "\n"
+ output += utils.WithPadding("IPv4 Gateway: ", padding) + network.AppleNetwork.Status.IPv4Gateway + "\n"
output += "\n"
- output += utils.WithPadding("Labels: ", padding) + utils.FormatMap(padding, network.Network.Labels) + "\n"
- output += utils.WithPadding("Options: ", padding) + utils.FormatMap(padding, network.Network.Options)
+ output += utils.WithPadding("Labels: ", padding) + utils.FormatMap(padding, network.AppleNetwork.Config.Labels)
return output
}
@@ -89,7 +70,7 @@ func (gui *Gui) reloadNetworks() error {
}
func (gui *Gui) refreshStateNetworks() error {
- networks, err := gui.DockerCommand.RefreshNetworks()
+ networks, err := gui.ContainerCmd.RefreshNetworks()
if err != nil {
return err
}
@@ -113,7 +94,7 @@ func (gui *Gui) handleNetworksRemoveMenu(g *gocui.Gui, v *gocui.View) error {
options := []*removeNetworkOption{
{
description: gui.Tr.Remove,
- command: utils.WithShortSha("docker network rm " + network.Name),
+ command: utils.WithShortSha("container network rm " + network.Name),
},
}
@@ -140,7 +121,7 @@ func (gui *Gui) handleNetworksRemoveMenu(g *gocui.Gui, v *gocui.View) error {
func (gui *Gui) handlePruneNetworks() error {
return gui.createConfirmationPanel(gui.Tr.Confirm, gui.Tr.ConfirmPruneNetworks, func(g *gocui.Gui, v *gocui.View) error {
return gui.WithWaitingStatus(gui.Tr.PruningStatus, func() error {
- err := gui.DockerCommand.PruneNetworks()
+ err := gui.ContainerCmd.PruneNetworks()
if err != nil {
return gui.createErrorPanel(err.Error())
}
@@ -155,7 +136,7 @@ func (gui *Gui) handleNetworksCustomCommand(g *gocui.Gui, v *gocui.View) error {
return nil
}
- commandObject := gui.DockerCommand.NewCommandObject(commands.CommandObject{
+ commandObject := gui.ContainerCmd.Client.NewCommandObject(commands.CommandObject{
Network: network,
})
@@ -173,7 +154,7 @@ func (gui *Gui) handleNetworksBulkCommand(g *gocui.Gui, v *gocui.View) error {
}
bulkCommands := append(baseBulkCommands, gui.Config.UserConfig.BulkCommands.Networks...)
- commandObject := gui.DockerCommand.NewCommandObject(commands.CommandObject{})
+ commandObject := gui.ContainerCmd.Client.NewCommandObject(commands.CommandObject{})
return gui.createBulkCommandMenu(bulkCommands, commandObject)
}
diff --git a/pkg/gui/options_menu_panel.go b/pkg/gui/options_menu_panel.go
index 38ba71a5..92021b77 100644
--- a/pkg/gui/options_menu_panel.go
+++ b/pkg/gui/options_menu_panel.go
@@ -4,7 +4,7 @@ import (
"github.com/samber/lo"
"github.com/jesseduffield/gocui"
- "github.com/jesseduffield/lazydocker/pkg/gui/types"
+ "github.com/jesseduffield/lazycontainer/pkg/gui/types"
)
func (gui *Gui) getBindings(v *gocui.View) []*Binding {
diff --git a/pkg/gui/panels.go b/pkg/gui/panels.go
index b92e44e1..49b2ebce 100644
--- a/pkg/gui/panels.go
+++ b/pkg/gui/panels.go
@@ -1,7 +1,17 @@
package gui
-import "github.com/jesseduffield/lazydocker/pkg/gui/panels"
+import (
+ "github.com/jesseduffield/gocui"
+ "github.com/jesseduffield/lazycontainer/pkg/gui/panels"
+)
func (gui *Gui) intoInterface() panels.IGui {
return gui
}
+
+func (gui *Gui) FilterString(view *gocui.View) string {
+ if gui.State.Filter.active && gui.State.Filter.panel != nil && gui.State.Filter.panel.GetView() == view {
+ return gui.State.Filter.needle
+ }
+ return ""
+}
diff --git a/pkg/gui/panels/context_state.go b/pkg/gui/panels/context_state.go
index 36610cff..fa52f825 100644
--- a/pkg/gui/panels/context_state.go
+++ b/pkg/gui/panels/context_state.go
@@ -1,7 +1,7 @@
package panels
import (
- "github.com/jesseduffield/lazydocker/pkg/tasks"
+ "github.com/jesseduffield/lazycontainer/pkg/tasks"
"github.com/samber/lo"
)
diff --git a/pkg/gui/panels/side_list_panel.go b/pkg/gui/panels/side_list_panel.go
index 26ddc798..92a99d83 100644
--- a/pkg/gui/panels/side_list_panel.go
+++ b/pkg/gui/panels/side_list_panel.go
@@ -7,8 +7,8 @@ import (
"github.com/go-errors/errors"
"github.com/jesseduffield/gocui"
- "github.com/jesseduffield/lazydocker/pkg/tasks"
- "github.com/jesseduffield/lazydocker/pkg/utils"
+ "github.com/jesseduffield/lazycontainer/pkg/tasks"
+ "github.com/jesseduffield/lazycontainer/pkg/utils"
"github.com/samber/lo"
)
diff --git a/pkg/gui/presentation/container_stats.go b/pkg/gui/presentation/container_stats.go
index e3a9b3dc..31de6c94 100644
--- a/pkg/gui/presentation/container_stats.go
+++ b/pkg/gui/presentation/container_stats.go
@@ -10,9 +10,9 @@ import (
"github.com/fatih/color"
"github.com/jesseduffield/asciigraph"
- "github.com/jesseduffield/lazydocker/pkg/commands"
- "github.com/jesseduffield/lazydocker/pkg/config"
- "github.com/jesseduffield/lazydocker/pkg/utils"
+ "github.com/jesseduffield/lazycontainer/pkg/commands"
+ "github.com/jesseduffield/lazycontainer/pkg/config"
+ "github.com/jesseduffield/lazycontainer/pkg/utils"
"github.com/mcuadros/go-lookup"
"github.com/samber/lo"
)
@@ -33,9 +33,9 @@ func RenderStats(userConfig *config.UserConfig, container *commands.Container, v
graphs[i] = utils.ColoredString(graph, utils.GetColorAttribute(spec.Color))
}
- pidsCount := fmt.Sprintf("PIDs: %d", stats.ClientStats.PidsStats.Current)
- dataReceived := fmt.Sprintf("Traffic received: %s", utils.FormatDecimalBytes(stats.ClientStats.Networks.Eth0.RxBytes))
- dataSent := fmt.Sprintf("Traffic sent: %s", utils.FormatDecimalBytes(stats.ClientStats.Networks.Eth0.TxBytes))
+ pidsCount := fmt.Sprintf("Processes: %d", stats.ClientStats.NumProcesses)
+ dataReceived := fmt.Sprintf("Traffic received: %s", utils.FormatDecimalBytes(int(stats.ClientStats.NetworkRxBytes)))
+ dataSent := fmt.Sprintf("Traffic sent: %s", utils.FormatDecimalBytes(int(stats.ClientStats.NetworkTxBytes)))
originalStats, err := utils.MarshalIntoYaml(stats)
if err != nil {
@@ -53,7 +53,6 @@ func RenderStats(userConfig *config.UserConfig, container *commands.Container, v
return contents, nil
}
-// plotGraph returns the plotted graph based on the graph spec and the stat history
func plotGraph(container *commands.Container, spec config.GraphConfig, width int) (string, error) {
container.StatsMutex.Lock()
defer container.StatsMutex.Unlock()
@@ -105,7 +104,6 @@ func plotGraph(container *commands.Container, spec config.GraphConfig, width int
), nil
}
-// from Dave C's answer at https://stackoverflow.com/questions/20767724/converting-unknown-interface-to-float64-in-golang
func getFloat(unk interface{}) (float64, error) {
floatType := reflect.TypeOf(float64(0))
stringType := reflect.TypeOf("")
diff --git a/pkg/gui/presentation/containers.go b/pkg/gui/presentation/containers.go
index 173a1e8a..e844ddf1 100644
--- a/pkg/gui/presentation/containers.go
+++ b/pkg/gui/presentation/containers.go
@@ -3,14 +3,12 @@ package presentation
import (
"fmt"
"sort"
- "strconv"
"strings"
- "github.com/docker/docker/api/types/container"
"github.com/fatih/color"
- "github.com/jesseduffield/lazydocker/pkg/commands"
- "github.com/jesseduffield/lazydocker/pkg/config"
- "github.com/jesseduffield/lazydocker/pkg/utils"
+ "github.com/jesseduffield/lazycontainer/pkg/commands"
+ "github.com/jesseduffield/lazycontainer/pkg/config"
+ "github.com/jesseduffield/lazycontainer/pkg/utils"
"github.com/samber/lo"
)
@@ -26,34 +24,36 @@ func GetContainerDisplayStrings(guiConfig *config.GuiConfig, container *commands
}
func displayContainerImage(container *commands.Container) string {
- return strings.TrimPrefix(container.Container.Image, "sha256:")
+ return strings.TrimPrefix(container.GetImage(), "sha256:")
}
func displayPorts(c *commands.Container) string {
- portStrings := lo.Map(c.Container.Ports, func(port container.Port, _ int) string {
- if port.PublicPort == 0 {
- return fmt.Sprintf("%d/%s", port.PrivatePort, port.Type)
+ ports := c.AppleContainer.Configuration.PublishedPorts
+ portStrings := lo.Map(ports, func(port struct {
+ HostIP string `json:"hostIP"`
+ HostPort int `json:"hostPort"`
+ ContainerPort int `json:"containerPort"`
+ Protocol string `json:"protocol"`
+ }, _ int) string {
+ if port.HostPort == 0 {
+ return fmt.Sprintf("%d/%s", port.ContainerPort, port.Protocol)
}
- // docker ps will show '0.0.0.0:80->80/tcp' but we'll show
- // '80->80/tcp' instead to save space (unless the IP is something other than
- // 0.0.0.0)
ipString := ""
- if port.IP != "0.0.0.0" {
- ipString = port.IP + ":"
+ if port.HostIP != "0.0.0.0" && port.HostIP != "" {
+ ipString = port.HostIP + ":"
}
- return fmt.Sprintf("%s%d->%d/%s", ipString, port.PublicPort, port.PrivatePort, port.Type)
+ return fmt.Sprintf("%s%d->%d/%s", ipString, port.HostPort, port.ContainerPort, port.Protocol)
})
- // sorting because the order of the ports is not deterministic
- // and we don't want to have them constantly swapping
sort.Strings(portStrings)
return strings.Join(portStrings, ", ")
}
-// getContainerDisplayStatus returns the colored status of the container
func getContainerDisplayStatus(guiConfig *config.GuiConfig, c *commands.Container) string {
+ status := c.GetStatus()
+
shortStatusMap := map[string]string{
"paused": "P",
"exited": "X",
@@ -62,6 +62,7 @@ func getContainerDisplayStatus(guiConfig *config.GuiConfig, c *commands.Containe
"restarting": "RS",
"running": "R",
"dead": "D",
+ "stopped": "S",
}
iconStatusMap := map[string]rune{
@@ -72,33 +73,34 @@ func getContainerDisplayStatus(guiConfig *config.GuiConfig, c *commands.Containe
"restarting": '⟳',
"running": '▶',
"dead": '!',
+ "stopped": '■',
}
var containerState string
switch guiConfig.ContainerStatusHealthStyle {
case "short":
- containerState = shortStatusMap[c.Container.State]
+ containerState = shortStatusMap[status]
case "icon":
- containerState = string(iconStatusMap[c.Container.State])
+ containerState = string(iconStatusMap[status])
case "long":
fallthrough
default:
- containerState = c.Container.State
+ containerState = status
}
return utils.ColoredString(containerState, getContainerColor(c))
}
-// GetDisplayStatus returns the exit code if the container has exited, and the health status if the container is running (and has a health check)
func getContainerDisplaySubstatus(guiConfig *config.GuiConfig, c *commands.Container) string {
if !c.DetailsLoaded() {
return ""
}
- switch c.Container.State {
- case "exited":
+ status := c.GetStatus()
+ switch status {
+ case "exited", "stopped":
return utils.ColoredString(
- fmt.Sprintf("(%s)", strconv.Itoa(c.Details.State.ExitCode)), getContainerColor(c),
+ fmt.Sprintf("(%d)", 0), getContainerColor(c),
)
case "running":
return getHealthStatus(guiConfig, c)
@@ -108,51 +110,9 @@ func getContainerDisplaySubstatus(guiConfig *config.GuiConfig, c *commands.Conta
}
func getHealthStatus(guiConfig *config.GuiConfig, c *commands.Container) string {
- if !c.DetailsLoaded() {
- return ""
- }
-
- healthStatusColorMap := map[string]color.Attribute{
- "healthy": color.FgGreen,
- "unhealthy": color.FgRed,
- "starting": color.FgYellow,
- }
-
- if c.Details.State.Health == nil {
- return ""
- }
-
- shortHealthStatusMap := map[string]string{
- "healthy": "H",
- "unhealthy": "U",
- "starting": "S",
- }
-
- iconHealthStatusMap := map[string]rune{
- "healthy": '✔',
- "unhealthy": '?',
- "starting": '…',
- }
-
- var healthStatus string
- switch guiConfig.ContainerStatusHealthStyle {
- case "short":
- healthStatus = shortHealthStatusMap[c.Details.State.Health.Status]
- case "icon":
- healthStatus = string(iconHealthStatusMap[c.Details.State.Health.Status])
- case "long":
- fallthrough
- default:
- healthStatus = c.Details.State.Health.Status
- }
-
- if healthStatusColor, ok := healthStatusColorMap[c.Details.State.Health.Status]; ok {
- return utils.ColoredString(fmt.Sprintf("(%s)", healthStatus), healthStatusColor)
- }
return ""
}
-// getDisplayCPUPerc colors the cpu percentage based on how extreme it is
func getDisplayCPUPerc(c *commands.Container) string {
stats, ok := c.GetLastStats()
if !ok {
@@ -174,16 +134,11 @@ func getDisplayCPUPerc(c *commands.Container) string {
return utils.ColoredString(formattedPercentage, clr)
}
-// getContainerColor Container color
func getContainerColor(c *commands.Container) color.Attribute {
- switch c.Container.State {
- case "exited":
- // This means the colour may be briefly yellow and then switch to red upon starting
- // Not sure what a better alternative is.
- if !c.DetailsLoaded() || c.Details.State.ExitCode == 0 {
- return color.FgYellow
- }
- return color.FgRed
+ status := c.GetStatus()
+ switch status {
+ case "exited", "stopped":
+ return color.FgYellow
case "created":
return color.FgCyan
case "running":
diff --git a/pkg/gui/presentation/images.go b/pkg/gui/presentation/images.go
index a420a85d..253ae115 100644
--- a/pkg/gui/presentation/images.go
+++ b/pkg/gui/presentation/images.go
@@ -1,14 +1,14 @@
package presentation
import (
- "github.com/jesseduffield/lazydocker/pkg/commands"
- "github.com/jesseduffield/lazydocker/pkg/utils"
+ "github.com/jesseduffield/lazycontainer/pkg/commands"
+ "github.com/jesseduffield/lazycontainer/pkg/utils"
)
func GetImageDisplayStrings(image *commands.Image) []string {
return []string{
image.Name,
image.Tag,
- utils.FormatDecimalBytes(int(image.Image.Size)),
+ utils.FormatDecimalBytes(int(image.AppleImage.Descriptor.Size)),
}
}
diff --git a/pkg/gui/presentation/menu_items.go b/pkg/gui/presentation/menu_items.go
index 6febd261..8862cac0 100644
--- a/pkg/gui/presentation/menu_items.go
+++ b/pkg/gui/presentation/menu_items.go
@@ -1,6 +1,6 @@
package presentation
-import "github.com/jesseduffield/lazydocker/pkg/gui/types"
+import "github.com/jesseduffield/lazycontainer/pkg/gui/types"
func GetMenuItemDisplayStrings(menuItem *types.MenuItem) []string {
return menuItem.LabelColumns
diff --git a/pkg/gui/presentation/networks.go b/pkg/gui/presentation/networks.go
index 88a29fc9..27eb53c6 100644
--- a/pkg/gui/presentation/networks.go
+++ b/pkg/gui/presentation/networks.go
@@ -1,7 +1,7 @@
package presentation
-import "github.com/jesseduffield/lazydocker/pkg/commands"
+import "github.com/jesseduffield/lazycontainer/pkg/commands"
func GetNetworkDisplayStrings(network *commands.Network) []string {
- return []string{network.Network.Driver, network.Name}
+ return []string{network.AppleNetwork.Config.Mode, network.Name}
}
diff --git a/pkg/gui/presentation/projects.go b/pkg/gui/presentation/projects.go
deleted file mode 100644
index 44d396c9..00000000
--- a/pkg/gui/presentation/projects.go
+++ /dev/null
@@ -1,7 +0,0 @@
-package presentation
-
-import "github.com/jesseduffield/lazydocker/pkg/commands"
-
-func GetProjectDisplayStrings(project *commands.Project) []string {
- return []string{project.Name}
-}
diff --git a/pkg/gui/presentation/services.go b/pkg/gui/presentation/services.go
deleted file mode 100644
index 7e781ec1..00000000
--- a/pkg/gui/presentation/services.go
+++ /dev/null
@@ -1,43 +0,0 @@
-package presentation
-
-import (
- "github.com/fatih/color"
- "github.com/jesseduffield/lazydocker/pkg/commands"
- "github.com/jesseduffield/lazydocker/pkg/config"
- "github.com/jesseduffield/lazydocker/pkg/utils"
-)
-
-func GetServiceDisplayStrings(guiConfig *config.GuiConfig, service *commands.Service) []string {
- if service.Container == nil {
- var containerState string
- switch guiConfig.ContainerStatusHealthStyle {
- case "short":
- containerState = "n"
- case "icon":
- containerState = "."
- case "long":
- fallthrough
- default:
- containerState = "none"
- }
-
- return []string{
- utils.ColoredString(containerState, color.FgBlue),
- "",
- service.Name,
- "",
- "",
- "",
- }
- }
-
- container := service.Container
- return []string{
- getContainerDisplayStatus(guiConfig, container),
- getContainerDisplaySubstatus(guiConfig, container),
- service.Name,
- getDisplayCPUPerc(container),
- utils.ColoredString(displayPorts(container), color.FgYellow),
- utils.ColoredString(displayContainerImage(container), color.FgMagenta),
- }
-}
diff --git a/pkg/gui/presentation/volumes.go b/pkg/gui/presentation/volumes.go
index c6b90eb1..d6916932 100644
--- a/pkg/gui/presentation/volumes.go
+++ b/pkg/gui/presentation/volumes.go
@@ -1,7 +1,10 @@
package presentation
-import "github.com/jesseduffield/lazydocker/pkg/commands"
+import (
+ "github.com/jesseduffield/lazycontainer/pkg/commands"
+ "github.com/jesseduffield/lazycontainer/pkg/utils"
+)
func GetVolumeDisplayStrings(volume *commands.Volume) []string {
- return []string{volume.Volume.Driver, volume.Name}
+ return []string{utils.FormatBinaryBytes(int(volume.AppleVolume.SizeInBytes)), volume.Name}
}
diff --git a/pkg/gui/project_panel.go b/pkg/gui/project_panel.go
deleted file mode 100644
index ca9acf28..00000000
--- a/pkg/gui/project_panel.go
+++ /dev/null
@@ -1,230 +0,0 @@
-package gui
-
-import (
- "bytes"
- "context"
- "strings"
-
- "github.com/fatih/color"
- "github.com/jesseduffield/gocui"
- "github.com/jesseduffield/lazydocker/pkg/commands"
- "github.com/jesseduffield/lazydocker/pkg/gui/panels"
- "github.com/jesseduffield/lazydocker/pkg/gui/presentation"
- "github.com/jesseduffield/lazydocker/pkg/tasks"
- "github.com/jesseduffield/lazydocker/pkg/utils"
- "github.com/jesseduffield/yaml"
-)
-
-func (gui *Gui) getProjectPanel() *panels.SideListPanel[*commands.Project] {
- return &panels.SideListPanel[*commands.Project]{
- ContextState: &panels.ContextState[*commands.Project]{
- GetMainTabs: func() []panels.MainTab[*commands.Project] {
- return []panels.MainTab[*commands.Project]{
- {
- Key: "logs",
- Title: gui.Tr.LogsTitle,
- Render: gui.renderAllLogs,
- },
- {
- Key: "config",
- Title: gui.Tr.DockerComposeConfigTitle,
- Render: gui.renderDockerComposeConfig,
- },
- {
- Key: "credits",
- Title: gui.Tr.CreditsTitle,
- Render: gui.renderCredits,
- },
- }
- },
- GetItemContextCacheKey: func(project *commands.Project) string {
- return "projects-" + project.Name
- },
- },
-
- ListPanel: panels.ListPanel[*commands.Project]{
- List: panels.NewFilteredList[*commands.Project](),
- View: gui.Views.Project,
- },
- NoItemsMessage: "",
- Gui: gui.intoInterface(),
-
- Sort: func(a *commands.Project, b *commands.Project) bool {
- return a.Name < b.Name
- },
- GetTableCells: presentation.GetProjectDisplayStrings,
- OnSelect: func(project *commands.Project) error {
- // When a different project is selected, re-filter services and
- // containers to show only those belonging to the selected project.
- return gui.renderContainersAndServices()
- },
- }
-}
-
-func (gui *Gui) refreshProject() error {
- projects := gui.getDiscoveredProjects()
-
- // Preserve the current selection across refreshes. On the first refresh,
- // select the project specified via -p flag, or fall back to the local project.
- selectedName := gui.getSelectedProjectName()
- if selectedName == "" {
- if gui.Config.ProjectName != "" {
- selectedName = gui.Config.ProjectName
- } else {
- selectedName = gui.DockerCommand.LocalProjectName
- }
- }
-
- gui.Panels.Projects.SetItems(projects)
-
- if selectedName != "" {
- for i, p := range gui.Panels.Projects.List.GetItems() {
- if p.Name == selectedName {
- gui.Panels.Projects.SetSelectedLineIdx(i)
- gui.Panels.Projects.Refocus()
- break
- }
- }
- }
-
- return gui.Panels.Projects.RerenderList()
-}
-
-// getDiscoveredProjects returns all docker compose projects by examining container labels.
-// The local project (from docker-compose.yml in the current directory) is included if
-// it has running containers or if InDockerComposeProject is true.
-func (gui *Gui) getDiscoveredProjects() []*commands.Project {
- containers := gui.Panels.Containers.List.GetAllItems()
- projectNames := gui.DockerCommand.GetProjectNames(containers)
-
- // If we're in a docker compose project but it has no running containers,
- // still include it. We don't fall back to the directory name here to avoid
- // briefly flashing the wrong project name on startup.
- localName := gui.DockerCommand.LocalProjectName
-
- if gui.DockerCommand.InDockerComposeProject && localName != "" {
- found := false
- for _, name := range projectNames {
- if name == localName {
- found = true
- break
- }
- }
- if !found {
- projectNames = append([]string{localName}, projectNames...)
- }
- }
-
- projects := make([]*commands.Project, len(projectNames))
- for i, name := range projectNames {
- projects[i] = &commands.Project{Name: name}
- }
-
- return projects
-}
-
-// getSelectedProjectName returns the name of the currently selected project,
-// or empty string if none is selected.
-func (gui *Gui) getSelectedProjectName() string {
- project, err := gui.Panels.Projects.GetSelectedItem()
- if err != nil {
- return ""
- }
- return project.Name
-}
-
-func (gui *Gui) renderCredits(_project *commands.Project) tasks.TaskFunc {
- return gui.NewSimpleRenderStringTask(func() string { return gui.creditsStr() })
-}
-
-func (gui *Gui) creditsStr() string {
- var configBuf bytes.Buffer
- _ = yaml.NewEncoder(&configBuf, yaml.IncludeOmitted).Encode(gui.Config.UserConfig)
-
- return strings.Join(
- []string{
- lazydockerTitle(),
- "Copyright (c) 2019 Jesse Duffield",
- "Keybindings: https://github.com/jesseduffield/lazydocker/blob/master/docs/keybindings",
- "Config Options: https://github.com/jesseduffield/lazydocker/blob/master/docs/Config.md",
- "Raise an Issue: https://github.com/jesseduffield/lazydocker/issues",
- utils.ColoredString("Buy Jesse a coffee: https://github.com/sponsors/jesseduffield", color.FgMagenta), // caffeine ain't free
- "Here's your lazydocker config when merged in with the defaults (you can open your config by pressing 'o'):",
- utils.ColoredYamlString(configBuf.String()),
- }, "\n\n")
-}
-
-func (gui *Gui) renderAllLogs(project *commands.Project) tasks.TaskFunc {
- return gui.NewTask(TaskOpts{
- Autoscroll: true,
- Wrap: gui.Config.UserConfig.Gui.WrapMainPanel,
- Func: func(ctx context.Context) {
- gui.clearMainView()
-
- cmd := gui.OSCommand.RunCustomCommand(
- utils.ApplyTemplate(
- gui.Config.UserConfig.CommandTemplates.AllLogs,
- gui.DockerCommand.NewCommandObject(commands.CommandObject{Project: project}),
- ),
- )
-
- cmd.Stdout = gui.Views.Main
- cmd.Stderr = gui.Views.Main
-
- gui.OSCommand.PrepareForChildren(cmd)
- _ = cmd.Start()
-
- go func() {
- <-ctx.Done()
- if err := gui.OSCommand.Kill(cmd); err != nil {
- gui.Log.Error(err)
- }
- }()
-
- _ = cmd.Wait()
- },
- })
-}
-
-func (gui *Gui) renderDockerComposeConfig(project *commands.Project) tasks.TaskFunc {
- if project != nil && project.Name != gui.DockerCommand.LocalProjectName {
- return gui.NewSimpleRenderStringTask(func() string {
- return "Compose config is not available for non-local projects"
- })
- }
- return gui.NewSimpleRenderStringTask(func() string {
- return utils.ColoredYamlString(gui.DockerCommand.DockerComposeConfigForProject(project))
- })
-}
-
-func (gui *Gui) handleOpenConfig(g *gocui.Gui, v *gocui.View) error {
- return gui.openFile(gui.Config.ConfigFilename())
-}
-
-func (gui *Gui) handleEditConfig(g *gocui.Gui, v *gocui.View) error {
- return gui.editFile(gui.Config.ConfigFilename())
-}
-
-func lazydockerTitle() string {
- return `
- _ _ _
- | | | | | |
- | | __ _ _____ _ __| | ___ ___| | _____ _ __
- | |/ _` + "`" + ` |_ / | | |/ _` + "`" + ` |/ _ \ / __| |/ / _ \ '__|
- | | (_| |/ /| |_| | (_| | (_) | (__| < __/ |
- |_|\__,_/___|\__, |\__,_|\___/ \___|_|\_\___|_|
- __/ |
- |___/
-`
-}
-
-// handleViewAllLogs switches to a subprocess viewing all the logs from docker-compose
-func (gui *Gui) handleViewAllLogs(g *gocui.Gui, v *gocui.View) error {
- project, _ := gui.Panels.Projects.GetSelectedItem()
- c, err := gui.DockerCommand.ViewAllLogs(project)
- if err != nil {
- return gui.createErrorPanel(err.Error())
- }
-
- return gui.runSubprocess(c)
-}
diff --git a/pkg/gui/services_panel.go b/pkg/gui/services_panel.go
deleted file mode 100644
index fb768a74..00000000
--- a/pkg/gui/services_panel.go
+++ /dev/null
@@ -1,559 +0,0 @@
-package gui
-
-import (
- "context"
- "fmt"
- "time"
-
- "github.com/fatih/color"
- "github.com/jesseduffield/gocui"
- "github.com/jesseduffield/lazydocker/pkg/commands"
- "github.com/jesseduffield/lazydocker/pkg/config"
- "github.com/jesseduffield/lazydocker/pkg/gui/panels"
- "github.com/jesseduffield/lazydocker/pkg/gui/presentation"
- "github.com/jesseduffield/lazydocker/pkg/gui/types"
- "github.com/jesseduffield/lazydocker/pkg/tasks"
- "github.com/jesseduffield/lazydocker/pkg/utils"
- "github.com/samber/lo"
-)
-
-func (gui *Gui) getServicesPanel() *panels.SideListPanel[*commands.Service] {
- return &panels.SideListPanel[*commands.Service]{
- ContextState: &panels.ContextState[*commands.Service]{
- GetMainTabs: func() []panels.MainTab[*commands.Service] {
- return []panels.MainTab[*commands.Service]{
- {
- Key: "logs",
- Title: gui.Tr.LogsTitle,
- Render: gui.renderServiceLogs,
- },
- {
- Key: "stats",
- Title: gui.Tr.StatsTitle,
- Render: gui.renderServiceStats,
- },
- {
- Key: "container-env",
- Title: gui.Tr.ContainerEnvTitle,
- Render: gui.renderServiceContainerEnv,
- },
- {
- Key: "container-config",
- Title: gui.Tr.ContainerConfigTitle,
- Render: gui.renderServiceContainerConfig,
- },
- {
- Key: "top",
- Title: gui.Tr.TopTitle,
- Render: gui.renderServiceTop,
- },
- }
- },
- GetItemContextCacheKey: func(service *commands.Service) string {
- if service.Container == nil {
- return "services-" + service.ID
- }
- return "services-" + service.ID + "-" + service.Container.ID + "-" + service.Container.Container.State
- },
- },
- ListPanel: panels.ListPanel[*commands.Service]{
- List: panels.NewFilteredList[*commands.Service](),
- View: gui.Views.Services,
- },
- NoItemsMessage: gui.Tr.NoServices,
- Gui: gui.intoInterface(),
- // sort services first by whether they have a linked container, and second by alphabetical order
- Sort: func(a *commands.Service, b *commands.Service) bool {
- if a.Container != nil && b.Container == nil {
- return true
- }
-
- if a.Container == nil && b.Container != nil {
- return false
- }
-
- return a.Name < b.Name
- },
- Filter: func(service *commands.Service) bool {
- selectedProject := gui.getSelectedProjectName()
- if selectedProject == "" {
- // Before any project is selected (e.g. startup), default to
- // the local project so we don't briefly flash all services.
- selectedProject = gui.DockerCommand.LocalProjectName
- }
- if selectedProject == "" {
- return true
- }
- return service.ProjectName == selectedProject
- },
- GetTableCells: func(service *commands.Service) []string {
- return presentation.GetServiceDisplayStrings(&gui.Config.UserConfig.Gui, service)
- },
- Hide: func() bool {
- // Show services panel if there are any compose projects (local or discovered)
- return !gui.DockerCommand.InDockerComposeProject && len(gui.Panels.Services.List.GetAllItems()) == 0
- },
- }
-}
-
-func (gui *Gui) renderServiceContainerConfig(service *commands.Service) tasks.TaskFunc {
- if service.Container == nil {
- return gui.NewSimpleRenderStringTask(func() string { return gui.Tr.NoContainer })
- }
-
- return gui.renderContainerConfig(service.Container)
-}
-
-func (gui *Gui) renderServiceContainerEnv(service *commands.Service) tasks.TaskFunc {
- if service.Container == nil {
- return gui.NewSimpleRenderStringTask(func() string { return gui.Tr.NoContainer })
- }
-
- return gui.renderContainerEnv(service.Container)
-}
-
-func (gui *Gui) renderServiceStats(service *commands.Service) tasks.TaskFunc {
- if service.Container == nil {
- return gui.NewSimpleRenderStringTask(func() string { return gui.Tr.NoContainer })
- }
-
- return gui.renderContainerStats(service.Container)
-}
-
-func (gui *Gui) renderServiceTop(service *commands.Service) tasks.TaskFunc {
- return gui.NewTickerTask(TickerTaskOpts{
- Func: func(ctx context.Context, notifyStopped chan struct{}) {
- contents, err := service.RenderTop(ctx)
- if err != nil {
- gui.RenderStringMain(err.Error())
- }
-
- gui.reRenderStringMain(contents)
- },
- Duration: time.Second,
- Before: func(ctx context.Context) { gui.clearMainView() },
- Wrap: gui.Config.UserConfig.Gui.WrapMainPanel,
- Autoscroll: false,
- })
-}
-
-func (gui *Gui) renderServiceLogs(service *commands.Service) tasks.TaskFunc {
- if service.Container == nil {
- return gui.NewSimpleRenderStringTask(func() string { return gui.Tr.NoContainerForService })
- }
-
- return gui.renderContainerLogsToMain(service.Container)
-}
-
-type commandOption struct {
- description string
- command string
- onPress func() error
-}
-
-func (r *commandOption) getDisplayStrings() []string {
- return []string{r.description, color.New(color.FgCyan).Sprint(r.command)}
-}
-
-// isServiceFromLocalProject returns true if the given service belongs to the
-// local compose project (the one whose compose file is in the current directory).
-// Compose commands like up/stop/restart only work for local project services.
-func (gui *Gui) isServiceFromLocalProject(service *commands.Service) bool {
- return service.ProjectName == gui.DockerCommand.LocalProjectName
-}
-
-func (gui *Gui) handleServiceRemoveMenu(g *gocui.Gui, v *gocui.View) error {
- service, err := gui.Panels.Services.GetSelectedItem()
- if err != nil {
- return nil
- }
-
- if !gui.isServiceFromLocalProject(service) {
- return gui.createErrorPanel(gui.Tr.CannotManageNonLocalService)
- }
-
- composeCommand := gui.DockerCommand.NewCommandObject(commands.CommandObject{Service: service}).DockerCompose
-
- options := []*commandOption{
- {
- description: gui.Tr.Remove,
- command: fmt.Sprintf("%s rm --stop --force %s", composeCommand, service.Name),
- },
- {
- description: gui.Tr.RemoveWithVolumes,
- command: fmt.Sprintf("%s rm --stop --force -v %s", composeCommand, service.Name),
- },
- }
-
- menuItems := lo.Map(options, func(option *commandOption, _ int) *types.MenuItem {
- return &types.MenuItem{
- LabelColumns: option.getDisplayStrings(),
- OnPress: func() error {
- return gui.WithWaitingStatus(gui.Tr.RemovingStatus, func() error {
- if err := gui.OSCommand.RunCommand(option.command); err != nil {
- return gui.createErrorPanel(err.Error())
- }
-
- return nil
- })
- },
- }
- })
-
- return gui.Menu(CreateMenuOptions{
- Title: "",
- Items: menuItems,
- })
-}
-
-func (gui *Gui) handleServicePause(g *gocui.Gui, v *gocui.View) error {
- service, err := gui.Panels.Services.GetSelectedItem()
- if err != nil {
- return nil
- }
- if service.Container == nil {
- return nil
- }
-
- return gui.PauseContainer(service.Container)
-}
-
-func (gui *Gui) handleServiceStop(g *gocui.Gui, v *gocui.View) error {
- service, err := gui.Panels.Services.GetSelectedItem()
- if err != nil {
- return nil
- }
-
- return gui.createConfirmationPanel(gui.Tr.Confirm, gui.Tr.StopService, func(g *gocui.Gui, v *gocui.View) error {
- return gui.WithWaitingStatus(gui.Tr.StoppingStatus, func() error {
- if !gui.isServiceFromLocalProject(service) {
- if service.Container == nil {
- return gui.createErrorPanel(gui.Tr.CannotManageNonLocalService)
- }
- return service.Container.Stop()
- }
- if err := service.Stop(); err != nil {
- return gui.createErrorPanel(err.Error())
- }
- return nil
- })
- }, nil)
-}
-
-func (gui *Gui) handleServiceUp(g *gocui.Gui, v *gocui.View) error {
- service, err := gui.Panels.Services.GetSelectedItem()
- if err != nil {
- return nil
- }
-
- if !gui.isServiceFromLocalProject(service) {
- return gui.createErrorPanel(gui.Tr.CannotManageNonLocalService)
- }
-
- return gui.WithWaitingStatus(gui.Tr.UppingServiceStatus, func() error {
- if err := service.Up(); err != nil {
- return gui.createErrorPanel(err.Error())
- }
-
- return nil
- })
-}
-
-func (gui *Gui) handleServiceRestart(g *gocui.Gui, v *gocui.View) error {
- service, err := gui.Panels.Services.GetSelectedItem()
- if err != nil {
- return nil
- }
-
- return gui.WithWaitingStatus(gui.Tr.RestartingStatus, func() error {
- if !gui.isServiceFromLocalProject(service) {
- if service.Container == nil {
- return gui.createErrorPanel(gui.Tr.CannotManageNonLocalService)
- }
- return service.Container.Restart()
- }
- if err := service.Restart(); err != nil {
- return gui.createErrorPanel(err.Error())
- }
- return nil
- })
-}
-
-func (gui *Gui) handleServiceStart(g *gocui.Gui, v *gocui.View) error {
- service, err := gui.Panels.Services.GetSelectedItem()
- if err != nil {
- return nil
- }
-
- if !gui.isServiceFromLocalProject(service) {
- if service.Container == nil {
- return gui.createErrorPanel(gui.Tr.CannotManageNonLocalService)
- }
- return gui.WithWaitingStatus(gui.Tr.StartingStatus, func() error {
- return service.Container.Start()
- })
- }
-
- return gui.WithWaitingStatus(gui.Tr.StartingStatus, func() error {
- if err := service.Start(); err != nil {
- return gui.createErrorPanel(err.Error())
- }
- return nil
- })
-}
-
-func (gui *Gui) handleServiceAttach(g *gocui.Gui, v *gocui.View) error {
- service, err := gui.Panels.Services.GetSelectedItem()
- if err != nil {
- return nil
- }
-
- if service.Container == nil {
- return gui.createErrorPanel(gui.Tr.NoContainers)
- }
-
- c, err := service.Attach()
- if err != nil {
- return gui.createErrorPanel(err.Error())
- }
-
- return gui.runSubprocess(c)
-}
-
-func (gui *Gui) handleServiceRenderLogsToMain(g *gocui.Gui, v *gocui.View) error {
- service, err := gui.Panels.Services.GetSelectedItem()
- if err != nil {
- return nil
- }
-
- c, err := service.ViewLogs()
- if err != nil {
- return gui.createErrorPanel(err.Error())
- }
-
- return gui.runSubprocess(c)
-}
-
-func (gui *Gui) handleProjectUp(g *gocui.Gui, v *gocui.View) error {
- project, _ := gui.Panels.Projects.GetSelectedItem()
- if project != nil && project.Name != gui.DockerCommand.LocalProjectName {
- return gui.createErrorPanel(gui.Tr.CannotManageNonLocalService)
- }
- return gui.createConfirmationPanel(gui.Tr.Confirm, gui.Tr.ConfirmUpProject, func(g *gocui.Gui, v *gocui.View) error {
- cmdStr := utils.ApplyTemplate(
- gui.Config.UserConfig.CommandTemplates.Up,
- gui.DockerCommand.NewCommandObject(commands.CommandObject{Project: project}),
- )
-
- return gui.WithWaitingStatus(gui.Tr.UppingProjectStatus, func() error {
- if err := gui.OSCommand.RunCommand(cmdStr); err != nil {
- return gui.createErrorPanel(err.Error())
- }
- return nil
- })
- }, nil)
-}
-
-func (gui *Gui) handleProjectDown(g *gocui.Gui, v *gocui.View) error {
- project, _ := gui.Panels.Projects.GetSelectedItem()
- if project != nil && project.Name != gui.DockerCommand.LocalProjectName {
- return gui.createErrorPanel(gui.Tr.CannotManageNonLocalService)
- }
- downCommand := utils.ApplyTemplate(
- gui.Config.UserConfig.CommandTemplates.Down,
- gui.DockerCommand.NewCommandObject(commands.CommandObject{Project: project}),
- )
-
- downWithVolumesCommand := utils.ApplyTemplate(
- gui.Config.UserConfig.CommandTemplates.DownWithVolumes,
- gui.DockerCommand.NewCommandObject(commands.CommandObject{Project: project}),
- )
-
- options := []*commandOption{
- {
- description: gui.Tr.Down,
- command: downCommand,
- onPress: func() error {
- return gui.WithWaitingStatus(gui.Tr.DowningStatus, func() error {
- if err := gui.OSCommand.RunCommand(downCommand); err != nil {
- return gui.createErrorPanel(err.Error())
- }
- return nil
- })
- },
- },
- {
- description: gui.Tr.DownWithVolumes,
- command: downWithVolumesCommand,
- onPress: func() error {
- return gui.WithWaitingStatus(gui.Tr.DowningStatus, func() error {
- if err := gui.OSCommand.RunCommand(downWithVolumesCommand); err != nil {
- return gui.createErrorPanel(err.Error())
- }
- return nil
- })
- },
- },
- }
-
- menuItems := lo.Map(options, func(option *commandOption, _ int) *types.MenuItem {
- return &types.MenuItem{
- LabelColumns: option.getDisplayStrings(),
- OnPress: option.onPress,
- }
- })
-
- return gui.Menu(CreateMenuOptions{
- Title: "",
- Items: menuItems,
- })
-}
-
-func (gui *Gui) handleServiceRestartMenu(g *gocui.Gui, v *gocui.View) error {
- service, err := gui.Panels.Services.GetSelectedItem()
- if err != nil {
- return nil
- }
-
- if !gui.isServiceFromLocalProject(service) {
- return gui.createErrorPanel(gui.Tr.CannotManageNonLocalService)
- }
-
- rebuildCommand := utils.ApplyTemplate(
- gui.Config.UserConfig.CommandTemplates.RebuildService,
- gui.DockerCommand.NewCommandObject(commands.CommandObject{Service: service}),
- )
-
- recreateCommand := utils.ApplyTemplate(
- gui.Config.UserConfig.CommandTemplates.RecreateService,
- gui.DockerCommand.NewCommandObject(commands.CommandObject{Service: service}),
- )
-
- options := []*commandOption{
- {
- description: gui.Tr.Restart,
- command: utils.ApplyTemplate(
- gui.Config.UserConfig.CommandTemplates.RestartService,
- gui.DockerCommand.NewCommandObject(commands.CommandObject{Service: service}),
- ),
- onPress: func() error {
- return gui.WithWaitingStatus(gui.Tr.RestartingStatus, func() error {
- if err := service.Restart(); err != nil {
- return gui.createErrorPanel(err.Error())
- }
- return nil
- })
- },
- },
- {
- description: gui.Tr.Recreate,
- command: utils.ApplyTemplate(
- gui.Config.UserConfig.CommandTemplates.RecreateService,
- gui.DockerCommand.NewCommandObject(commands.CommandObject{Service: service}),
- ),
- onPress: func() error {
- return gui.WithWaitingStatus(gui.Tr.RestartingStatus, func() error {
- if err := gui.OSCommand.RunCommand(recreateCommand); err != nil {
- return gui.createErrorPanel(err.Error())
- }
- return nil
- })
- },
- },
- {
- description: gui.Tr.Rebuild,
- command: utils.ApplyTemplate(
- gui.Config.UserConfig.CommandTemplates.RebuildService,
- gui.DockerCommand.NewCommandObject(commands.CommandObject{Service: service}),
- ),
- onPress: func() error {
- return gui.runSubprocess(gui.OSCommand.RunCustomCommand(rebuildCommand))
- },
- },
- }
-
- menuItems := lo.Map(options, func(option *commandOption, _ int) *types.MenuItem {
- return &types.MenuItem{
- LabelColumns: option.getDisplayStrings(),
- OnPress: option.onPress,
- }
- })
-
- return gui.Menu(CreateMenuOptions{
- Title: "",
- Items: menuItems,
- })
-}
-
-func (gui *Gui) handleServicesCustomCommand(g *gocui.Gui, v *gocui.View) error {
- service, err := gui.Panels.Services.GetSelectedItem()
- if err != nil {
- return nil
- }
-
- commandObject := gui.DockerCommand.NewCommandObject(commands.CommandObject{
- Service: service,
- Container: service.Container,
- })
-
- var customCommands []config.CustomCommand
-
- customServiceCommands := gui.Config.UserConfig.CustomCommands.Services
- // we only include service commands if they have no serviceNames defined or if our service happens to be one of the serviceNames defined
-L:
- for _, cmd := range customServiceCommands {
- if len(cmd.ServiceNames) == 0 {
- customCommands = append(customCommands, cmd)
- continue L
- }
- for _, serviceName := range cmd.ServiceNames {
- if serviceName == service.Name {
- // appending these to the top given they're more likely to be selected
- customCommands = append([]config.CustomCommand{cmd}, customCommands...)
- continue L
- }
- }
- }
-
- if service.Container != nil {
- customCommands = append(customCommands, gui.Config.UserConfig.CustomCommands.Containers...)
- }
-
- return gui.createCustomCommandMenu(customCommands, commandObject)
-}
-
-func (gui *Gui) handleServicesBulkCommand(g *gocui.Gui, v *gocui.View) error {
- project, _ := gui.Panels.Projects.GetSelectedItem()
- bulkCommands := gui.Config.UserConfig.BulkCommands.Services
- commandObject := gui.DockerCommand.NewCommandObject(commands.CommandObject{Project: project})
-
- return gui.createBulkCommandMenu(bulkCommands, commandObject)
-}
-
-func (gui *Gui) handleServicesExecShell(g *gocui.Gui, v *gocui.View) error {
- service, err := gui.Panels.Services.GetSelectedItem()
- if err != nil {
- return nil
- }
-
- container := service.Container
- if container == nil {
- return gui.createErrorPanel(gui.Tr.NoContainers)
- }
-
- return gui.containerExecShell(container)
-}
-
-func (gui *Gui) handleServicesOpenInBrowserCommand(g *gocui.Gui, v *gocui.View) error {
- service, err := gui.Panels.Services.GetSelectedItem()
- if err != nil {
- return nil
- }
-
- container := service.Container
- if container == nil {
- return gui.createErrorPanel(gui.Tr.NoContainers)
- }
-
- return gui.openContainerInBrowser(container)
-}
diff --git a/pkg/gui/sort_container_test.go b/pkg/gui/sort_container_test.go
index a8af434c..336db39a 100644
--- a/pkg/gui/sort_container_test.go
+++ b/pkg/gui/sort_container_test.go
@@ -4,40 +4,31 @@ import (
"sort"
"testing"
- "github.com/docker/docker/api/types/container"
- "github.com/jesseduffield/lazydocker/pkg/commands"
+ "github.com/jesseduffield/lazycontainer/pkg/commands"
"github.com/stretchr/testify/assert"
)
func sampleContainers() []*commands.Container {
return []*commands.Container{
{
- ID: "1",
- Name: "1",
- Container: container.Summary{
- State: "exited",
- },
+ ID: "1",
+ Name: "1",
+ AppleContainer: commands.AppleContainer{Status: "exited"},
},
{
- ID: "2",
- Name: "2",
- Container: container.Summary{
- State: "running",
- },
+ ID: "2",
+ Name: "2",
+ AppleContainer: commands.AppleContainer{Status: "running"},
},
{
- ID: "3",
- Name: "3",
- Container: container.Summary{
- State: "running",
- },
+ ID: "3",
+ Name: "3",
+ AppleContainer: commands.AppleContainer{Status: "running"},
},
{
- ID: "4",
- Name: "4",
- Container: container.Summary{
- State: "created",
- },
+ ID: "4",
+ Name: "4",
+ AppleContainer: commands.AppleContainer{Status: "created"},
},
}
}
@@ -45,32 +36,24 @@ func sampleContainers() []*commands.Container {
func expectedPerStatusContainers() []*commands.Container {
return []*commands.Container{
{
- ID: "2",
- Name: "2",
- Container: container.Summary{
- State: "running",
- },
+ ID: "2",
+ Name: "2",
+ AppleContainer: commands.AppleContainer{Status: "running"},
},
{
- ID: "3",
- Name: "3",
- Container: container.Summary{
- State: "running",
- },
+ ID: "3",
+ Name: "3",
+ AppleContainer: commands.AppleContainer{Status: "running"},
},
{
- ID: "1",
- Name: "1",
- Container: container.Summary{
- State: "exited",
- },
+ ID: "1",
+ Name: "1",
+ AppleContainer: commands.AppleContainer{Status: "exited"},
},
{
- ID: "4",
- Name: "4",
- Container: container.Summary{
- State: "created",
- },
+ ID: "4",
+ Name: "4",
+ AppleContainer: commands.AppleContainer{Status: "created"},
},
}
}
@@ -78,40 +61,32 @@ func expectedPerStatusContainers() []*commands.Container {
func expectedLegacySortedContainers() []*commands.Container {
return []*commands.Container{
{
- ID: "1",
- Name: "1",
- Container: container.Summary{
- State: "exited",
- },
+ ID: "1",
+ Name: "1",
+ AppleContainer: commands.AppleContainer{Status: "exited"},
},
{
- ID: "2",
- Name: "2",
- Container: container.Summary{
- State: "running",
- },
+ ID: "2",
+ Name: "2",
+ AppleContainer: commands.AppleContainer{Status: "running"},
},
{
- ID: "3",
- Name: "3",
- Container: container.Summary{
- State: "running",
- },
+ ID: "3",
+ Name: "3",
+ AppleContainer: commands.AppleContainer{Status: "running"},
},
{
- ID: "4",
- Name: "4",
- Container: container.Summary{
- State: "created",
- },
+ ID: "4",
+ Name: "4",
+ AppleContainer: commands.AppleContainer{Status: "created"},
},
}
}
func assertEqualContainers(t *testing.T, left *commands.Container, right *commands.Container) {
t.Helper()
- assert.Equal(t, left.Container.State, right.Container.State)
- assert.Equal(t, left.Container.ID, right.Container.ID)
+ assert.Equal(t, left.GetStatus(), right.GetStatus())
+ assert.Equal(t, left.ID, right.ID)
assert.Equal(t, left.Name, right.Name)
}
diff --git a/pkg/gui/subprocess.go b/pkg/gui/subprocess.go
index b41ac475..40291ace 100644
--- a/pkg/gui/subprocess.go
+++ b/pkg/gui/subprocess.go
@@ -9,7 +9,7 @@ import (
"strings"
"github.com/fatih/color"
- "github.com/jesseduffield/lazydocker/pkg/utils"
+ "github.com/jesseduffield/lazycontainer/pkg/utils"
)
func (gui *Gui) runSubprocess(cmd *exec.Cmd) error {
diff --git a/pkg/gui/tasks_adapter.go b/pkg/gui/tasks_adapter.go
index f85e4723..3a53850d 100644
--- a/pkg/gui/tasks_adapter.go
+++ b/pkg/gui/tasks_adapter.go
@@ -4,7 +4,7 @@ import (
"context"
"time"
- "github.com/jesseduffield/lazydocker/pkg/tasks"
+ "github.com/jesseduffield/lazycontainer/pkg/tasks"
)
func (gui *Gui) QueueTask(f func(ctx context.Context)) error {
diff --git a/pkg/gui/view_helpers.go b/pkg/gui/view_helpers.go
index 6acbb18c..74a84d40 100644
--- a/pkg/gui/view_helpers.go
+++ b/pkg/gui/view_helpers.go
@@ -6,8 +6,8 @@ import (
"strings"
"github.com/jesseduffield/gocui"
- "github.com/jesseduffield/lazydocker/pkg/gui/panels"
- "github.com/jesseduffield/lazydocker/pkg/utils"
+ "github.com/jesseduffield/lazycontainer/pkg/gui/panels"
+ "github.com/jesseduffield/lazycontainer/pkg/utils"
"github.com/samber/lo"
"github.com/spkg/bom"
)
@@ -364,8 +364,6 @@ func (gui *Gui) currentListPanel() (panels.ISideListPanel, bool) {
func (gui *Gui) allSidePanels() []panels.ISideListPanel {
return []panels.ISideListPanel{
- gui.Panels.Projects,
- gui.Panels.Services,
gui.Panels.Containers,
gui.Panels.Images,
gui.Panels.Volumes,
diff --git a/pkg/gui/views.go b/pkg/gui/views.go
index 95553f5a..b03f8eb8 100644
--- a/pkg/gui/views.go
+++ b/pkg/gui/views.go
@@ -8,9 +8,6 @@ import (
"github.com/samber/lo"
)
-// See https://github.com/xtermjs/xterm.js/issues/4238
-// VSCode is soon to fix this in an upcoming update.
-// Once that's done, we can scrap the HIDE_UNDERSCORES variable
var (
underscoreEnvChecked bool
hideUnderscores bool
@@ -26,48 +23,33 @@ func hideUnderScores() bool {
}
type Views struct {
- // side panels
- Project *gocui.View
- Services *gocui.View
Containers *gocui.View
Images *gocui.View
Volumes *gocui.View
Networks *gocui.View
- // main panel
Main *gocui.View
- // bottom line
Options *gocui.View
Information *gocui.View
AppStatus *gocui.View
- // text that prompts you to enter text in the Filter view
FilterPrefix *gocui.View
- // appears next to the SearchPrefix view, it's where you type in the search string
Filter *gocui.View
- // popups
Confirmation *gocui.View
Menu *gocui.View
- // will cover everything when it appears
Limit *gocui.View
}
type viewNameMapping struct {
viewPtr **gocui.View
name string
- // if true, we handle the position/size of the view in arrangement.go. Otherwise
- // we handle it manually.
autoPosition bool
}
func (gui *Gui) orderedViewNameMappings() []viewNameMapping {
return []viewNameMapping{
- // first layer. Ordering within this layer does not matter because there are
- // no overlapping views
- {viewPtr: &gui.Views.Project, name: "project", autoPosition: true},
- {viewPtr: &gui.Views.Services, name: "services", autoPosition: true},
{viewPtr: &gui.Views.Containers, name: "containers", autoPosition: true},
{viewPtr: &gui.Views.Images, name: "images", autoPosition: true},
{viewPtr: &gui.Views.Volumes, name: "volumes", autoPosition: true},
@@ -75,18 +57,15 @@ func (gui *Gui) orderedViewNameMappings() []viewNameMapping {
{viewPtr: &gui.Views.Main, name: "main", autoPosition: true},
- // bottom line
{viewPtr: &gui.Views.Options, name: "options", autoPosition: true},
{viewPtr: &gui.Views.AppStatus, name: "appStatus", autoPosition: true},
{viewPtr: &gui.Views.Information, name: "information", autoPosition: true},
{viewPtr: &gui.Views.Filter, name: "filter", autoPosition: true},
{viewPtr: &gui.Views.FilterPrefix, name: "filterPrefix", autoPosition: true},
- // popups.
{viewPtr: &gui.Views.Menu, name: "menu", autoPosition: false},
{viewPtr: &gui.Views.Confirmation, name: "confirmation", autoPosition: false},
- // this guy will cover everything else when it appears
{viewPtr: &gui.Views.Limit, name: "limit", autoPosition: true},
}
}
@@ -115,41 +94,26 @@ func (gui *Gui) createAllViews() error {
selectedLineBgColor := GetGocuiStyle(gui.Config.UserConfig.Gui.Theme.SelectedLineBgColor)
gui.Views.Main.Wrap = gui.Config.UserConfig.Gui.WrapMainPanel
- // when you run a docker container with the -it flags (interactive mode) it adds carriage returns for some reason. This is not docker's fault, it's an os-level default.
gui.Views.Main.IgnoreCarriageReturns = true
- gui.Views.Project.Title = gui.Tr.ProjectTitle
- gui.Views.Project.TitlePrefix = "[1]"
- gui.Views.Project.Highlight = true
- gui.Views.Project.SelBgColor = selectedLineBgColor
-
- gui.Views.Services.Highlight = true
- gui.Views.Services.Title = gui.Tr.ServicesTitle
- gui.Views.Services.TitlePrefix = "[2]"
- gui.Views.Services.SelBgColor = selectedLineBgColor
-
gui.Views.Containers.Highlight = true
gui.Views.Containers.SelBgColor = selectedLineBgColor
- if gui.Config.UserConfig.Gui.ShowAllContainers || !gui.DockerCommand.InDockerComposeProject {
- gui.Views.Containers.Title = gui.Tr.ContainersTitle
- } else {
- gui.Views.Containers.Title = gui.Tr.StandaloneContainersTitle
- }
- gui.Views.Containers.TitlePrefix = "[3]"
+ gui.Views.Containers.Title = gui.Tr.ContainersTitle
+ gui.Views.Containers.TitlePrefix = "[1]"
gui.Views.Images.Highlight = true
gui.Views.Images.Title = gui.Tr.ImagesTitle
gui.Views.Images.SelBgColor = selectedLineBgColor
- gui.Views.Images.TitlePrefix = "[4]"
+ gui.Views.Images.TitlePrefix = "[2]"
gui.Views.Volumes.Highlight = true
gui.Views.Volumes.Title = gui.Tr.VolumesTitle
- gui.Views.Volumes.TitlePrefix = "[5]"
+ gui.Views.Volumes.TitlePrefix = "[3]"
gui.Views.Volumes.SelBgColor = selectedLineBgColor
gui.Views.Networks.Highlight = true
gui.Views.Networks.Title = gui.Tr.NetworksTitle
- gui.Views.Networks.TitlePrefix = "[6]"
+ gui.Views.Networks.TitlePrefix = "[4]"
gui.Views.Networks.SelBgColor = selectedLineBgColor
gui.Views.Options.Frame = false
@@ -212,7 +176,6 @@ func (gui *Gui) popupViewNames() []string {
return []string{"confirmation", "menu"}
}
-// these views have their position and size determined by arrangement.go
func (gui *Gui) autoPositionedViewNames() []string {
views := lo.Filter(gui.orderedViewNameMappings(), func(viewNameMapping viewNameMapping, _ int) bool {
return viewNameMapping.autoPosition
diff --git a/pkg/gui/volumes_panel.go b/pkg/gui/volumes_panel.go
index 96085949..e796907b 100644
--- a/pkg/gui/volumes_panel.go
+++ b/pkg/gui/volumes_panel.go
@@ -1,17 +1,15 @@
package gui
import (
- "fmt"
-
"github.com/fatih/color"
"github.com/jesseduffield/gocui"
- "github.com/jesseduffield/lazydocker/pkg/commands"
- "github.com/jesseduffield/lazydocker/pkg/config"
- "github.com/jesseduffield/lazydocker/pkg/gui/panels"
- "github.com/jesseduffield/lazydocker/pkg/gui/presentation"
- "github.com/jesseduffield/lazydocker/pkg/gui/types"
- "github.com/jesseduffield/lazydocker/pkg/tasks"
- "github.com/jesseduffield/lazydocker/pkg/utils"
+ "github.com/jesseduffield/lazycontainer/pkg/commands"
+ "github.com/jesseduffield/lazycontainer/pkg/config"
+ "github.com/jesseduffield/lazycontainer/pkg/gui/panels"
+ "github.com/jesseduffield/lazycontainer/pkg/gui/presentation"
+ "github.com/jesseduffield/lazycontainer/pkg/gui/types"
+ "github.com/jesseduffield/lazycontainer/pkg/tasks"
+ "github.com/jesseduffield/lazycontainer/pkg/utils"
"github.com/samber/lo"
)
@@ -37,16 +35,7 @@ func (gui *Gui) getVolumesPanel() *panels.SideListPanel[*commands.Volume] {
},
NoItemsMessage: gui.Tr.NoVolumes,
Gui: gui.intoInterface(),
- // we're sorting these volumes based on whether they have labels defined,
- // because those are the ones you typically care about.
- // Within that, we also sort them alphabetically
Sort: func(a *commands.Volume, b *commands.Volume) bool {
- if len(a.Volume.Labels) == 0 && len(b.Volume.Labels) > 0 {
- return false
- }
- if len(a.Volume.Labels) > 0 && len(b.Volume.Labels) == 0 {
- return true
- }
return a.Name < b.Name
},
GetTableCells: presentation.GetVolumeDisplayStrings,
@@ -61,26 +50,7 @@ func (gui *Gui) volumeConfigStr(volume *commands.Volume) string {
padding := 15
output := ""
output += utils.WithPadding("Name: ", padding) + volume.Name + "\n"
- output += utils.WithPadding("Driver: ", padding) + volume.Volume.Driver + "\n"
- output += utils.WithPadding("Scope: ", padding) + volume.Volume.Scope + "\n"
- output += utils.WithPadding("Mountpoint: ", padding) + volume.Volume.Mountpoint + "\n"
- output += utils.WithPadding("Labels: ", padding) + utils.FormatMap(padding, volume.Volume.Labels) + "\n"
- output += utils.WithPadding("Options: ", padding) + utils.FormatMap(padding, volume.Volume.Options) + "\n"
-
- output += utils.WithPadding("Status: ", padding)
- if volume.Volume.Status != nil {
- output += "\n"
- for k, v := range volume.Volume.Status {
- output += utils.FormatMapItem(padding, k, v)
- }
- } else {
- output += "n/a"
- }
-
- if volume.Volume.UsageData != nil {
- output += utils.WithPadding("RefCount: ", padding) + fmt.Sprintf("%d", volume.Volume.UsageData.RefCount) + "\n"
- output += utils.WithPadding("Size: ", padding) + utils.FormatBinaryBytes(int(volume.Volume.UsageData.Size)) + "\n"
- }
+ output += utils.WithPadding("Size: ", padding) + utils.FormatBinaryBytes(int(volume.AppleVolume.SizeInBytes)) + "\n"
return output
}
@@ -94,7 +64,7 @@ func (gui *Gui) reloadVolumes() error {
}
func (gui *Gui) refreshStateVolumes() error {
- volumes, err := gui.DockerCommand.RefreshVolumes()
+ volumes, err := gui.ContainerCmd.RefreshVolumes()
if err != nil {
return err
}
@@ -113,19 +83,12 @@ func (gui *Gui) handleVolumesRemoveMenu(g *gocui.Gui, v *gocui.View) error {
type removeVolumeOption struct {
description string
command string
- force bool
}
options := []*removeVolumeOption{
{
description: gui.Tr.Remove,
- command: utils.WithShortSha("docker volume rm " + volume.Name),
- force: false,
- },
- {
- description: gui.Tr.ForceRemove,
- command: utils.WithShortSha("docker volume rm --force " + volume.Name),
- force: true,
+ command: utils.WithShortSha("container volume rm " + volume.Name),
},
}
@@ -134,7 +97,7 @@ func (gui *Gui) handleVolumesRemoveMenu(g *gocui.Gui, v *gocui.View) error {
LabelColumns: []string{option.description, color.New(color.FgRed).Sprint(option.command)},
OnPress: func() error {
return gui.WithWaitingStatus(gui.Tr.RemovingStatus, func() error {
- if err := volume.Remove(option.force); err != nil {
+ if err := volume.Remove(); err != nil {
return gui.createErrorPanel(err.Error())
}
return nil
@@ -152,7 +115,7 @@ func (gui *Gui) handleVolumesRemoveMenu(g *gocui.Gui, v *gocui.View) error {
func (gui *Gui) handlePruneVolumes() error {
return gui.createConfirmationPanel(gui.Tr.Confirm, gui.Tr.ConfirmPruneVolumes, func(g *gocui.Gui, v *gocui.View) error {
return gui.WithWaitingStatus(gui.Tr.PruningStatus, func() error {
- err := gui.DockerCommand.PruneVolumes()
+ err := gui.ContainerCmd.PruneVolumes()
if err != nil {
return gui.createErrorPanel(err.Error())
}
@@ -167,7 +130,7 @@ func (gui *Gui) handleVolumesCustomCommand(g *gocui.Gui, v *gocui.View) error {
return nil
}
- commandObject := gui.DockerCommand.NewCommandObject(commands.CommandObject{
+ commandObject := gui.ContainerCmd.Client.NewCommandObject(commands.CommandObject{
Volume: volume,
})
@@ -185,7 +148,7 @@ func (gui *Gui) handleVolumesBulkCommand(g *gocui.Gui, v *gocui.View) error {
}
bulkCommands := append(baseBulkCommands, gui.Config.UserConfig.BulkCommands.Volumes...)
- commandObject := gui.DockerCommand.NewCommandObject(commands.CommandObject{})
+ commandObject := gui.ContainerCmd.Client.NewCommandObject(commands.CommandObject{})
return gui.createBulkCommandMenu(bulkCommands, commandObject)
}
diff --git a/pkg/i18n/chinese.go b/pkg/i18n/chinese.go
index aa125f0b..90e3f26d 100644
--- a/pkg/i18n/chinese.go
+++ b/pkg/i18n/chinese.go
@@ -16,12 +16,12 @@ func chineseSet() TranslationSet {
NoViewMachingNewLineFocusedSwitchStatement: "没有匹配 newLineFocused switch 语句的视图",
- ErrorOccurred: "发生错误!请在 https://github.com/jesseduffield/lazydocker/issues 上创建一个问题",
+ ErrorOccurred: "发生错误!请在 https://github.com/g-battaglia/lazy-apple-container/issues 上创建一个问题",
ConnectionFailed: "无法连接到 Docker 客户端。您可能需要重新启动 Docker 客户端",
UnattachableContainerError: "容器不支持 attaching。您必须使用“-it”标志运行服务,或者在docker-compose.yml文件中使用`stdin_open: true,tty: true`",
WaitingForContainerInfo: "在 Docker 给我们更多关于容器的信息之前,无法继续。请几分钟后重试。",
CannotAttachStoppedContainerError: "您不能 attach 到已停止的容器,您需要先启动它(您可以用 'r' 键来执行此操作)(是的,我懒得为您自动执行此操作)(很酷的是,我可以通过错误消息与您进行一对一的通讯)",
- CannotAccessDockerSocketError: "无法访问 Docker 套接字:unix:///var/run/docker.sock\n请以 root 用户身份运行 lazydocker 或阅读https://docs.docker.com/install/linux/linux-postinstall/",
+ CannotAccessDockerSocketError: "无法访问 Docker 套接字:unix:///var/run/docker.sock\n请以 root 用户身份运行 lazyapple 或阅读https://docs.docker.com/install/linux/linux-postinstall/",
CannotKillChildError: "等待三秒钟以停止子进程。可能有一个孤儿进程在您的系统上继续运行。",
Donate: "捐赠",
@@ -37,8 +37,8 @@ func chineseSet() TranslationSet {
Menu: "菜单",
MenuTitle: "菜单",
Scroll: "滚动",
- OpenConfig: "打开lazydocker配置",
- EditConfig: "编辑lazydocker配置",
+ OpenConfig: "打开lazyapple配置",
+ EditConfig: "编辑lazyapple配置",
Cancel: "取消",
Remove: "移除",
HideStopped: "隐藏/显示已停止的容器",
@@ -124,7 +124,7 @@ func chineseSet() TranslationSet {
ConfirmPruneNetworks: "您确定要删除所有未使用的网络吗?",
StopService: "您确定要停止此服务的容器吗?",
StopContainer: "您确定要停止此容器吗?",
- PressEnterToReturn: "按 enter 返回 lazydocker(您可以在配置文件中设置 `gui.returnImmediately: true` 来禁用此提示)",
+ PressEnterToReturn: "按 enter 返回 lazyapple(您可以在配置文件中设置 `gui.returnImmediately: true` 来禁用此提示)",
No: "否",
Yes: "是",
diff --git a/pkg/i18n/dutch.go b/pkg/i18n/dutch.go
index 8a8601c2..328398db 100644
--- a/pkg/i18n/dutch.go
+++ b/pkg/i18n/dutch.go
@@ -10,11 +10,11 @@ func dutchSet() TranslationSet {
NoViewMachingNewLineFocusedSwitchStatement: "No view matching newLineFocused switch statement",
- ErrorOccurred: "Er is iets fout gegaan! Zou je hier een issue aan willen maken: https://github.com/jesseduffield/lazydocker/issues",
+ ErrorOccurred: "Er is iets fout gegaan! Zou je hier een issue aan willen maken: https://github.com/g-battaglia/lazy-apple-container/issues",
ConnectionFailed: "connectie naar de docker client mislukt. Het zou kunnen dat je de docker client moet herstarten",
UnattachableContainerError: "Container heeft geen ondersteuning voor vastmaken. Je zou de service met het '-it' argument kunnen draaien of stop dit in je `stdin_open: true, tty: true` docker-compose.yml",
CannotAttachStoppedContainerError: "Je kan niet een vastgemaakte container stoppen, je moet het eerst starten (dit kan je doen met de 'r' toets) (ja ik ben te leu om dat voor je te doen automatisch)",
- CannotAccessDockerSocketError: "Kan de docker socket niet bereiken: unix:///var/run/docker.sock\nDraai lazydocker als root of lees https://docs.docker.com/install/linux/linux-postinstall/",
+ CannotAccessDockerSocketError: "Kan de docker socket niet bereiken: unix:///var/run/docker.sock\nDraai lazyapple als root of lees https://docs.docker.com/install/linux/linux-postinstall/",
Donate: "Doneer",
Confirm: "Bevestigen",
@@ -27,8 +27,8 @@ func dutchSet() TranslationSet {
Menu: "menu",
MenuTitle: "Menu",
Scroll: "scroll",
- OpenConfig: "open de lazydocker configuratie",
- EditConfig: "verander de lazydocker configuratie",
+ OpenConfig: "open de lazyapple configuratie",
+ EditConfig: "verander de lazyapple configuratie",
Cancel: "annuleren",
Remove: "verwijder",
HideStopped: "verberg gestopte containers",
@@ -92,7 +92,7 @@ func dutchSet() TranslationSet {
ConfirmPruneNetworks: "Weet je zeker dat je alle niet gebruikte networks wil vernietigen?",
StopService: "Weet je zeker dat je deze service zijn containers wil stoppen?",
StopContainer: "Weet je zeker dat je deze container wil stoppen?",
- PressEnterToReturn: "Druk op enter om terug te gaan naar lazydocker (Deze popup kan uit gezet worden door in de config dit neer te zetten `gui.returnImmediately: true`)",
+ PressEnterToReturn: "Druk op enter om terug te gaan naar lazyapple (Deze popup kan uit gezet worden door in de config dit neer te zetten `gui.returnImmediately: true`)",
DetachFromContainerShortCut: "Als u wilt loskoppelen van de container, drukt u standaard op ctrl-p en vervolgens op ctrl-q",
}
}
diff --git a/pkg/i18n/english.go b/pkg/i18n/english.go
index d86b0c84..b9a3d5a6 100644
--- a/pkg/i18n/english.go
+++ b/pkg/i18n/english.go
@@ -136,6 +136,14 @@ type TranslationSet struct {
FocusImages string
FocusVolumes string
FocusNetworks string
+
+ Kill string
+ KillingStatus string
+ RemoveContainer string
+ ContainerNotRunning string
+ NoIp string
+ NoPorts string
+ NoStats string
}
func englishSet() TranslationSet {
@@ -154,12 +162,12 @@ func englishSet() TranslationSet {
NoViewMachingNewLineFocusedSwitchStatement: "No view matching newLineFocused switch statement",
- ErrorOccurred: "An error occurred! Please create an issue at https://github.com/jesseduffield/lazydocker/issues",
+ ErrorOccurred: "An error occurred! Please create an issue at https://github.com/g-battaglia/lazy-apple-container/issues",
ConnectionFailed: "connection to docker client failed. You may need to restart the docker client",
UnattachableContainerError: "Container does not support attaching. You must either run the service with the '-it' flag or use `stdin_open: true, tty: true` in the docker-compose.yml file",
WaitingForContainerInfo: "Cannot proceed until docker gives us more information about the container. Please retry in a few moments.",
CannotAttachStoppedContainerError: "You cannot attach to a stopped container, you need to start it first (which you can actually do with the 'r' key) (yes I'm too lazy to do this automatically for you) (pretty cool that I get to communicate one-on-one with you in the form of an error message though)",
- CannotAccessDockerSocketError: "Can't access docker socket at: unix:///var/run/docker.sock\nRun lazydocker as root or read https://docs.docker.com/install/linux/linux-postinstall/",
+ CannotAccessDockerSocketError: "Can't access docker socket at: unix:///var/run/docker.sock\nRun lazyapple as root or read https://docs.docker.com/install/linux/linux-postinstall/",
CannotKillChildError: "Waited three seconds for child process to stop. There may be an orphan process that continues to run on your system.",
Donate: "Donate",
@@ -175,8 +183,8 @@ func englishSet() TranslationSet {
Menu: "menu",
MenuTitle: "Menu",
Scroll: "scroll",
- OpenConfig: "open lazydocker config",
- EditConfig: "edit lazydocker config",
+ OpenConfig: "open lazyapple config",
+ EditConfig: "edit lazyapple config",
Cancel: "cancel",
Remove: "remove",
HideStopped: "hide/show stopped containers",
@@ -242,7 +250,7 @@ func englishSet() TranslationSet {
NothingToDisplay: "Nothing to display",
NoContainerForService: "No logs to show; service is not associated with a container",
CannotDisplayEnvVariables: "Something went wrong while displaying environment variables",
- CannotManageNonLocalService: "This service belongs to a different compose project. Run lazydocker from that project's directory to manage it.",
+ CannotManageNonLocalService: "This service belongs to a different compose project. Run lazyapple from that project's directory to manage it.",
NoContainers: "No containers",
NoContainer: "No container",
@@ -263,7 +271,7 @@ func englishSet() TranslationSet {
ConfirmPruneNetworks: "Are you sure you want to prune all unused networks?",
StopService: "Are you sure you want to stop this service's containers?",
StopContainer: "Are you sure you want to stop this container?",
- PressEnterToReturn: "Press enter to return to lazydocker (this prompt can be disabled in your config by setting `gui.returnImmediately: true`)",
+ PressEnterToReturn: "Press enter to return to lazyapple (this prompt can be disabled in your config by setting `gui.returnImmediately: true`)",
DetachFromContainerShortCut: "By default, to detach from the container press ctrl-p then ctrl-q",
No: "no",
@@ -279,5 +287,13 @@ func englishSet() TranslationSet {
FocusImages: "focus images panel",
FocusVolumes: "focus volumes panel",
FocusNetworks: "focus networks panel",
+
+ Kill: "kill",
+ KillingStatus: "killing",
+ RemoveContainer: "remove container",
+ ContainerNotRunning: "container is not running",
+ NoIp: "n/a",
+ NoPorts: "n/a",
+ NoStats: "No stats available",
}
}
diff --git a/pkg/i18n/french.go b/pkg/i18n/french.go
index 29ab5be2..6aa194b6 100644
--- a/pkg/i18n/french.go
+++ b/pkg/i18n/french.go
@@ -13,13 +13,13 @@ func frenchSet() TranslationSet {
NoViewMachingNewLineFocusedSwitchStatement: "Aucune vue correspondant au switch newLineFocused",
- ErrorOccurred: "Une erreur s'est produite ! Veuillez créer un rapport d'erreur sur https://github.com/jesseduffield/lazydocker/issues",
+ ErrorOccurred: "Une erreur s'est produite ! Veuillez créer un rapport d'erreur sur https://github.com/g-battaglia/lazy-apple-container/issues",
ConnectionFailed: "Erreur lors de la connexion au client Docker. Essayez de redémarrer votre client Docker",
UnattachableContainerError: "Le conteneur ne peut pas être attaché. Vous devez exécuter le service avec le drapeau 'it' ou bien utiliser `stdin_open: true, tty: true` dans votre fichier docker-compose.yml",
WaitingForContainerInfo: "Le processus ne peut pas continuer avant que Docker ne fournisse plus d'informations. Veuillez réessayer dans quelques instants.",
CannotAttachStoppedContainerError: "Vous ne pouvez pas vous attacher à un conteneur arrêté, vous devez le démarrer en amont (ce que vous pouvez faire avec la touche 'r') (oui, je suis trop paresseux pour le faire automatiquement pour vous) (plutôt cool que je puisse communiquer en tête-à-tête avec vous au travers d'un message d'erreur, cependant)",
- CannotAccessDockerSocketError: "Impossible d'accéder au socket Docker à : unix:///var/run/docker.sock\nLancez lazydocker en tant que root ou alors lisez https://docs.docker.com/install/linux/linux-postinstall/",
+ CannotAccessDockerSocketError: "Impossible d'accéder au socket Docker à : unix:///var/run/docker.sock\nLancez lazyapple en tant que root ou alors lisez https://docs.docker.com/install/linux/linux-postinstall/",
CannotKillChildError: "Trois secondes se sont écoulées depuis la demande d'arrêt des processus enfants. Il se peut qu'un processus orphelin continue à tourner sur votre système.",
Donate: "Donner",
@@ -33,8 +33,8 @@ func frenchSet() TranslationSet {
Menu: "menu",
MenuTitle: "Menu",
Scroll: "faire défiler",
- OpenConfig: "ouvrir la configuration lazydocker",
- EditConfig: "modifier la configuration lazydocker",
+ OpenConfig: "ouvrir la configuration lazyapple",
+ EditConfig: "modifier la configuration lazyapple",
Cancel: "annuler",
Remove: "supprimer",
HideStopped: "cacher/montrer les conteneurs arrêtés",
@@ -111,7 +111,7 @@ func frenchSet() TranslationSet {
ConfirmPruneNetworks: "Êtes-vous certain de vouloir détruire tous les réseaux non utilisés ?",
StopService: "Êtes-vous certain de vouloir arrêter le conteneur de ce service ?",
StopContainer: "Êtes-vous certain de vouloir arrêter ce conteneur ?",
- PressEnterToReturn: "Appuyez sur Entrée pour revenir à lazydocker (ce message peut être désactivé dans vos configurations en appliquant `gui.returnImmediately: true`)",
+ PressEnterToReturn: "Appuyez sur Entrée pour revenir à lazyapple (ce message peut être désactivé dans vos configurations en appliquant `gui.returnImmediately: true`)",
DetachFromContainerShortCut: "Par défaut, pour se détacher du conteneur appuyez sur CTRL-P puis CTRL-Q",
No: "non",
diff --git a/pkg/i18n/german.go b/pkg/i18n/german.go
index ac23d6b1..fb946b64 100644
--- a/pkg/i18n/german.go
+++ b/pkg/i18n/german.go
@@ -10,11 +10,11 @@ func germanSet() TranslationSet {
NoViewMachingNewLineFocusedSwitchStatement: "No view matching newLineFocused switch statement",
- ErrorOccurred: "Es ist ein Fehler aufgetreten! Bitte erstelle ein Issue hier: https://github.com/jesseduffield/lazydocker/issues",
+ ErrorOccurred: "Es ist ein Fehler aufgetreten! Bitte erstelle ein Issue hier: https://github.com/g-battaglia/lazy-apple-container/issues",
ConnectionFailed: "Verbindung zum Docker Client fehlgeschlagen. Du musst ggf. den Docker Client neustarten.",
UnattachableContainerError: "Der Container bietet keine Unterstützung für das Anbinden. Du musst den Dienst entweder mit der '-it' Flagge benutzen oder `stdin_open: true, tty: true` in der docker-compose.yml Datei setzen.",
CannotAttachStoppedContainerError: "Du kannst keinen angehaltenen Container anbinden. Du musst ihn erst starten (was du tun kannst, indem du 'r' drückst), (ja, ich bin zu faul um das zu automatisieren) (aber ist schon cool, dass ich so eine Konversation durch eine Fehlermeldung mit dir führen kann)",
- CannotAccessDockerSocketError: "Kann nicht auf den Socket zugreifen: unix:///var/run/docker.sock\nFühre lazydocker als root aus oder lese https://docs.docker.com/install/linux/linux-postinstall/",
+ CannotAccessDockerSocketError: "Kann nicht auf den Socket zugreifen: unix:///var/run/docker.sock\nFühre lazyapple als root aus oder lese https://docs.docker.com/install/linux/linux-postinstall/",
Donate: "Spenden",
Confirm: "Bestätigen",
@@ -27,8 +27,8 @@ func germanSet() TranslationSet {
Menu: "menü",
MenuTitle: "Menü",
Scroll: "scrollen",
- OpenConfig: "öffne lazydocker Konfiguration",
- EditConfig: "bearbeite lazydocker Konfiguration",
+ OpenConfig: "öffne lazyapple Konfiguration",
+ EditConfig: "bearbeite lazyapple Konfiguration",
Cancel: "abbrechen",
Remove: "entfernen",
ForceRemove: "Entfernen erzwingen",
@@ -91,7 +91,7 @@ func germanSet() TranslationSet {
ConfirmPruneNetworks: "Bist du dir sicher, dass du alle unbenutzen Netzwerk entfernen möchtest?",
StopService: "Bist du dir sicher, dass du den Dienst dieses Containers anhalten möchtest?",
StopContainer: "Bist du dir sicher, dass du den Container anhalten möchtest?",
- PressEnterToReturn: "Drücke Eingabe um zu lazydocker zurückzukehren. (Diese Nachfrage kann in Deiner Konfiguration deaktiviert werden, indem du folgenden Wert setzt: `gui.returnImmediately: true`)",
+ PressEnterToReturn: "Drücke Eingabe um zu lazyapple zurückzukehren. (Diese Nachfrage kann in Deiner Konfiguration deaktiviert werden, indem du folgenden Wert setzt: `gui.returnImmediately: true`)",
DetachFromContainerShortCut: "Um sich vom Container zu trennen, drücken Sie standardmäßig ctrl-p und dann ctrl-q",
}
}
diff --git a/pkg/i18n/i18n.go b/pkg/i18n/i18n.go
index 2fb447c6..79489578 100644
--- a/pkg/i18n/i18n.go
+++ b/pkg/i18n/i18n.go
@@ -3,7 +3,7 @@ package i18n
import (
"strings"
- "github.com/imdario/mergo"
+ "dario.cat/mergo"
"github.com/cloudfoundry/jibber_jabber"
"github.com/go-errors/errors"
diff --git a/pkg/i18n/polish.go b/pkg/i18n/polish.go
index 1025bf47..c7639299 100644
--- a/pkg/i18n/polish.go
+++ b/pkg/i18n/polish.go
@@ -10,7 +10,7 @@ func polishSet() TranslationSet {
NoViewMachingNewLineFocusedSwitchStatement: "Żaden widok nie odpowiada instrukcji przełączenia newLineFocused",
- ErrorOccurred: "Wystąpił błąd! Proszę go zgłosić na https://github.com/jesseduffield/lazydocker/issues",
+ ErrorOccurred: "Wystąpił błąd! Proszę go zgłosić na https://github.com/g-battaglia/lazy-apple-container/issues",
ConnectionFailed: "Błąd połączenia z Dockerem. Być może należy go zrestartować.",
UnattachableContainerError: "Kontener nie obsługuje przyczepiania (attach). Musisz albo użyć flag '-it', albo `stdin_open: true, tty: true` w pliku docker-compose.yml.",
CannotAttachStoppedContainerError: "Nie można przyczepić się do zatrzymanego kontenera, należy go najpierw uruchomić (co można wykonać wciskając przycisk 'r')",
@@ -91,7 +91,7 @@ func polishSet() TranslationSet {
ConfirmPruneNetworks: "Na pewno wyczyścić wszystkie nieużywane sieci?",
StopService: "Na pewno zatrzymać kontenery tego serwisu?",
StopContainer: "Na pewno zatrzymać ten kontener?",
- PressEnterToReturn: "Wciśnij enter aby powrócić do lazydockera (ten komunikat może być wyłączony w konfiguracji poprzez ustawienie `gui.returnImmediately: true`)",
+ PressEnterToReturn: "Wciśnij enter aby powrócić do lazyapplea (ten komunikat może być wyłączony w konfiguracji poprzez ustawienie `gui.returnImmediately: true`)",
DetachFromContainerShortCut: "Domyślnie, aby odłączyć się od kontenera, naciśnij ctrl-p, a następnie ctrl-q",
}
}
diff --git a/pkg/i18n/portuguese.go b/pkg/i18n/portuguese.go
index 36d9bcfd..613b4c69 100644
--- a/pkg/i18n/portuguese.go
+++ b/pkg/i18n/portuguese.go
@@ -16,12 +16,12 @@ func portugueseSet() TranslationSet {
NoViewMachingNewLineFocusedSwitchStatement: "No view matching newLineFocused switch statement",
- ErrorOccurred: "Um erro ocorreu! Por favor, crie uma issue em https://github.com/jesseduffield/lazydocker/issues",
+ ErrorOccurred: "Um erro ocorreu! Por favor, crie uma issue em https://github.com/g-battaglia/lazy-apple-container/issues",
ConnectionFailed: "Falha na conexão com o cliente Docker. Você pode precisar reiniciar o seu cliente Docker",
UnattachableContainerError: "O contêiner não suporta anexação. Você deve executar o serviço com a flag '-it' ou usar `stdin_open: true, tty: true` no arquivo docker-compose.yml",
WaitingForContainerInfo: "Não é possível prosseguir até que o Docker forneça mais informações sobre o contêiner. Por favor, tente novamente em alguns momentos.",
CannotAttachStoppedContainerError: "Você não pode anexar a um contêiner parado, você precisa iniciá-lo primeiro (o que você pode fazer com a tecla 'r') (sim, sou preguiçoso demais para fazer isso automaticamente para você) (aliás, bem legal que eu posso me comunicar diretamente com você na forma de uma mensagem de erro)",
- CannotAccessDockerSocketError: "Não é possível acessar o sôquete docker em: unix:///var/run/docker.sock\nExecute o lazydocker como root ou leia https://docs.docker.com/install/linux/linux-postinstall/",
+ CannotAccessDockerSocketError: "Não é possível acessar o sôquete docker em: unix:///var/run/docker.sock\nExecute o lazyapple como root ou leia https://docs.docker.com/install/linux/linux-postinstall/",
CannotKillChildError: "Três segundos foram esperarados para que os processos filhos parassem. Pode haver um processo órfão que continua em execução em seu sistema.",
Donate: "Doar",
@@ -37,8 +37,8 @@ func portugueseSet() TranslationSet {
Menu: "menu",
MenuTitle: "Menu",
Scroll: "rolar",
- OpenConfig: "abrir configuração do lazydocker",
- EditConfig: "editar configuração do lazydocker",
+ OpenConfig: "abrir configuração do lazyapple",
+ EditConfig: "editar configuração do lazyapple",
Cancel: "cancelar",
Remove: "remover",
HideStopped: "ocultar/mostrar contêineres parados",
@@ -124,7 +124,7 @@ func portugueseSet() TranslationSet {
ConfirmPruneNetworks: "Tem certeza que deseja destruir todas as redes não utilizadas?",
StopService: "Tem certeza que deseja parar os contêineres deste serviço?",
StopContainer: "Tem certeza que deseja parar este contêiner?",
- PressEnterToReturn: "Pressione enter para retornar ao lazydocker (este prompt pode ser desativado em sua configuração definindo `gui.returnImmediately: true`)",
+ PressEnterToReturn: "Pressione enter para retornar ao lazyapple (este prompt pode ser desativado em sua configuração definindo `gui.returnImmediately: true`)",
DetachFromContainerShortCut: "Por padrão, para desanexar do contêiner, pressione ctrl-p e depois ctrl-q",
No: "não",
diff --git a/pkg/i18n/spanish.go b/pkg/i18n/spanish.go
index b9148c3a..62319115 100644
--- a/pkg/i18n/spanish.go
+++ b/pkg/i18n/spanish.go
@@ -14,11 +14,11 @@ func spanishSet() TranslationSet {
RunningCustomCommandStatus: "ejecutando comando personalizado",
RunningBulkCommandStatus: "ejecutando comando masivo",
- ErrorOccurred: "¡Hubo un error! Por favor crea un issue en https://github.com/jesseduffield/lazydocker/issues",
+ ErrorOccurred: "¡Hubo un error! Por favor crea un issue en https://github.com/g-battaglia/lazy-apple-container/issues",
ConnectionFailed: "Falló la conexión con el docker client. Quizá necesitas reiniciar tu docker client",
UnattachableContainerError: "Container does not support attaching. You must either run the service with the '-it' flag or use `stdin_open: true, tty: true` in the docker-compose.yml file",
WaitingForContainerInfo: "No podemos proceder hasta que docker nos de más información sobre el contenedor. Inténtalo otra vez en unos segundos.",
- CannotAccessDockerSocketError: "No es posible acceder al docker socket en: unix:///var/run/docker.sock\nEjecuta lazydocker como root o lee https://docs.docker.com/install/linux/linux-postinstall/",
+ CannotAccessDockerSocketError: "No es posible acceder al docker socket en: unix:///var/run/docker.sock\nEjecuta lazyapple como root o lee https://docs.docker.com/install/linux/linux-postinstall/",
CannotKillChildError: "Esperamos tres segundos a que el proceso hijo se detenga. Debe de haber un proceso huérfano que continua activo en tu sistema.",
Donate: "Donar",
@@ -33,8 +33,8 @@ func spanishSet() TranslationSet {
Quit: "salir",
Menu: "menú",
MenuTitle: "Menú",
- OpenConfig: "abrir configuración de lazydocker",
- EditConfig: "editar configuración de lazydocker",
+ OpenConfig: "abrir configuración de lazyapple",
+ EditConfig: "editar configuración de lazyapple",
Cancel: "cancelar",
Remove: "borrar",
HideStopped: "esconder/mostrar contenedores parados",
@@ -119,7 +119,7 @@ func spanishSet() TranslationSet {
ConfirmPruneNetworks: "¿Realmente quieres limpiar todas las redes sin usar?",
StopService: "¿Realmente quieres detener los contenedores de este servicio?",
StopContainer: "¿Realmente quieres detener este contenedor?",
- PressEnterToReturn: "Presionar [enter] para volver a lazydocker (este mensaje puede ser desactivado en tu configuración poniendo `gui.returnImmediately: true`)",
+ PressEnterToReturn: "Presionar [enter] para volver a lazyapple (este mensaje puede ser desactivado en tu configuración poniendo `gui.returnImmediately: true`)",
No: "no",
Yes: "sí",
diff --git a/pkg/i18n/turkish.go b/pkg/i18n/turkish.go
index fccb3d91..efe60491 100644
--- a/pkg/i18n/turkish.go
+++ b/pkg/i18n/turkish.go
@@ -10,11 +10,11 @@ func turkishSet() TranslationSet {
NoViewMachingNewLineFocusedSwitchStatement: "NewLineFocused anahtar deyimi ile eşleşen görünüm yok",
- ErrorOccurred: "Bir hata oluştu! Lütfen https://github.com/jesseduffield/lazydocker/issues adresinden bir hataya ilişkin konu oluşturun",
+ ErrorOccurred: "Bir hata oluştu! Lütfen https://github.com/g-battaglia/lazy-apple-container/issues adresinden bir hataya ilişkin konu oluşturun",
ConnectionFailed: "Docker bağlantısı başarısız oldu. Docker' ı yeniden başlatmanız gerekebilir",
UnattachableContainerError: "Konteyner attaching modunda çalışmayı desteklemiyor. Hizmeti '-it' opsiyonu ile çalıştırmanız veya docker-compose.yml dosyasında `stdin_open: true, tty: true` kullanmanız gerekir.",
CannotAttachStoppedContainerError: "Durdurulan konteynera bağlanamazsınız, ilk önce başlatmanız gerekir (aslında başlatmayı r tuşu ile yapabilirsiniz) (evet, senin için bunu otomatik olarak yapabilirim fakat çok tembelim) (hata mesajı ile seninle birebir iletişim kurmam çok daha güzel)",
- CannotAccessDockerSocketError: "Docker' a şu adresten erişilemiyor : unix:///var/run/docker.sock\n lazydocker' ı root(kök kullanıcı) olarak çalıştır veya şu adresteki adımları takip et : https://docs.docker.com/install/linux/linux-postinstall/",
+ CannotAccessDockerSocketError: "Docker' a şu adresten erişilemiyor : unix:///var/run/docker.sock\n lazyapple' ı root(kök kullanıcı) olarak çalıştır veya şu adresteki adımları takip et : https://docs.docker.com/install/linux/linux-postinstall/",
Donate: "Bağış",
Confirm: "Onayla",
@@ -27,7 +27,7 @@ func turkishSet() TranslationSet {
Menu: "menü",
MenuTitle: "Menü",
Scroll: "kaydır",
- OpenConfig: "lazydocker ayarlarını aç",
+ OpenConfig: "lazyapple ayarlarını aç",
EditConfig: "lazzydocker ayarlarını düzenle",
Cancel: "iptal",
Remove: "kaldır",
@@ -91,7 +91,7 @@ func turkishSet() TranslationSet {
ConfirmPruneNetworks: "Kullanılmayan tüm ağları temizlemek istediğinizden emin misiniz?",
StopService: "Bu servisin konteynerlerini durdurmak istediğinize emin misiniz?",
StopContainer: "Bu konteyneri durdurmak istediğinize emin misiniz?",
- PressEnterToReturn: "lazydocker' a geri dönmek için enter tuşuna basın ( Bu uyarı, `gui.return Immediately: true` ayarıyla devre dışı bırakılabilir)",
+ PressEnterToReturn: "lazyapple' a geri dönmek için enter tuşuna basın ( Bu uyarı, `gui.return Immediately: true` ayarıyla devre dışı bırakılabilir)",
DetachFromContainerShortCut: "Varsayılan olarak, kaptan ayırmak için ctrl-p ve ardından ctrl-q tuşlarına basın",
}
}
diff --git a/pkg/log/log.go b/pkg/log/log.go
index 232a7bb8..04959aae 100644
--- a/pkg/log/log.go
+++ b/pkg/log/log.go
@@ -6,7 +6,7 @@ import (
"os"
"path/filepath"
- "github.com/jesseduffield/lazydocker/pkg/config"
+ "github.com/jesseduffield/lazycontainer/pkg/config"
"github.com/sirupsen/logrus"
)
diff --git a/pkg/tasks/tasks.go b/pkg/tasks/tasks.go
index 06fac38f..72325b88 100644
--- a/pkg/tasks/tasks.go
+++ b/pkg/tasks/tasks.go
@@ -5,7 +5,7 @@ import (
"fmt"
"time"
- "github.com/jesseduffield/lazydocker/pkg/i18n"
+ "github.com/jesseduffield/lazycontainer/pkg/i18n"
"github.com/sasha-s/go-deadlock"
"github.com/sirupsen/logrus"
)
diff --git a/scripts/cheatsheet/main.go b/scripts/cheatsheet/main.go
index 53f72eda..1b87afb0 100644
--- a/scripts/cheatsheet/main.go
+++ b/scripts/cheatsheet/main.go
@@ -5,7 +5,7 @@ import (
"log"
"os"
- "github.com/jesseduffield/lazydocker/pkg/cheatsheet"
+ "github.com/jesseduffield/lazycontainer/pkg/cheatsheet"
)
func main() {
diff --git a/scripts/translations/get_required_translations.go b/scripts/translations/get_required_translations.go
index 9e0dd400..b8df7179 100644
--- a/scripts/translations/get_required_translations.go
+++ b/scripts/translations/get_required_translations.go
@@ -4,7 +4,7 @@ import (
"fmt"
"reflect"
- "github.com/jesseduffield/lazydocker/pkg/i18n"
+ "github.com/jesseduffield/lazycontainer/pkg/i18n"
)
func main() {
diff --git a/vendor/github.com/imdario/mergo/.deepsource.toml b/vendor/dario.cat/mergo/.deepsource.toml
similarity index 72%
rename from vendor/github.com/imdario/mergo/.deepsource.toml
rename to vendor/dario.cat/mergo/.deepsource.toml
index 8a0681af..a8bc979e 100644
--- a/vendor/github.com/imdario/mergo/.deepsource.toml
+++ b/vendor/dario.cat/mergo/.deepsource.toml
@@ -9,4 +9,4 @@ name = "go"
enabled = true
[analyzers.meta]
- import_path = "github.com/imdario/mergo"
\ No newline at end of file
+ import_path = "dario.cat/mergo"
\ No newline at end of file
diff --git a/vendor/github.com/imdario/mergo/.gitignore b/vendor/dario.cat/mergo/.gitignore
similarity index 95%
rename from vendor/github.com/imdario/mergo/.gitignore
rename to vendor/dario.cat/mergo/.gitignore
index 529c3412..45ad0f1a 100644
--- a/vendor/github.com/imdario/mergo/.gitignore
+++ b/vendor/dario.cat/mergo/.gitignore
@@ -13,6 +13,9 @@
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
+# Golang/Intellij
+.idea
+
# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
.glide/
diff --git a/vendor/github.com/imdario/mergo/.travis.yml b/vendor/dario.cat/mergo/.travis.yml
similarity index 100%
rename from vendor/github.com/imdario/mergo/.travis.yml
rename to vendor/dario.cat/mergo/.travis.yml
diff --git a/vendor/github.com/imdario/mergo/CODE_OF_CONDUCT.md b/vendor/dario.cat/mergo/CODE_OF_CONDUCT.md
similarity index 100%
rename from vendor/github.com/imdario/mergo/CODE_OF_CONDUCT.md
rename to vendor/dario.cat/mergo/CODE_OF_CONDUCT.md
diff --git a/vendor/github.com/imdario/mergo/CONTRIBUTING.md b/vendor/dario.cat/mergo/CONTRIBUTING.md
similarity index 100%
rename from vendor/github.com/imdario/mergo/CONTRIBUTING.md
rename to vendor/dario.cat/mergo/CONTRIBUTING.md
diff --git a/vendor/dario.cat/mergo/FUNDING.json b/vendor/dario.cat/mergo/FUNDING.json
new file mode 100644
index 00000000..0585e1fe
--- /dev/null
+++ b/vendor/dario.cat/mergo/FUNDING.json
@@ -0,0 +1,7 @@
+{
+ "drips": {
+ "ethereum": {
+ "ownedBy": "0x6160020e7102237aC41bdb156e94401692D76930"
+ }
+ }
+}
diff --git a/vendor/github.com/imdario/mergo/LICENSE b/vendor/dario.cat/mergo/LICENSE
similarity index 100%
rename from vendor/github.com/imdario/mergo/LICENSE
rename to vendor/dario.cat/mergo/LICENSE
diff --git a/vendor/github.com/imdario/mergo/README.md b/vendor/dario.cat/mergo/README.md
similarity index 65%
rename from vendor/github.com/imdario/mergo/README.md
rename to vendor/dario.cat/mergo/README.md
index ffbbb62c..0e4a59af 100644
--- a/vendor/github.com/imdario/mergo/README.md
+++ b/vendor/dario.cat/mergo/README.md
@@ -44,77 +44,59 @@ Also a lovely [comune](http://en.wikipedia.org/wiki/Mergo) (municipality) in the
## Status
-It is ready for production use. [It is used in several projects by Docker, Google, The Linux Foundation, VMWare, Shopify, Microsoft, etc](https://github.com/imdario/mergo#mergo-in-the-wild).
+Mergo is stable and frozen, ready for production. Check a short list of the projects using at large scale it [here](https://github.com/imdario/mergo#mergo-in-the-wild).
-### Important note
+No new features are accepted. They will be considered for a future v2 that improves the implementation and fixes bugs for corner cases.
+
+### Important notes
+
+#### 1.0.0
+
+In [1.0.0](//github.com/imdario/mergo/releases/tag/1.0.0) Mergo moves to a vanity URL `dario.cat/mergo`. No more v1 versions will be released.
+
+If the vanity URL is causing issues in your project due to a dependency pulling Mergo - it isn't a direct dependency in your project - it is recommended to use [replace](https://github.com/golang/go/wiki/Modules#when-should-i-use-the-replace-directive) to pin the version to the last one with the old import URL:
+
+```
+replace github.com/imdario/mergo => github.com/imdario/mergo v0.3.16
+```
+
+#### 0.3.9
Please keep in mind that a problematic PR broke [0.3.9](//github.com/imdario/mergo/releases/tag/0.3.9). I reverted it in [0.3.10](//github.com/imdario/mergo/releases/tag/0.3.10), and I consider it stable but not bug-free. Also, this version adds support for go modules.
Keep in mind that in [0.3.2](//github.com/imdario/mergo/releases/tag/0.3.2), Mergo changed `Merge()`and `Map()` signatures to support [transformers](#transformers). I added an optional/variadic argument so that it won't break the existing code.
-If you were using Mergo before April 6th, 2015, please check your project works as intended after updating your local copy with ```go get -u github.com/imdario/mergo```. I apologize for any issue caused by its previous behavior and any future bug that Mergo could cause in existing projects after the change (release 0.2.0).
+If you were using Mergo before April 6th, 2015, please check your project works as intended after updating your local copy with ```go get -u dario.cat/mergo```. I apologize for any issue caused by its previous behavior and any future bug that Mergo could cause in existing projects after the change (release 0.2.0).
### Donations
If Mergo is useful to you, consider buying me a coffee, a beer, or making a monthly donation to allow me to keep building great free software. :heart_eyes:
-
### Mergo in the wild
-- [moby/moby](https://github.com/moby/moby)
-- [kubernetes/kubernetes](https://github.com/kubernetes/kubernetes)
-- [vmware/dispatch](https://github.com/vmware/dispatch)
-- [Shopify/themekit](https://github.com/Shopify/themekit)
-- [imdario/zas](https://github.com/imdario/zas)
-- [matcornic/hermes](https://github.com/matcornic/hermes)
-- [OpenBazaar/openbazaar-go](https://github.com/OpenBazaar/openbazaar-go)
-- [kataras/iris](https://github.com/kataras/iris)
-- [michaelsauter/crane](https://github.com/michaelsauter/crane)
-- [go-task/task](https://github.com/go-task/task)
-- [sensu/uchiwa](https://github.com/sensu/uchiwa)
-- [ory/hydra](https://github.com/ory/hydra)
-- [sisatech/vcli](https://github.com/sisatech/vcli)
-- [dairycart/dairycart](https://github.com/dairycart/dairycart)
-- [projectcalico/felix](https://github.com/projectcalico/felix)
-- [resin-os/balena](https://github.com/resin-os/balena)
-- [go-kivik/kivik](https://github.com/go-kivik/kivik)
-- [Telefonica/govice](https://github.com/Telefonica/govice)
-- [supergiant/supergiant](supergiant/supergiant)
-- [SergeyTsalkov/brooce](https://github.com/SergeyTsalkov/brooce)
-- [soniah/dnsmadeeasy](https://github.com/soniah/dnsmadeeasy)
-- [ohsu-comp-bio/funnel](https://github.com/ohsu-comp-bio/funnel)
-- [EagerIO/Stout](https://github.com/EagerIO/Stout)
-- [lynndylanhurley/defsynth-api](https://github.com/lynndylanhurley/defsynth-api)
-- [russross/canvasassignments](https://github.com/russross/canvasassignments)
-- [rdegges/cryptly-api](https://github.com/rdegges/cryptly-api)
-- [casualjim/exeggutor](https://github.com/casualjim/exeggutor)
-- [divshot/gitling](https://github.com/divshot/gitling)
-- [RWJMurphy/gorl](https://github.com/RWJMurphy/gorl)
-- [andrerocker/deploy42](https://github.com/andrerocker/deploy42)
-- [elwinar/rambler](https://github.com/elwinar/rambler)
-- [tmaiaroto/gopartman](https://github.com/tmaiaroto/gopartman)
-- [jfbus/impressionist](https://github.com/jfbus/impressionist)
-- [Jmeyering/zealot](https://github.com/Jmeyering/zealot)
-- [godep-migrator/rigger-host](https://github.com/godep-migrator/rigger-host)
-- [Dronevery/MultiwaySwitch-Go](https://github.com/Dronevery/MultiwaySwitch-Go)
-- [thoas/picfit](https://github.com/thoas/picfit)
-- [mantasmatelis/whooplist-server](https://github.com/mantasmatelis/whooplist-server)
-- [jnuthong/item_search](https://github.com/jnuthong/item_search)
-- [bukalapak/snowboard](https://github.com/bukalapak/snowboard)
-- [containerssh/containerssh](https://github.com/containerssh/containerssh)
-- [goreleaser/goreleaser](https://github.com/goreleaser/goreleaser)
-- [tjpnz/structbot](https://github.com/tjpnz/structbot)
+Mergo is used by [thousands](https://deps.dev/go/dario.cat%2Fmergo/v1.0.0/dependents) [of](https://deps.dev/go/github.com%2Fimdario%2Fmergo/v0.3.16/dependents) [projects](https://deps.dev/go/github.com%2Fimdario%2Fmergo/v0.3.12), including:
+
+* [containerd/containerd](https://github.com/containerd/containerd)
+* [datadog/datadog-agent](https://github.com/datadog/datadog-agent)
+* [docker/cli/](https://github.com/docker/cli/)
+* [goreleaser/goreleaser](https://github.com/goreleaser/goreleaser)
+* [go-micro/go-micro](https://github.com/go-micro/go-micro)
+* [grafana/loki](https://github.com/grafana/loki)
+* [masterminds/sprig](github.com/Masterminds/sprig)
+* [moby/moby](https://github.com/moby/moby)
+* [slackhq/nebula](https://github.com/slackhq/nebula)
+* [volcano-sh/volcano](https://github.com/volcano-sh/volcano)
## Install
- go get github.com/imdario/mergo
+ go get dario.cat/mergo
// use in your .go code
import (
- "github.com/imdario/mergo"
+ "dario.cat/mergo"
)
## Usage
@@ -135,6 +117,39 @@ if err := mergo.Merge(&dst, src, mergo.WithOverride); err != nil {
}
```
+If you need to override pointers, so the source pointer's value is assigned to the destination's pointer, you must use `WithoutDereference`:
+
+```go
+package main
+
+import (
+ "fmt"
+
+ "dario.cat/mergo"
+)
+
+type Foo struct {
+ A *string
+ B int64
+}
+
+func main() {
+ first := "first"
+ second := "second"
+ src := Foo{
+ A: &first,
+ B: 2,
+ }
+
+ dest := Foo{
+ A: &second,
+ B: 1,
+ }
+
+ mergo.Merge(&dest, src, mergo.WithOverride, mergo.WithoutDereference)
+}
+```
+
Additionally, you can map a `map[string]interface{}` to a struct (and otherwise, from struct to map), following the same restrictions as in `Merge()`. Keys are capitalized to find each corresponding exported field.
```go
@@ -152,7 +167,7 @@ package main
import (
"fmt"
- "github.com/imdario/mergo"
+ "dario.cat/mergo"
)
type Foo struct {
@@ -175,10 +190,6 @@ func main() {
}
```
-Note: if test are failing due missing package, please execute:
-
- go get gopkg.in/yaml.v3
-
### Transformers
Transformers allow to merge specific types differently than in the default behavior. In other words, now you can customize how some types are merged. For example, `time.Time` is a struct; it doesn't have zero value but IsZero can return true because it has fields with zero value. How can we merge a non-zero `time.Time`?
@@ -188,9 +199,9 @@ package main
import (
"fmt"
- "github.com/imdario/mergo"
- "reflect"
- "time"
+ "dario.cat/mergo"
+ "reflect"
+ "time"
)
type timeTransformer struct {
diff --git a/vendor/github.com/imdario/mergo/SECURITY.md b/vendor/dario.cat/mergo/SECURITY.md
similarity index 82%
rename from vendor/github.com/imdario/mergo/SECURITY.md
rename to vendor/dario.cat/mergo/SECURITY.md
index a5de61f7..3788fcc1 100644
--- a/vendor/github.com/imdario/mergo/SECURITY.md
+++ b/vendor/dario.cat/mergo/SECURITY.md
@@ -4,8 +4,8 @@
| Version | Supported |
| ------- | ------------------ |
-| 0.3.x | :white_check_mark: |
-| < 0.3 | :x: |
+| 1.x.x | :white_check_mark: |
+| < 1.0 | :x: |
## Security contact information
diff --git a/vendor/github.com/imdario/mergo/doc.go b/vendor/dario.cat/mergo/doc.go
similarity index 88%
rename from vendor/github.com/imdario/mergo/doc.go
rename to vendor/dario.cat/mergo/doc.go
index fcd985f9..7d96ec05 100644
--- a/vendor/github.com/imdario/mergo/doc.go
+++ b/vendor/dario.cat/mergo/doc.go
@@ -8,30 +8,36 @@ A helper to merge structs and maps in Golang. Useful for configuration default v
Mergo merges same-type structs and maps by setting default values in zero-value fields. Mergo won't merge unexported (private) fields. It will do recursively any exported one. It also won't merge structs inside maps (because they are not addressable using Go reflection).
-Status
+# Status
It is ready for production use. It is used in several projects by Docker, Google, The Linux Foundation, VMWare, Shopify, etc.
-Important note
+# Important notes
+
+1.0.0
+
+In 1.0.0 Mergo moves to a vanity URL `dario.cat/mergo`.
+
+0.3.9
Please keep in mind that a problematic PR broke 0.3.9. We reverted it in 0.3.10. We consider 0.3.10 as stable but not bug-free. . Also, this version adds suppot for go modules.
Keep in mind that in 0.3.2, Mergo changed Merge() and Map() signatures to support transformers. We added an optional/variadic argument so that it won't break the existing code.
-If you were using Mergo before April 6th, 2015, please check your project works as intended after updating your local copy with go get -u github.com/imdario/mergo. I apologize for any issue caused by its previous behavior and any future bug that Mergo could cause in existing projects after the change (release 0.2.0).
+If you were using Mergo before April 6th, 2015, please check your project works as intended after updating your local copy with go get -u dario.cat/mergo. I apologize for any issue caused by its previous behavior and any future bug that Mergo could cause in existing projects after the change (release 0.2.0).
-Install
+# Install
Do your usual installation procedure:
- go get github.com/imdario/mergo
+ go get dario.cat/mergo
- // use in your .go code
- import (
- "github.com/imdario/mergo"
- )
+ // use in your .go code
+ import (
+ "dario.cat/mergo"
+ )
-Usage
+# Usage
You can only merge same-type structs with exported fields initialized as zero value of their type and same-types maps. Mergo won't merge unexported (private) fields but will do recursively any exported one. It won't merge empty structs value as they are zero values too. Also, maps will be merged recursively except for structs inside maps (because they are not addressable using Go reflection).
@@ -59,7 +65,7 @@ Here is a nice example:
import (
"fmt"
- "github.com/imdario/mergo"
+ "dario.cat/mergo"
)
type Foo struct {
@@ -81,7 +87,7 @@ Here is a nice example:
// {two 2}
}
-Transformers
+# Transformers
Transformers allow to merge specific types differently than in the default behavior. In other words, now you can customize how some types are merged. For example, time.Time is a struct; it doesn't have zero value but IsZero can return true because it has fields with zero value. How can we merge a non-zero time.Time?
@@ -89,9 +95,9 @@ Transformers allow to merge specific types differently than in the default behav
import (
"fmt"
- "github.com/imdario/mergo"
- "reflect"
- "time"
+ "dario.cat/mergo"
+ "reflect"
+ "time"
)
type timeTransformer struct {
@@ -127,17 +133,16 @@ Transformers allow to merge specific types differently than in the default behav
// { 2018-01-12 01:15:00 +0000 UTC m=+0.000000001 }
}
-Contact me
+# Contact me
If I can help you, you have an idea or you are using Mergo in your projects, don't hesitate to drop me a line (or a pull request): https://twitter.com/im_dario
-About
+# About
Written by Dario Castañé: https://da.rio.hn
-License
+# License
BSD 3-Clause license, as Go language.
-
*/
package mergo
diff --git a/vendor/github.com/imdario/mergo/map.go b/vendor/dario.cat/mergo/map.go
similarity index 96%
rename from vendor/github.com/imdario/mergo/map.go
rename to vendor/dario.cat/mergo/map.go
index b50d5c2a..759b4f74 100644
--- a/vendor/github.com/imdario/mergo/map.go
+++ b/vendor/dario.cat/mergo/map.go
@@ -58,7 +58,7 @@ func deepMap(dst, src reflect.Value, visited map[uintptr]*visit, depth int, conf
}
fieldName := field.Name
fieldName = changeInitialCase(fieldName, unicode.ToLower)
- if v, ok := dstMap[fieldName]; !ok || (isEmptyValue(reflect.ValueOf(v), !config.ShouldNotDereference) || overwrite) {
+ if _, ok := dstMap[fieldName]; !ok || (!isEmptyValue(reflect.ValueOf(src.Field(i).Interface()), !config.ShouldNotDereference) && overwrite) || config.overwriteWithEmptyValue {
dstMap[fieldName] = src.Field(i).Interface()
}
}
diff --git a/vendor/github.com/imdario/mergo/merge.go b/vendor/dario.cat/mergo/merge.go
similarity index 99%
rename from vendor/github.com/imdario/mergo/merge.go
rename to vendor/dario.cat/mergo/merge.go
index 0ef9b213..fd47c95b 100644
--- a/vendor/github.com/imdario/mergo/merge.go
+++ b/vendor/dario.cat/mergo/merge.go
@@ -269,7 +269,7 @@ func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, co
if err = deepMerge(dst.Elem(), src.Elem(), visited, depth+1, config); err != nil {
return
}
- } else {
+ } else if src.Elem().Kind() != reflect.Struct {
if overwriteWithEmptySrc || (overwrite && !src.IsNil()) || dst.IsNil() {
dst.Set(src)
}
diff --git a/vendor/github.com/imdario/mergo/mergo.go b/vendor/dario.cat/mergo/mergo.go
similarity index 100%
rename from vendor/github.com/imdario/mergo/mergo.go
rename to vendor/dario.cat/mergo/mergo.go
diff --git a/vendor/github.com/Microsoft/go-winio/.gitattributes b/vendor/github.com/Microsoft/go-winio/.gitattributes
deleted file mode 100644
index 94f480de..00000000
--- a/vendor/github.com/Microsoft/go-winio/.gitattributes
+++ /dev/null
@@ -1 +0,0 @@
-* text=auto eol=lf
\ No newline at end of file
diff --git a/vendor/github.com/Microsoft/go-winio/.gitignore b/vendor/github.com/Microsoft/go-winio/.gitignore
deleted file mode 100644
index 815e2066..00000000
--- a/vendor/github.com/Microsoft/go-winio/.gitignore
+++ /dev/null
@@ -1,10 +0,0 @@
-.vscode/
-
-*.exe
-
-# testing
-testdata
-
-# go workspaces
-go.work
-go.work.sum
diff --git a/vendor/github.com/Microsoft/go-winio/.golangci.yml b/vendor/github.com/Microsoft/go-winio/.golangci.yml
deleted file mode 100644
index faedfe93..00000000
--- a/vendor/github.com/Microsoft/go-winio/.golangci.yml
+++ /dev/null
@@ -1,147 +0,0 @@
-linters:
- enable:
- # style
- - containedctx # struct contains a context
- - dupl # duplicate code
- - errname # erorrs are named correctly
- - nolintlint # "//nolint" directives are properly explained
- - revive # golint replacement
- - unconvert # unnecessary conversions
- - wastedassign
-
- # bugs, performance, unused, etc ...
- - contextcheck # function uses a non-inherited context
- - errorlint # errors not wrapped for 1.13
- - exhaustive # check exhaustiveness of enum switch statements
- - gofmt # files are gofmt'ed
- - gosec # security
- - nilerr # returns nil even with non-nil error
- - thelper # test helpers without t.Helper()
- - unparam # unused function params
-
-issues:
- exclude-dirs:
- - pkg/etw/sample
-
- exclude-rules:
- # err is very often shadowed in nested scopes
- - linters:
- - govet
- text: '^shadow: declaration of "err" shadows declaration'
-
- # ignore long lines for skip autogen directives
- - linters:
- - revive
- text: "^line-length-limit: "
- source: "^//(go:generate|sys) "
-
- #TODO: remove after upgrading to go1.18
- # ignore comment spacing for nolint and sys directives
- - linters:
- - revive
- text: "^comment-spacings: no space between comment delimiter and comment text"
- source: "//(cspell:|nolint:|sys |todo)"
-
- # not on go 1.18 yet, so no any
- - linters:
- - revive
- text: "^use-any: since GO 1.18 'interface{}' can be replaced by 'any'"
-
- # allow unjustified ignores of error checks in defer statements
- - linters:
- - nolintlint
- text: "^directive `//nolint:errcheck` should provide explanation"
- source: '^\s*defer '
-
- # allow unjustified ignores of error lints for io.EOF
- - linters:
- - nolintlint
- text: "^directive `//nolint:errorlint` should provide explanation"
- source: '[=|!]= io.EOF'
-
-
-linters-settings:
- exhaustive:
- default-signifies-exhaustive: true
- govet:
- enable-all: true
- disable:
- # struct order is often for Win32 compat
- # also, ignore pointer bytes/GC issues for now until performance becomes an issue
- - fieldalignment
- nolintlint:
- require-explanation: true
- require-specific: true
- revive:
- # revive is more configurable than static check, so likely the preferred alternative to static-check
- # (once the perf issue is solved: https://github.com/golangci/golangci-lint/issues/2997)
- enable-all-rules:
- true
- # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md
- rules:
- # rules with required arguments
- - name: argument-limit
- disabled: true
- - name: banned-characters
- disabled: true
- - name: cognitive-complexity
- disabled: true
- - name: cyclomatic
- disabled: true
- - name: file-header
- disabled: true
- - name: function-length
- disabled: true
- - name: function-result-limit
- disabled: true
- - name: max-public-structs
- disabled: true
- # geneally annoying rules
- - name: add-constant # complains about any and all strings and integers
- disabled: true
- - name: confusing-naming # we frequently use "Foo()" and "foo()" together
- disabled: true
- - name: flag-parameter # excessive, and a common idiom we use
- disabled: true
- - name: unhandled-error # warns over common fmt.Print* and io.Close; rely on errcheck instead
- disabled: true
- # general config
- - name: line-length-limit
- arguments:
- - 140
- - name: var-naming
- arguments:
- - []
- - - CID
- - CRI
- - CTRD
- - DACL
- - DLL
- - DOS
- - ETW
- - FSCTL
- - GCS
- - GMSA
- - HCS
- - HV
- - IO
- - LCOW
- - LDAP
- - LPAC
- - LTSC
- - MMIO
- - NT
- - OCI
- - PMEM
- - PWSH
- - RX
- - SACl
- - SID
- - SMB
- - TX
- - VHD
- - VHDX
- - VMID
- - VPCI
- - WCOW
- - WIM
diff --git a/vendor/github.com/Microsoft/go-winio/CODEOWNERS b/vendor/github.com/Microsoft/go-winio/CODEOWNERS
deleted file mode 100644
index ae1b4942..00000000
--- a/vendor/github.com/Microsoft/go-winio/CODEOWNERS
+++ /dev/null
@@ -1 +0,0 @@
- * @microsoft/containerplat
diff --git a/vendor/github.com/Microsoft/go-winio/LICENSE b/vendor/github.com/Microsoft/go-winio/LICENSE
deleted file mode 100644
index b8b569d7..00000000
--- a/vendor/github.com/Microsoft/go-winio/LICENSE
+++ /dev/null
@@ -1,22 +0,0 @@
-The MIT License (MIT)
-
-Copyright (c) 2015 Microsoft
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-
diff --git a/vendor/github.com/Microsoft/go-winio/README.md b/vendor/github.com/Microsoft/go-winio/README.md
deleted file mode 100644
index 7474b4f0..00000000
--- a/vendor/github.com/Microsoft/go-winio/README.md
+++ /dev/null
@@ -1,89 +0,0 @@
-# go-winio [](https://github.com/microsoft/go-winio/actions/workflows/ci.yml)
-
-This repository contains utilities for efficiently performing Win32 IO operations in
-Go. Currently, this is focused on accessing named pipes and other file handles, and
-for using named pipes as a net transport.
-
-This code relies on IO completion ports to avoid blocking IO on system threads, allowing Go
-to reuse the thread to schedule another goroutine. This limits support to Windows Vista and
-newer operating systems. This is similar to the implementation of network sockets in Go's net
-package.
-
-Please see the LICENSE file for licensing information.
-
-## Contributing
-
-This project welcomes contributions and suggestions.
-Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that
-you have the right to, and actually do, grant us the rights to use your contribution.
-For details, visit [Microsoft CLA](https://cla.microsoft.com).
-
-When you submit a pull request, a CLA-bot will automatically determine whether you need to
-provide a CLA and decorate the PR appropriately (e.g., label, comment).
-Simply follow the instructions provided by the bot.
-You will only need to do this once across all repos using our CLA.
-
-Additionally, the pull request pipeline requires the following steps to be performed before
-mergining.
-
-### Code Sign-Off
-
-We require that contributors sign their commits using [`git commit --signoff`][git-commit-s]
-to certify they either authored the work themselves or otherwise have permission to use it in this project.
-
-A range of commits can be signed off using [`git rebase --signoff`][git-rebase-s].
-
-Please see [the developer certificate](https://developercertificate.org) for more info,
-as well as to make sure that you can attest to the rules listed.
-Our CI uses the DCO Github app to ensure that all commits in a given PR are signed-off.
-
-### Linting
-
-Code must pass a linting stage, which uses [`golangci-lint`][lint].
-The linting settings are stored in [`.golangci.yaml`](./.golangci.yaml), and can be run
-automatically with VSCode by adding the following to your workspace or folder settings:
-
-```json
- "go.lintTool": "golangci-lint",
- "go.lintOnSave": "package",
-```
-
-Additional editor [integrations options are also available][lint-ide].
-
-Alternatively, `golangci-lint` can be [installed locally][lint-install] and run from the repo root:
-
-```shell
-# use . or specify a path to only lint a package
-# to show all lint errors, use flags "--max-issues-per-linter=0 --max-same-issues=0"
-> golangci-lint run ./...
-```
-
-### Go Generate
-
-The pipeline checks that auto-generated code, via `go generate`, are up to date.
-
-This can be done for the entire repo:
-
-```shell
-> go generate ./...
-```
-
-## Code of Conduct
-
-This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
-For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or
-contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
-
-## Special Thanks
-
-Thanks to [natefinch][natefinch] for the inspiration for this library.
-See [npipe](https://github.com/natefinch/npipe) for another named pipe implementation.
-
-[lint]: https://golangci-lint.run/
-[lint-ide]: https://golangci-lint.run/usage/integrations/#editor-integration
-[lint-install]: https://golangci-lint.run/usage/install/#local-installation
-
-[git-commit-s]: https://git-scm.com/docs/git-commit#Documentation/git-commit.txt--s
-[git-rebase-s]: https://git-scm.com/docs/git-rebase#Documentation/git-rebase.txt---signoff
-
-[natefinch]: https://github.com/natefinch
diff --git a/vendor/github.com/Microsoft/go-winio/SECURITY.md b/vendor/github.com/Microsoft/go-winio/SECURITY.md
deleted file mode 100644
index 869fdfe2..00000000
--- a/vendor/github.com/Microsoft/go-winio/SECURITY.md
+++ /dev/null
@@ -1,41 +0,0 @@
-
-
-## Security
-
-Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/).
-
-If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below.
-
-## Reporting Security Issues
-
-**Please do not report security vulnerabilities through public GitHub issues.**
-
-Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report).
-
-If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey).
-
-You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc).
-
-Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue:
-
- * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.)
- * Full paths of source file(s) related to the manifestation of the issue
- * The location of the affected source code (tag/branch/commit or direct URL)
- * Any special configuration required to reproduce the issue
- * Step-by-step instructions to reproduce the issue
- * Proof-of-concept or exploit code (if possible)
- * Impact of the issue, including how an attacker might exploit the issue
-
-This information will help us triage your report more quickly.
-
-If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs.
-
-## Preferred Languages
-
-We prefer all communications to be in English.
-
-## Policy
-
-Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd).
-
-
diff --git a/vendor/github.com/Microsoft/go-winio/backup.go b/vendor/github.com/Microsoft/go-winio/backup.go
deleted file mode 100644
index b54341da..00000000
--- a/vendor/github.com/Microsoft/go-winio/backup.go
+++ /dev/null
@@ -1,287 +0,0 @@
-//go:build windows
-// +build windows
-
-package winio
-
-import (
- "encoding/binary"
- "errors"
- "fmt"
- "io"
- "os"
- "runtime"
- "unicode/utf16"
-
- "github.com/Microsoft/go-winio/internal/fs"
- "golang.org/x/sys/windows"
-)
-
-//sys backupRead(h windows.Handle, b []byte, bytesRead *uint32, abort bool, processSecurity bool, context *uintptr) (err error) = BackupRead
-//sys backupWrite(h windows.Handle, b []byte, bytesWritten *uint32, abort bool, processSecurity bool, context *uintptr) (err error) = BackupWrite
-
-const (
- BackupData = uint32(iota + 1)
- BackupEaData
- BackupSecurity
- BackupAlternateData
- BackupLink
- BackupPropertyData
- BackupObjectId //revive:disable-line:var-naming ID, not Id
- BackupReparseData
- BackupSparseBlock
- BackupTxfsData
-)
-
-const (
- StreamSparseAttributes = uint32(8)
-)
-
-//nolint:revive // var-naming: ALL_CAPS
-const (
- WRITE_DAC = windows.WRITE_DAC
- WRITE_OWNER = windows.WRITE_OWNER
- ACCESS_SYSTEM_SECURITY = windows.ACCESS_SYSTEM_SECURITY
-)
-
-// BackupHeader represents a backup stream of a file.
-type BackupHeader struct {
- //revive:disable-next-line:var-naming ID, not Id
- Id uint32 // The backup stream ID
- Attributes uint32 // Stream attributes
- Size int64 // The size of the stream in bytes
- Name string // The name of the stream (for BackupAlternateData only).
- Offset int64 // The offset of the stream in the file (for BackupSparseBlock only).
-}
-
-type win32StreamID struct {
- StreamID uint32
- Attributes uint32
- Size uint64
- NameSize uint32
-}
-
-// BackupStreamReader reads from a stream produced by the BackupRead Win32 API and produces a series
-// of BackupHeader values.
-type BackupStreamReader struct {
- r io.Reader
- bytesLeft int64
-}
-
-// NewBackupStreamReader produces a BackupStreamReader from any io.Reader.
-func NewBackupStreamReader(r io.Reader) *BackupStreamReader {
- return &BackupStreamReader{r, 0}
-}
-
-// Next returns the next backup stream and prepares for calls to Read(). It skips the remainder of the current stream if
-// it was not completely read.
-func (r *BackupStreamReader) Next() (*BackupHeader, error) {
- if r.bytesLeft > 0 { //nolint:nestif // todo: flatten this
- if s, ok := r.r.(io.Seeker); ok {
- // Make sure Seek on io.SeekCurrent sometimes succeeds
- // before trying the actual seek.
- if _, err := s.Seek(0, io.SeekCurrent); err == nil {
- if _, err = s.Seek(r.bytesLeft, io.SeekCurrent); err != nil {
- return nil, err
- }
- r.bytesLeft = 0
- }
- }
- if _, err := io.Copy(io.Discard, r); err != nil {
- return nil, err
- }
- }
- var wsi win32StreamID
- if err := binary.Read(r.r, binary.LittleEndian, &wsi); err != nil {
- return nil, err
- }
- hdr := &BackupHeader{
- Id: wsi.StreamID,
- Attributes: wsi.Attributes,
- Size: int64(wsi.Size),
- }
- if wsi.NameSize != 0 {
- name := make([]uint16, int(wsi.NameSize/2))
- if err := binary.Read(r.r, binary.LittleEndian, name); err != nil {
- return nil, err
- }
- hdr.Name = windows.UTF16ToString(name)
- }
- if wsi.StreamID == BackupSparseBlock {
- if err := binary.Read(r.r, binary.LittleEndian, &hdr.Offset); err != nil {
- return nil, err
- }
- hdr.Size -= 8
- }
- r.bytesLeft = hdr.Size
- return hdr, nil
-}
-
-// Read reads from the current backup stream.
-func (r *BackupStreamReader) Read(b []byte) (int, error) {
- if r.bytesLeft == 0 {
- return 0, io.EOF
- }
- if int64(len(b)) > r.bytesLeft {
- b = b[:r.bytesLeft]
- }
- n, err := r.r.Read(b)
- r.bytesLeft -= int64(n)
- if err == io.EOF {
- err = io.ErrUnexpectedEOF
- } else if r.bytesLeft == 0 && err == nil {
- err = io.EOF
- }
- return n, err
-}
-
-// BackupStreamWriter writes a stream compatible with the BackupWrite Win32 API.
-type BackupStreamWriter struct {
- w io.Writer
- bytesLeft int64
-}
-
-// NewBackupStreamWriter produces a BackupStreamWriter on top of an io.Writer.
-func NewBackupStreamWriter(w io.Writer) *BackupStreamWriter {
- return &BackupStreamWriter{w, 0}
-}
-
-// WriteHeader writes the next backup stream header and prepares for calls to Write().
-func (w *BackupStreamWriter) WriteHeader(hdr *BackupHeader) error {
- if w.bytesLeft != 0 {
- return fmt.Errorf("missing %d bytes", w.bytesLeft)
- }
- name := utf16.Encode([]rune(hdr.Name))
- wsi := win32StreamID{
- StreamID: hdr.Id,
- Attributes: hdr.Attributes,
- Size: uint64(hdr.Size),
- NameSize: uint32(len(name) * 2),
- }
- if hdr.Id == BackupSparseBlock {
- // Include space for the int64 block offset
- wsi.Size += 8
- }
- if err := binary.Write(w.w, binary.LittleEndian, &wsi); err != nil {
- return err
- }
- if len(name) != 0 {
- if err := binary.Write(w.w, binary.LittleEndian, name); err != nil {
- return err
- }
- }
- if hdr.Id == BackupSparseBlock {
- if err := binary.Write(w.w, binary.LittleEndian, hdr.Offset); err != nil {
- return err
- }
- }
- w.bytesLeft = hdr.Size
- return nil
-}
-
-// Write writes to the current backup stream.
-func (w *BackupStreamWriter) Write(b []byte) (int, error) {
- if w.bytesLeft < int64(len(b)) {
- return 0, fmt.Errorf("too many bytes by %d", int64(len(b))-w.bytesLeft)
- }
- n, err := w.w.Write(b)
- w.bytesLeft -= int64(n)
- return n, err
-}
-
-// BackupFileReader provides an io.ReadCloser interface on top of the BackupRead Win32 API.
-type BackupFileReader struct {
- f *os.File
- includeSecurity bool
- ctx uintptr
-}
-
-// NewBackupFileReader returns a new BackupFileReader from a file handle. If includeSecurity is true,
-// Read will attempt to read the security descriptor of the file.
-func NewBackupFileReader(f *os.File, includeSecurity bool) *BackupFileReader {
- r := &BackupFileReader{f, includeSecurity, 0}
- return r
-}
-
-// Read reads a backup stream from the file by calling the Win32 API BackupRead().
-func (r *BackupFileReader) Read(b []byte) (int, error) {
- var bytesRead uint32
- err := backupRead(windows.Handle(r.f.Fd()), b, &bytesRead, false, r.includeSecurity, &r.ctx)
- if err != nil {
- return 0, &os.PathError{Op: "BackupRead", Path: r.f.Name(), Err: err}
- }
- runtime.KeepAlive(r.f)
- if bytesRead == 0 {
- return 0, io.EOF
- }
- return int(bytesRead), nil
-}
-
-// Close frees Win32 resources associated with the BackupFileReader. It does not close
-// the underlying file.
-func (r *BackupFileReader) Close() error {
- if r.ctx != 0 {
- _ = backupRead(windows.Handle(r.f.Fd()), nil, nil, true, false, &r.ctx)
- runtime.KeepAlive(r.f)
- r.ctx = 0
- }
- return nil
-}
-
-// BackupFileWriter provides an io.WriteCloser interface on top of the BackupWrite Win32 API.
-type BackupFileWriter struct {
- f *os.File
- includeSecurity bool
- ctx uintptr
-}
-
-// NewBackupFileWriter returns a new BackupFileWriter from a file handle. If includeSecurity is true,
-// Write() will attempt to restore the security descriptor from the stream.
-func NewBackupFileWriter(f *os.File, includeSecurity bool) *BackupFileWriter {
- w := &BackupFileWriter{f, includeSecurity, 0}
- return w
-}
-
-// Write restores a portion of the file using the provided backup stream.
-func (w *BackupFileWriter) Write(b []byte) (int, error) {
- var bytesWritten uint32
- err := backupWrite(windows.Handle(w.f.Fd()), b, &bytesWritten, false, w.includeSecurity, &w.ctx)
- if err != nil {
- return 0, &os.PathError{Op: "BackupWrite", Path: w.f.Name(), Err: err}
- }
- runtime.KeepAlive(w.f)
- if int(bytesWritten) != len(b) {
- return int(bytesWritten), errors.New("not all bytes could be written")
- }
- return len(b), nil
-}
-
-// Close frees Win32 resources associated with the BackupFileWriter. It does not
-// close the underlying file.
-func (w *BackupFileWriter) Close() error {
- if w.ctx != 0 {
- _ = backupWrite(windows.Handle(w.f.Fd()), nil, nil, true, false, &w.ctx)
- runtime.KeepAlive(w.f)
- w.ctx = 0
- }
- return nil
-}
-
-// OpenForBackup opens a file or directory, potentially skipping access checks if the backup
-// or restore privileges have been acquired.
-//
-// If the file opened was a directory, it cannot be used with Readdir().
-func OpenForBackup(path string, access uint32, share uint32, createmode uint32) (*os.File, error) {
- h, err := fs.CreateFile(path,
- fs.AccessMask(access),
- fs.FileShareMode(share),
- nil,
- fs.FileCreationDisposition(createmode),
- fs.FILE_FLAG_BACKUP_SEMANTICS|fs.FILE_FLAG_OPEN_REPARSE_POINT,
- 0,
- )
- if err != nil {
- err = &os.PathError{Op: "open", Path: path, Err: err}
- return nil, err
- }
- return os.NewFile(uintptr(h), path), nil
-}
diff --git a/vendor/github.com/Microsoft/go-winio/doc.go b/vendor/github.com/Microsoft/go-winio/doc.go
deleted file mode 100644
index 1f5bfe2d..00000000
--- a/vendor/github.com/Microsoft/go-winio/doc.go
+++ /dev/null
@@ -1,22 +0,0 @@
-// This package provides utilities for efficiently performing Win32 IO operations in Go.
-// Currently, this package is provides support for genreal IO and management of
-// - named pipes
-// - files
-// - [Hyper-V sockets]
-//
-// This code is similar to Go's [net] package, and uses IO completion ports to avoid
-// blocking IO on system threads, allowing Go to reuse the thread to schedule other goroutines.
-//
-// This limits support to Windows Vista and newer operating systems.
-//
-// Additionally, this package provides support for:
-// - creating and managing GUIDs
-// - writing to [ETW]
-// - opening and manageing VHDs
-// - parsing [Windows Image files]
-// - auto-generating Win32 API code
-//
-// [Hyper-V sockets]: https://docs.microsoft.com/en-us/virtualization/hyper-v-on-windows/user-guide/make-integration-service
-// [ETW]: https://docs.microsoft.com/en-us/windows-hardware/drivers/devtest/event-tracing-for-windows--etw-
-// [Windows Image files]: https://docs.microsoft.com/en-us/windows-hardware/manufacture/desktop/work-with-windows-images
-package winio
diff --git a/vendor/github.com/Microsoft/go-winio/ea.go b/vendor/github.com/Microsoft/go-winio/ea.go
deleted file mode 100644
index e104dbdf..00000000
--- a/vendor/github.com/Microsoft/go-winio/ea.go
+++ /dev/null
@@ -1,137 +0,0 @@
-package winio
-
-import (
- "bytes"
- "encoding/binary"
- "errors"
-)
-
-type fileFullEaInformation struct {
- NextEntryOffset uint32
- Flags uint8
- NameLength uint8
- ValueLength uint16
-}
-
-var (
- fileFullEaInformationSize = binary.Size(&fileFullEaInformation{})
-
- errInvalidEaBuffer = errors.New("invalid extended attribute buffer")
- errEaNameTooLarge = errors.New("extended attribute name too large")
- errEaValueTooLarge = errors.New("extended attribute value too large")
-)
-
-// ExtendedAttribute represents a single Windows EA.
-type ExtendedAttribute struct {
- Name string
- Value []byte
- Flags uint8
-}
-
-func parseEa(b []byte) (ea ExtendedAttribute, nb []byte, err error) {
- var info fileFullEaInformation
- err = binary.Read(bytes.NewReader(b), binary.LittleEndian, &info)
- if err != nil {
- err = errInvalidEaBuffer
- return ea, nb, err
- }
-
- nameOffset := fileFullEaInformationSize
- nameLen := int(info.NameLength)
- valueOffset := nameOffset + int(info.NameLength) + 1
- valueLen := int(info.ValueLength)
- nextOffset := int(info.NextEntryOffset)
- if valueLen+valueOffset > len(b) || nextOffset < 0 || nextOffset > len(b) {
- err = errInvalidEaBuffer
- return ea, nb, err
- }
-
- ea.Name = string(b[nameOffset : nameOffset+nameLen])
- ea.Value = b[valueOffset : valueOffset+valueLen]
- ea.Flags = info.Flags
- if info.NextEntryOffset != 0 {
- nb = b[info.NextEntryOffset:]
- }
- return ea, nb, err
-}
-
-// DecodeExtendedAttributes decodes a list of EAs from a FILE_FULL_EA_INFORMATION
-// buffer retrieved from BackupRead, ZwQueryEaFile, etc.
-func DecodeExtendedAttributes(b []byte) (eas []ExtendedAttribute, err error) {
- for len(b) != 0 {
- ea, nb, err := parseEa(b)
- if err != nil {
- return nil, err
- }
-
- eas = append(eas, ea)
- b = nb
- }
- return eas, err
-}
-
-func writeEa(buf *bytes.Buffer, ea *ExtendedAttribute, last bool) error {
- if int(uint8(len(ea.Name))) != len(ea.Name) {
- return errEaNameTooLarge
- }
- if int(uint16(len(ea.Value))) != len(ea.Value) {
- return errEaValueTooLarge
- }
- entrySize := uint32(fileFullEaInformationSize + len(ea.Name) + 1 + len(ea.Value))
- withPadding := (entrySize + 3) &^ 3
- nextOffset := uint32(0)
- if !last {
- nextOffset = withPadding
- }
- info := fileFullEaInformation{
- NextEntryOffset: nextOffset,
- Flags: ea.Flags,
- NameLength: uint8(len(ea.Name)),
- ValueLength: uint16(len(ea.Value)),
- }
-
- err := binary.Write(buf, binary.LittleEndian, &info)
- if err != nil {
- return err
- }
-
- _, err = buf.Write([]byte(ea.Name))
- if err != nil {
- return err
- }
-
- err = buf.WriteByte(0)
- if err != nil {
- return err
- }
-
- _, err = buf.Write(ea.Value)
- if err != nil {
- return err
- }
-
- _, err = buf.Write([]byte{0, 0, 0}[0 : withPadding-entrySize])
- if err != nil {
- return err
- }
-
- return nil
-}
-
-// EncodeExtendedAttributes encodes a list of EAs into a FILE_FULL_EA_INFORMATION
-// buffer for use with BackupWrite, ZwSetEaFile, etc.
-func EncodeExtendedAttributes(eas []ExtendedAttribute) ([]byte, error) {
- var buf bytes.Buffer
- for i := range eas {
- last := false
- if i == len(eas)-1 {
- last = true
- }
-
- err := writeEa(&buf, &eas[i], last)
- if err != nil {
- return nil, err
- }
- }
- return buf.Bytes(), nil
-}
diff --git a/vendor/github.com/Microsoft/go-winio/file.go b/vendor/github.com/Microsoft/go-winio/file.go
deleted file mode 100644
index fe82a180..00000000
--- a/vendor/github.com/Microsoft/go-winio/file.go
+++ /dev/null
@@ -1,320 +0,0 @@
-//go:build windows
-// +build windows
-
-package winio
-
-import (
- "errors"
- "io"
- "runtime"
- "sync"
- "sync/atomic"
- "syscall"
- "time"
-
- "golang.org/x/sys/windows"
-)
-
-//sys cancelIoEx(file windows.Handle, o *windows.Overlapped) (err error) = CancelIoEx
-//sys createIoCompletionPort(file windows.Handle, port windows.Handle, key uintptr, threadCount uint32) (newport windows.Handle, err error) = CreateIoCompletionPort
-//sys getQueuedCompletionStatus(port windows.Handle, bytes *uint32, key *uintptr, o **ioOperation, timeout uint32) (err error) = GetQueuedCompletionStatus
-//sys setFileCompletionNotificationModes(h windows.Handle, flags uint8) (err error) = SetFileCompletionNotificationModes
-//sys wsaGetOverlappedResult(h windows.Handle, o *windows.Overlapped, bytes *uint32, wait bool, flags *uint32) (err error) = ws2_32.WSAGetOverlappedResult
-
-var (
- ErrFileClosed = errors.New("file has already been closed")
- ErrTimeout = &timeoutError{}
-)
-
-type timeoutError struct{}
-
-func (*timeoutError) Error() string { return "i/o timeout" }
-func (*timeoutError) Timeout() bool { return true }
-func (*timeoutError) Temporary() bool { return true }
-
-type timeoutChan chan struct{}
-
-var ioInitOnce sync.Once
-var ioCompletionPort windows.Handle
-
-// ioResult contains the result of an asynchronous IO operation.
-type ioResult struct {
- bytes uint32
- err error
-}
-
-// ioOperation represents an outstanding asynchronous Win32 IO.
-type ioOperation struct {
- o windows.Overlapped
- ch chan ioResult
-}
-
-func initIO() {
- h, err := createIoCompletionPort(windows.InvalidHandle, 0, 0, 0xffffffff)
- if err != nil {
- panic(err)
- }
- ioCompletionPort = h
- go ioCompletionProcessor(h)
-}
-
-// win32File implements Reader, Writer, and Closer on a Win32 handle without blocking in a syscall.
-// It takes ownership of this handle and will close it if it is garbage collected.
-type win32File struct {
- handle windows.Handle
- wg sync.WaitGroup
- wgLock sync.RWMutex
- closing atomic.Bool
- socket bool
- readDeadline deadlineHandler
- writeDeadline deadlineHandler
-}
-
-type deadlineHandler struct {
- setLock sync.Mutex
- channel timeoutChan
- channelLock sync.RWMutex
- timer *time.Timer
- timedout atomic.Bool
-}
-
-// makeWin32File makes a new win32File from an existing file handle.
-func makeWin32File(h windows.Handle) (*win32File, error) {
- f := &win32File{handle: h}
- ioInitOnce.Do(initIO)
- _, err := createIoCompletionPort(h, ioCompletionPort, 0, 0xffffffff)
- if err != nil {
- return nil, err
- }
- err = setFileCompletionNotificationModes(h, windows.FILE_SKIP_COMPLETION_PORT_ON_SUCCESS|windows.FILE_SKIP_SET_EVENT_ON_HANDLE)
- if err != nil {
- return nil, err
- }
- f.readDeadline.channel = make(timeoutChan)
- f.writeDeadline.channel = make(timeoutChan)
- return f, nil
-}
-
-// Deprecated: use NewOpenFile instead.
-func MakeOpenFile(h syscall.Handle) (io.ReadWriteCloser, error) {
- return NewOpenFile(windows.Handle(h))
-}
-
-func NewOpenFile(h windows.Handle) (io.ReadWriteCloser, error) {
- // If we return the result of makeWin32File directly, it can result in an
- // interface-wrapped nil, rather than a nil interface value.
- f, err := makeWin32File(h)
- if err != nil {
- return nil, err
- }
- return f, nil
-}
-
-// closeHandle closes the resources associated with a Win32 handle.
-func (f *win32File) closeHandle() {
- f.wgLock.Lock()
- // Atomically set that we are closing, releasing the resources only once.
- if !f.closing.Swap(true) {
- f.wgLock.Unlock()
- // cancel all IO and wait for it to complete
- _ = cancelIoEx(f.handle, nil)
- f.wg.Wait()
- // at this point, no new IO can start
- windows.Close(f.handle)
- f.handle = 0
- } else {
- f.wgLock.Unlock()
- }
-}
-
-// Close closes a win32File.
-func (f *win32File) Close() error {
- f.closeHandle()
- return nil
-}
-
-// IsClosed checks if the file has been closed.
-func (f *win32File) IsClosed() bool {
- return f.closing.Load()
-}
-
-// prepareIO prepares for a new IO operation.
-// The caller must call f.wg.Done() when the IO is finished, prior to Close() returning.
-func (f *win32File) prepareIO() (*ioOperation, error) {
- f.wgLock.RLock()
- if f.closing.Load() {
- f.wgLock.RUnlock()
- return nil, ErrFileClosed
- }
- f.wg.Add(1)
- f.wgLock.RUnlock()
- c := &ioOperation{}
- c.ch = make(chan ioResult)
- return c, nil
-}
-
-// ioCompletionProcessor processes completed async IOs forever.
-func ioCompletionProcessor(h windows.Handle) {
- for {
- var bytes uint32
- var key uintptr
- var op *ioOperation
- err := getQueuedCompletionStatus(h, &bytes, &key, &op, windows.INFINITE)
- if op == nil {
- panic(err)
- }
- op.ch <- ioResult{bytes, err}
- }
-}
-
-// todo: helsaawy - create an asyncIO version that takes a context
-
-// asyncIO processes the return value from ReadFile or WriteFile, blocking until
-// the operation has actually completed.
-func (f *win32File) asyncIO(c *ioOperation, d *deadlineHandler, bytes uint32, err error) (int, error) {
- if err != windows.ERROR_IO_PENDING { //nolint:errorlint // err is Errno
- return int(bytes), err
- }
-
- if f.closing.Load() {
- _ = cancelIoEx(f.handle, &c.o)
- }
-
- var timeout timeoutChan
- if d != nil {
- d.channelLock.Lock()
- timeout = d.channel
- d.channelLock.Unlock()
- }
-
- var r ioResult
- select {
- case r = <-c.ch:
- err = r.err
- if err == windows.ERROR_OPERATION_ABORTED { //nolint:errorlint // err is Errno
- if f.closing.Load() {
- err = ErrFileClosed
- }
- } else if err != nil && f.socket {
- // err is from Win32. Query the overlapped structure to get the winsock error.
- var bytes, flags uint32
- err = wsaGetOverlappedResult(f.handle, &c.o, &bytes, false, &flags)
- }
- case <-timeout:
- _ = cancelIoEx(f.handle, &c.o)
- r = <-c.ch
- err = r.err
- if err == windows.ERROR_OPERATION_ABORTED { //nolint:errorlint // err is Errno
- err = ErrTimeout
- }
- }
-
- // runtime.KeepAlive is needed, as c is passed via native
- // code to ioCompletionProcessor, c must remain alive
- // until the channel read is complete.
- // todo: (de)allocate *ioOperation via win32 heap functions, instead of needing to KeepAlive?
- runtime.KeepAlive(c)
- return int(r.bytes), err
-}
-
-// Read reads from a file handle.
-func (f *win32File) Read(b []byte) (int, error) {
- c, err := f.prepareIO()
- if err != nil {
- return 0, err
- }
- defer f.wg.Done()
-
- if f.readDeadline.timedout.Load() {
- return 0, ErrTimeout
- }
-
- var bytes uint32
- err = windows.ReadFile(f.handle, b, &bytes, &c.o)
- n, err := f.asyncIO(c, &f.readDeadline, bytes, err)
- runtime.KeepAlive(b)
-
- // Handle EOF conditions.
- if err == nil && n == 0 && len(b) != 0 {
- return 0, io.EOF
- } else if err == windows.ERROR_BROKEN_PIPE { //nolint:errorlint // err is Errno
- return 0, io.EOF
- }
- return n, err
-}
-
-// Write writes to a file handle.
-func (f *win32File) Write(b []byte) (int, error) {
- c, err := f.prepareIO()
- if err != nil {
- return 0, err
- }
- defer f.wg.Done()
-
- if f.writeDeadline.timedout.Load() {
- return 0, ErrTimeout
- }
-
- var bytes uint32
- err = windows.WriteFile(f.handle, b, &bytes, &c.o)
- n, err := f.asyncIO(c, &f.writeDeadline, bytes, err)
- runtime.KeepAlive(b)
- return n, err
-}
-
-func (f *win32File) SetReadDeadline(deadline time.Time) error {
- return f.readDeadline.set(deadline)
-}
-
-func (f *win32File) SetWriteDeadline(deadline time.Time) error {
- return f.writeDeadline.set(deadline)
-}
-
-func (f *win32File) Flush() error {
- return windows.FlushFileBuffers(f.handle)
-}
-
-func (f *win32File) Fd() uintptr {
- return uintptr(f.handle)
-}
-
-func (d *deadlineHandler) set(deadline time.Time) error {
- d.setLock.Lock()
- defer d.setLock.Unlock()
-
- if d.timer != nil {
- if !d.timer.Stop() {
- <-d.channel
- }
- d.timer = nil
- }
- d.timedout.Store(false)
-
- select {
- case <-d.channel:
- d.channelLock.Lock()
- d.channel = make(chan struct{})
- d.channelLock.Unlock()
- default:
- }
-
- if deadline.IsZero() {
- return nil
- }
-
- timeoutIO := func() {
- d.timedout.Store(true)
- close(d.channel)
- }
-
- now := time.Now()
- duration := deadline.Sub(now)
- if deadline.After(now) {
- // Deadline is in the future, set a timer to wait
- d.timer = time.AfterFunc(duration, timeoutIO)
- } else {
- // Deadline is in the past. Cancel all pending IO now.
- timeoutIO()
- }
- return nil
-}
diff --git a/vendor/github.com/Microsoft/go-winio/fileinfo.go b/vendor/github.com/Microsoft/go-winio/fileinfo.go
deleted file mode 100644
index c860eb99..00000000
--- a/vendor/github.com/Microsoft/go-winio/fileinfo.go
+++ /dev/null
@@ -1,106 +0,0 @@
-//go:build windows
-// +build windows
-
-package winio
-
-import (
- "os"
- "runtime"
- "unsafe"
-
- "golang.org/x/sys/windows"
-)
-
-// FileBasicInfo contains file access time and file attributes information.
-type FileBasicInfo struct {
- CreationTime, LastAccessTime, LastWriteTime, ChangeTime windows.Filetime
- FileAttributes uint32
- _ uint32 // padding
-}
-
-// alignedFileBasicInfo is a FileBasicInfo, but aligned to uint64 by containing
-// uint64 rather than windows.Filetime. Filetime contains two uint32s. uint64
-// alignment is necessary to pass this as FILE_BASIC_INFO.
-type alignedFileBasicInfo struct {
- CreationTime, LastAccessTime, LastWriteTime, ChangeTime uint64
- FileAttributes uint32
- _ uint32 // padding
-}
-
-// GetFileBasicInfo retrieves times and attributes for a file.
-func GetFileBasicInfo(f *os.File) (*FileBasicInfo, error) {
- bi := &alignedFileBasicInfo{}
- if err := windows.GetFileInformationByHandleEx(
- windows.Handle(f.Fd()),
- windows.FileBasicInfo,
- (*byte)(unsafe.Pointer(bi)),
- uint32(unsafe.Sizeof(*bi)),
- ); err != nil {
- return nil, &os.PathError{Op: "GetFileInformationByHandleEx", Path: f.Name(), Err: err}
- }
- runtime.KeepAlive(f)
- // Reinterpret the alignedFileBasicInfo as a FileBasicInfo so it matches the
- // public API of this module. The data may be unnecessarily aligned.
- return (*FileBasicInfo)(unsafe.Pointer(bi)), nil
-}
-
-// SetFileBasicInfo sets times and attributes for a file.
-func SetFileBasicInfo(f *os.File, bi *FileBasicInfo) error {
- // Create an alignedFileBasicInfo based on a FileBasicInfo. The copy is
- // suitable to pass to GetFileInformationByHandleEx.
- biAligned := *(*alignedFileBasicInfo)(unsafe.Pointer(bi))
- if err := windows.SetFileInformationByHandle(
- windows.Handle(f.Fd()),
- windows.FileBasicInfo,
- (*byte)(unsafe.Pointer(&biAligned)),
- uint32(unsafe.Sizeof(biAligned)),
- ); err != nil {
- return &os.PathError{Op: "SetFileInformationByHandle", Path: f.Name(), Err: err}
- }
- runtime.KeepAlive(f)
- return nil
-}
-
-// FileStandardInfo contains extended information for the file.
-// FILE_STANDARD_INFO in WinBase.h
-// https://docs.microsoft.com/en-us/windows/win32/api/winbase/ns-winbase-file_standard_info
-type FileStandardInfo struct {
- AllocationSize, EndOfFile int64
- NumberOfLinks uint32
- DeletePending, Directory bool
-}
-
-// GetFileStandardInfo retrieves ended information for the file.
-func GetFileStandardInfo(f *os.File) (*FileStandardInfo, error) {
- si := &FileStandardInfo{}
- if err := windows.GetFileInformationByHandleEx(windows.Handle(f.Fd()),
- windows.FileStandardInfo,
- (*byte)(unsafe.Pointer(si)),
- uint32(unsafe.Sizeof(*si))); err != nil {
- return nil, &os.PathError{Op: "GetFileInformationByHandleEx", Path: f.Name(), Err: err}
- }
- runtime.KeepAlive(f)
- return si, nil
-}
-
-// FileIDInfo contains the volume serial number and file ID for a file. This pair should be
-// unique on a system.
-type FileIDInfo struct {
- VolumeSerialNumber uint64
- FileID [16]byte
-}
-
-// GetFileID retrieves the unique (volume, file ID) pair for a file.
-func GetFileID(f *os.File) (*FileIDInfo, error) {
- fileID := &FileIDInfo{}
- if err := windows.GetFileInformationByHandleEx(
- windows.Handle(f.Fd()),
- windows.FileIdInfo,
- (*byte)(unsafe.Pointer(fileID)),
- uint32(unsafe.Sizeof(*fileID)),
- ); err != nil {
- return nil, &os.PathError{Op: "GetFileInformationByHandleEx", Path: f.Name(), Err: err}
- }
- runtime.KeepAlive(f)
- return fileID, nil
-}
diff --git a/vendor/github.com/Microsoft/go-winio/hvsock.go b/vendor/github.com/Microsoft/go-winio/hvsock.go
deleted file mode 100644
index c4fdd9d4..00000000
--- a/vendor/github.com/Microsoft/go-winio/hvsock.go
+++ /dev/null
@@ -1,582 +0,0 @@
-//go:build windows
-// +build windows
-
-package winio
-
-import (
- "context"
- "errors"
- "fmt"
- "io"
- "net"
- "os"
- "time"
- "unsafe"
-
- "golang.org/x/sys/windows"
-
- "github.com/Microsoft/go-winio/internal/socket"
- "github.com/Microsoft/go-winio/pkg/guid"
-)
-
-const afHVSock = 34 // AF_HYPERV
-
-// Well known Service and VM IDs
-// https://docs.microsoft.com/en-us/virtualization/hyper-v-on-windows/user-guide/make-integration-service#vmid-wildcards
-
-// HvsockGUIDWildcard is the wildcard VmId for accepting connections from all partitions.
-func HvsockGUIDWildcard() guid.GUID { // 00000000-0000-0000-0000-000000000000
- return guid.GUID{}
-}
-
-// HvsockGUIDBroadcast is the wildcard VmId for broadcasting sends to all partitions.
-func HvsockGUIDBroadcast() guid.GUID { // ffffffff-ffff-ffff-ffff-ffffffffffff
- return guid.GUID{
- Data1: 0xffffffff,
- Data2: 0xffff,
- Data3: 0xffff,
- Data4: [8]uint8{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
- }
-}
-
-// HvsockGUIDLoopback is the Loopback VmId for accepting connections to the same partition as the connector.
-func HvsockGUIDLoopback() guid.GUID { // e0e16197-dd56-4a10-9195-5ee7a155a838
- return guid.GUID{
- Data1: 0xe0e16197,
- Data2: 0xdd56,
- Data3: 0x4a10,
- Data4: [8]uint8{0x91, 0x95, 0x5e, 0xe7, 0xa1, 0x55, 0xa8, 0x38},
- }
-}
-
-// HvsockGUIDSiloHost is the address of a silo's host partition:
-// - The silo host of a hosted silo is the utility VM.
-// - The silo host of a silo on a physical host is the physical host.
-func HvsockGUIDSiloHost() guid.GUID { // 36bd0c5c-7276-4223-88ba-7d03b654c568
- return guid.GUID{
- Data1: 0x36bd0c5c,
- Data2: 0x7276,
- Data3: 0x4223,
- Data4: [8]byte{0x88, 0xba, 0x7d, 0x03, 0xb6, 0x54, 0xc5, 0x68},
- }
-}
-
-// HvsockGUIDChildren is the wildcard VmId for accepting connections from the connector's child partitions.
-func HvsockGUIDChildren() guid.GUID { // 90db8b89-0d35-4f79-8ce9-49ea0ac8b7cd
- return guid.GUID{
- Data1: 0x90db8b89,
- Data2: 0xd35,
- Data3: 0x4f79,
- Data4: [8]uint8{0x8c, 0xe9, 0x49, 0xea, 0xa, 0xc8, 0xb7, 0xcd},
- }
-}
-
-// HvsockGUIDParent is the wildcard VmId for accepting connections from the connector's parent partition.
-// Listening on this VmId accepts connection from:
-// - Inside silos: silo host partition.
-// - Inside hosted silo: host of the VM.
-// - Inside VM: VM host.
-// - Physical host: Not supported.
-func HvsockGUIDParent() guid.GUID { // a42e7cda-d03f-480c-9cc2-a4de20abb878
- return guid.GUID{
- Data1: 0xa42e7cda,
- Data2: 0xd03f,
- Data3: 0x480c,
- Data4: [8]uint8{0x9c, 0xc2, 0xa4, 0xde, 0x20, 0xab, 0xb8, 0x78},
- }
-}
-
-// hvsockVsockServiceTemplate is the Service GUID used for the VSOCK protocol.
-func hvsockVsockServiceTemplate() guid.GUID { // 00000000-facb-11e6-bd58-64006a7986d3
- return guid.GUID{
- Data2: 0xfacb,
- Data3: 0x11e6,
- Data4: [8]uint8{0xbd, 0x58, 0x64, 0x00, 0x6a, 0x79, 0x86, 0xd3},
- }
-}
-
-// An HvsockAddr is an address for a AF_HYPERV socket.
-type HvsockAddr struct {
- VMID guid.GUID
- ServiceID guid.GUID
-}
-
-type rawHvsockAddr struct {
- Family uint16
- _ uint16
- VMID guid.GUID
- ServiceID guid.GUID
-}
-
-var _ socket.RawSockaddr = &rawHvsockAddr{}
-
-// Network returns the address's network name, "hvsock".
-func (*HvsockAddr) Network() string {
- return "hvsock"
-}
-
-func (addr *HvsockAddr) String() string {
- return fmt.Sprintf("%s:%s", &addr.VMID, &addr.ServiceID)
-}
-
-// VsockServiceID returns an hvsock service ID corresponding to the specified AF_VSOCK port.
-func VsockServiceID(port uint32) guid.GUID {
- g := hvsockVsockServiceTemplate() // make a copy
- g.Data1 = port
- return g
-}
-
-func (addr *HvsockAddr) raw() rawHvsockAddr {
- return rawHvsockAddr{
- Family: afHVSock,
- VMID: addr.VMID,
- ServiceID: addr.ServiceID,
- }
-}
-
-func (addr *HvsockAddr) fromRaw(raw *rawHvsockAddr) {
- addr.VMID = raw.VMID
- addr.ServiceID = raw.ServiceID
-}
-
-// Sockaddr returns a pointer to and the size of this struct.
-//
-// Implements the [socket.RawSockaddr] interface, and allows use in
-// [socket.Bind] and [socket.ConnectEx].
-func (r *rawHvsockAddr) Sockaddr() (unsafe.Pointer, int32, error) {
- return unsafe.Pointer(r), int32(unsafe.Sizeof(rawHvsockAddr{})), nil
-}
-
-// Sockaddr interface allows use with `sockets.Bind()` and `.ConnectEx()`.
-func (r *rawHvsockAddr) FromBytes(b []byte) error {
- n := int(unsafe.Sizeof(rawHvsockAddr{}))
-
- if len(b) < n {
- return fmt.Errorf("got %d, want %d: %w", len(b), n, socket.ErrBufferSize)
- }
-
- copy(unsafe.Slice((*byte)(unsafe.Pointer(r)), n), b[:n])
- if r.Family != afHVSock {
- return fmt.Errorf("got %d, want %d: %w", r.Family, afHVSock, socket.ErrAddrFamily)
- }
-
- return nil
-}
-
-// HvsockListener is a socket listener for the AF_HYPERV address family.
-type HvsockListener struct {
- sock *win32File
- addr HvsockAddr
-}
-
-var _ net.Listener = &HvsockListener{}
-
-// HvsockConn is a connected socket of the AF_HYPERV address family.
-type HvsockConn struct {
- sock *win32File
- local, remote HvsockAddr
-}
-
-var _ net.Conn = &HvsockConn{}
-
-func newHVSocket() (*win32File, error) {
- fd, err := windows.Socket(afHVSock, windows.SOCK_STREAM, 1)
- if err != nil {
- return nil, os.NewSyscallError("socket", err)
- }
- f, err := makeWin32File(fd)
- if err != nil {
- windows.Close(fd)
- return nil, err
- }
- f.socket = true
- return f, nil
-}
-
-// ListenHvsock listens for connections on the specified hvsock address.
-func ListenHvsock(addr *HvsockAddr) (_ *HvsockListener, err error) {
- l := &HvsockListener{addr: *addr}
-
- var sock *win32File
- sock, err = newHVSocket()
- if err != nil {
- return nil, l.opErr("listen", err)
- }
- defer func() {
- if err != nil {
- _ = sock.Close()
- }
- }()
-
- sa := addr.raw()
- err = socket.Bind(sock.handle, &sa)
- if err != nil {
- return nil, l.opErr("listen", os.NewSyscallError("socket", err))
- }
- err = windows.Listen(sock.handle, 16)
- if err != nil {
- return nil, l.opErr("listen", os.NewSyscallError("listen", err))
- }
- return &HvsockListener{sock: sock, addr: *addr}, nil
-}
-
-func (l *HvsockListener) opErr(op string, err error) error {
- return &net.OpError{Op: op, Net: "hvsock", Addr: &l.addr, Err: err}
-}
-
-// Addr returns the listener's network address.
-func (l *HvsockListener) Addr() net.Addr {
- return &l.addr
-}
-
-// Accept waits for the next connection and returns it.
-func (l *HvsockListener) Accept() (_ net.Conn, err error) {
- sock, err := newHVSocket()
- if err != nil {
- return nil, l.opErr("accept", err)
- }
- defer func() {
- if sock != nil {
- sock.Close()
- }
- }()
- c, err := l.sock.prepareIO()
- if err != nil {
- return nil, l.opErr("accept", err)
- }
- defer l.sock.wg.Done()
-
- // AcceptEx, per documentation, requires an extra 16 bytes per address.
- //
- // https://docs.microsoft.com/en-us/windows/win32/api/mswsock/nf-mswsock-acceptex
- const addrlen = uint32(16 + unsafe.Sizeof(rawHvsockAddr{}))
- var addrbuf [addrlen * 2]byte
-
- var bytes uint32
- err = windows.AcceptEx(l.sock.handle, sock.handle, &addrbuf[0], 0 /* rxdatalen */, addrlen, addrlen, &bytes, &c.o)
- if _, err = l.sock.asyncIO(c, nil, bytes, err); err != nil {
- return nil, l.opErr("accept", os.NewSyscallError("acceptex", err))
- }
-
- conn := &HvsockConn{
- sock: sock,
- }
- // The local address returned in the AcceptEx buffer is the same as the Listener socket's
- // address. However, the service GUID reported by GetSockName is different from the Listeners
- // socket, and is sometimes the same as the local address of the socket that dialed the
- // address, with the service GUID.Data1 incremented, but othertimes is different.
- // todo: does the local address matter? is the listener's address or the actual address appropriate?
- conn.local.fromRaw((*rawHvsockAddr)(unsafe.Pointer(&addrbuf[0])))
- conn.remote.fromRaw((*rawHvsockAddr)(unsafe.Pointer(&addrbuf[addrlen])))
-
- // initialize the accepted socket and update its properties with those of the listening socket
- if err = windows.Setsockopt(sock.handle,
- windows.SOL_SOCKET, windows.SO_UPDATE_ACCEPT_CONTEXT,
- (*byte)(unsafe.Pointer(&l.sock.handle)), int32(unsafe.Sizeof(l.sock.handle))); err != nil {
- return nil, conn.opErr("accept", os.NewSyscallError("setsockopt", err))
- }
-
- sock = nil
- return conn, nil
-}
-
-// Close closes the listener, causing any pending Accept calls to fail.
-func (l *HvsockListener) Close() error {
- return l.sock.Close()
-}
-
-// HvsockDialer configures and dials a Hyper-V Socket (ie, [HvsockConn]).
-type HvsockDialer struct {
- // Deadline is the time the Dial operation must connect before erroring.
- Deadline time.Time
-
- // Retries is the number of additional connects to try if the connection times out, is refused,
- // or the host is unreachable
- Retries uint
-
- // RetryWait is the time to wait after a connection error to retry
- RetryWait time.Duration
-
- rt *time.Timer // redial wait timer
-}
-
-// Dial the Hyper-V socket at addr.
-//
-// See [HvsockDialer.Dial] for more information.
-func Dial(ctx context.Context, addr *HvsockAddr) (conn *HvsockConn, err error) {
- return (&HvsockDialer{}).Dial(ctx, addr)
-}
-
-// Dial attempts to connect to the Hyper-V socket at addr, and returns a connection if successful.
-// Will attempt (HvsockDialer).Retries if dialing fails, waiting (HvsockDialer).RetryWait between
-// retries.
-//
-// Dialing can be cancelled either by providing (HvsockDialer).Deadline, or cancelling ctx.
-func (d *HvsockDialer) Dial(ctx context.Context, addr *HvsockAddr) (conn *HvsockConn, err error) {
- op := "dial"
- // create the conn early to use opErr()
- conn = &HvsockConn{
- remote: *addr,
- }
-
- if !d.Deadline.IsZero() {
- var cancel context.CancelFunc
- ctx, cancel = context.WithDeadline(ctx, d.Deadline)
- defer cancel()
- }
-
- // preemptive timeout/cancellation check
- if err = ctx.Err(); err != nil {
- return nil, conn.opErr(op, err)
- }
-
- sock, err := newHVSocket()
- if err != nil {
- return nil, conn.opErr(op, err)
- }
- defer func() {
- if sock != nil {
- sock.Close()
- }
- }()
-
- sa := addr.raw()
- err = socket.Bind(sock.handle, &sa)
- if err != nil {
- return nil, conn.opErr(op, os.NewSyscallError("bind", err))
- }
-
- c, err := sock.prepareIO()
- if err != nil {
- return nil, conn.opErr(op, err)
- }
- defer sock.wg.Done()
- var bytes uint32
- for i := uint(0); i <= d.Retries; i++ {
- err = socket.ConnectEx(
- sock.handle,
- &sa,
- nil, // sendBuf
- 0, // sendDataLen
- &bytes,
- (*windows.Overlapped)(unsafe.Pointer(&c.o)))
- _, err = sock.asyncIO(c, nil, bytes, err)
- if i < d.Retries && canRedial(err) {
- if err = d.redialWait(ctx); err == nil {
- continue
- }
- }
- break
- }
- if err != nil {
- return nil, conn.opErr(op, os.NewSyscallError("connectex", err))
- }
-
- // update the connection properties, so shutdown can be used
- if err = windows.Setsockopt(
- sock.handle,
- windows.SOL_SOCKET,
- windows.SO_UPDATE_CONNECT_CONTEXT,
- nil, // optvalue
- 0, // optlen
- ); err != nil {
- return nil, conn.opErr(op, os.NewSyscallError("setsockopt", err))
- }
-
- // get the local name
- var sal rawHvsockAddr
- err = socket.GetSockName(sock.handle, &sal)
- if err != nil {
- return nil, conn.opErr(op, os.NewSyscallError("getsockname", err))
- }
- conn.local.fromRaw(&sal)
-
- // one last check for timeout, since asyncIO doesn't check the context
- if err = ctx.Err(); err != nil {
- return nil, conn.opErr(op, err)
- }
-
- conn.sock = sock
- sock = nil
-
- return conn, nil
-}
-
-// redialWait waits before attempting to redial, resetting the timer as appropriate.
-func (d *HvsockDialer) redialWait(ctx context.Context) (err error) {
- if d.RetryWait == 0 {
- return nil
- }
-
- if d.rt == nil {
- d.rt = time.NewTimer(d.RetryWait)
- } else {
- // should already be stopped and drained
- d.rt.Reset(d.RetryWait)
- }
-
- select {
- case <-ctx.Done():
- case <-d.rt.C:
- return nil
- }
-
- // stop and drain the timer
- if !d.rt.Stop() {
- <-d.rt.C
- }
- return ctx.Err()
-}
-
-// assumes error is a plain, unwrapped windows.Errno provided by direct syscall.
-func canRedial(err error) bool {
- //nolint:errorlint // guaranteed to be an Errno
- switch err {
- case windows.WSAECONNREFUSED, windows.WSAENETUNREACH, windows.WSAETIMEDOUT,
- windows.ERROR_CONNECTION_REFUSED, windows.ERROR_CONNECTION_UNAVAIL:
- return true
- default:
- return false
- }
-}
-
-func (conn *HvsockConn) opErr(op string, err error) error {
- // translate from "file closed" to "socket closed"
- if errors.Is(err, ErrFileClosed) {
- err = socket.ErrSocketClosed
- }
- return &net.OpError{Op: op, Net: "hvsock", Source: &conn.local, Addr: &conn.remote, Err: err}
-}
-
-func (conn *HvsockConn) Read(b []byte) (int, error) {
- c, err := conn.sock.prepareIO()
- if err != nil {
- return 0, conn.opErr("read", err)
- }
- defer conn.sock.wg.Done()
- buf := windows.WSABuf{Buf: &b[0], Len: uint32(len(b))}
- var flags, bytes uint32
- err = windows.WSARecv(conn.sock.handle, &buf, 1, &bytes, &flags, &c.o, nil)
- n, err := conn.sock.asyncIO(c, &conn.sock.readDeadline, bytes, err)
- if err != nil {
- var eno windows.Errno
- if errors.As(err, &eno) {
- err = os.NewSyscallError("wsarecv", eno)
- }
- return 0, conn.opErr("read", err)
- } else if n == 0 {
- err = io.EOF
- }
- return n, err
-}
-
-func (conn *HvsockConn) Write(b []byte) (int, error) {
- t := 0
- for len(b) != 0 {
- n, err := conn.write(b)
- if err != nil {
- return t + n, err
- }
- t += n
- b = b[n:]
- }
- return t, nil
-}
-
-func (conn *HvsockConn) write(b []byte) (int, error) {
- c, err := conn.sock.prepareIO()
- if err != nil {
- return 0, conn.opErr("write", err)
- }
- defer conn.sock.wg.Done()
- buf := windows.WSABuf{Buf: &b[0], Len: uint32(len(b))}
- var bytes uint32
- err = windows.WSASend(conn.sock.handle, &buf, 1, &bytes, 0, &c.o, nil)
- n, err := conn.sock.asyncIO(c, &conn.sock.writeDeadline, bytes, err)
- if err != nil {
- var eno windows.Errno
- if errors.As(err, &eno) {
- err = os.NewSyscallError("wsasend", eno)
- }
- return 0, conn.opErr("write", err)
- }
- return n, err
-}
-
-// Close closes the socket connection, failing any pending read or write calls.
-func (conn *HvsockConn) Close() error {
- return conn.sock.Close()
-}
-
-func (conn *HvsockConn) IsClosed() bool {
- return conn.sock.IsClosed()
-}
-
-// shutdown disables sending or receiving on a socket.
-func (conn *HvsockConn) shutdown(how int) error {
- if conn.IsClosed() {
- return socket.ErrSocketClosed
- }
-
- err := windows.Shutdown(conn.sock.handle, how)
- if err != nil {
- // If the connection was closed, shutdowns fail with "not connected"
- if errors.Is(err, windows.WSAENOTCONN) ||
- errors.Is(err, windows.WSAESHUTDOWN) {
- err = socket.ErrSocketClosed
- }
- return os.NewSyscallError("shutdown", err)
- }
- return nil
-}
-
-// CloseRead shuts down the read end of the socket, preventing future read operations.
-func (conn *HvsockConn) CloseRead() error {
- err := conn.shutdown(windows.SHUT_RD)
- if err != nil {
- return conn.opErr("closeread", err)
- }
- return nil
-}
-
-// CloseWrite shuts down the write end of the socket, preventing future write operations and
-// notifying the other endpoint that no more data will be written.
-func (conn *HvsockConn) CloseWrite() error {
- err := conn.shutdown(windows.SHUT_WR)
- if err != nil {
- return conn.opErr("closewrite", err)
- }
- return nil
-}
-
-// LocalAddr returns the local address of the connection.
-func (conn *HvsockConn) LocalAddr() net.Addr {
- return &conn.local
-}
-
-// RemoteAddr returns the remote address of the connection.
-func (conn *HvsockConn) RemoteAddr() net.Addr {
- return &conn.remote
-}
-
-// SetDeadline implements the net.Conn SetDeadline method.
-func (conn *HvsockConn) SetDeadline(t time.Time) error {
- // todo: implement `SetDeadline` for `win32File`
- if err := conn.SetReadDeadline(t); err != nil {
- return fmt.Errorf("set read deadline: %w", err)
- }
- if err := conn.SetWriteDeadline(t); err != nil {
- return fmt.Errorf("set write deadline: %w", err)
- }
- return nil
-}
-
-// SetReadDeadline implements the net.Conn SetReadDeadline method.
-func (conn *HvsockConn) SetReadDeadline(t time.Time) error {
- return conn.sock.SetReadDeadline(t)
-}
-
-// SetWriteDeadline implements the net.Conn SetWriteDeadline method.
-func (conn *HvsockConn) SetWriteDeadline(t time.Time) error {
- return conn.sock.SetWriteDeadline(t)
-}
diff --git a/vendor/github.com/Microsoft/go-winio/internal/fs/doc.go b/vendor/github.com/Microsoft/go-winio/internal/fs/doc.go
deleted file mode 100644
index 1f653881..00000000
--- a/vendor/github.com/Microsoft/go-winio/internal/fs/doc.go
+++ /dev/null
@@ -1,2 +0,0 @@
-// This package contains Win32 filesystem functionality.
-package fs
diff --git a/vendor/github.com/Microsoft/go-winio/internal/fs/fs.go b/vendor/github.com/Microsoft/go-winio/internal/fs/fs.go
deleted file mode 100644
index 0cd9621d..00000000
--- a/vendor/github.com/Microsoft/go-winio/internal/fs/fs.go
+++ /dev/null
@@ -1,262 +0,0 @@
-//go:build windows
-
-package fs
-
-import (
- "golang.org/x/sys/windows"
-
- "github.com/Microsoft/go-winio/internal/stringbuffer"
-)
-
-//go:generate go run github.com/Microsoft/go-winio/tools/mkwinsyscall -output zsyscall_windows.go fs.go
-
-// https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew
-//sys CreateFile(name string, access AccessMask, mode FileShareMode, sa *windows.SecurityAttributes, createmode FileCreationDisposition, attrs FileFlagOrAttribute, templatefile windows.Handle) (handle windows.Handle, err error) [failretval==windows.InvalidHandle] = CreateFileW
-
-const NullHandle windows.Handle = 0
-
-// AccessMask defines standard, specific, and generic rights.
-//
-// Used with CreateFile and NtCreateFile (and co.).
-//
-// Bitmask:
-// 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1
-// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
-// +---------------+---------------+-------------------------------+
-// |G|G|G|G|Resvd|A| StandardRights| SpecificRights |
-// |R|W|E|A| |S| | |
-// +-+-------------+---------------+-------------------------------+
-//
-// GR Generic Read
-// GW Generic Write
-// GE Generic Exectue
-// GA Generic All
-// Resvd Reserved
-// AS Access Security System
-//
-// https://learn.microsoft.com/en-us/windows/win32/secauthz/access-mask
-//
-// https://learn.microsoft.com/en-us/windows/win32/secauthz/generic-access-rights
-//
-// https://learn.microsoft.com/en-us/windows/win32/fileio/file-access-rights-constants
-type AccessMask = windows.ACCESS_MASK
-
-//nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API.
-const (
- // Not actually any.
- //
- // For CreateFile: "query certain metadata such as file, directory, or device attributes without accessing that file or device"
- // https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew#parameters
- FILE_ANY_ACCESS AccessMask = 0
-
- GENERIC_READ AccessMask = 0x8000_0000
- GENERIC_WRITE AccessMask = 0x4000_0000
- GENERIC_EXECUTE AccessMask = 0x2000_0000
- GENERIC_ALL AccessMask = 0x1000_0000
- ACCESS_SYSTEM_SECURITY AccessMask = 0x0100_0000
-
- // Specific Object Access
- // from ntioapi.h
-
- FILE_READ_DATA AccessMask = (0x0001) // file & pipe
- FILE_LIST_DIRECTORY AccessMask = (0x0001) // directory
-
- FILE_WRITE_DATA AccessMask = (0x0002) // file & pipe
- FILE_ADD_FILE AccessMask = (0x0002) // directory
-
- FILE_APPEND_DATA AccessMask = (0x0004) // file
- FILE_ADD_SUBDIRECTORY AccessMask = (0x0004) // directory
- FILE_CREATE_PIPE_INSTANCE AccessMask = (0x0004) // named pipe
-
- FILE_READ_EA AccessMask = (0x0008) // file & directory
- FILE_READ_PROPERTIES AccessMask = FILE_READ_EA
-
- FILE_WRITE_EA AccessMask = (0x0010) // file & directory
- FILE_WRITE_PROPERTIES AccessMask = FILE_WRITE_EA
-
- FILE_EXECUTE AccessMask = (0x0020) // file
- FILE_TRAVERSE AccessMask = (0x0020) // directory
-
- FILE_DELETE_CHILD AccessMask = (0x0040) // directory
-
- FILE_READ_ATTRIBUTES AccessMask = (0x0080) // all
-
- FILE_WRITE_ATTRIBUTES AccessMask = (0x0100) // all
-
- FILE_ALL_ACCESS AccessMask = (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x1FF)
- FILE_GENERIC_READ AccessMask = (STANDARD_RIGHTS_READ | FILE_READ_DATA | FILE_READ_ATTRIBUTES | FILE_READ_EA | SYNCHRONIZE)
- FILE_GENERIC_WRITE AccessMask = (STANDARD_RIGHTS_WRITE | FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA | FILE_APPEND_DATA | SYNCHRONIZE)
- FILE_GENERIC_EXECUTE AccessMask = (STANDARD_RIGHTS_EXECUTE | FILE_READ_ATTRIBUTES | FILE_EXECUTE | SYNCHRONIZE)
-
- SPECIFIC_RIGHTS_ALL AccessMask = 0x0000FFFF
-
- // Standard Access
- // from ntseapi.h
-
- DELETE AccessMask = 0x0001_0000
- READ_CONTROL AccessMask = 0x0002_0000
- WRITE_DAC AccessMask = 0x0004_0000
- WRITE_OWNER AccessMask = 0x0008_0000
- SYNCHRONIZE AccessMask = 0x0010_0000
-
- STANDARD_RIGHTS_REQUIRED AccessMask = 0x000F_0000
-
- STANDARD_RIGHTS_READ AccessMask = READ_CONTROL
- STANDARD_RIGHTS_WRITE AccessMask = READ_CONTROL
- STANDARD_RIGHTS_EXECUTE AccessMask = READ_CONTROL
-
- STANDARD_RIGHTS_ALL AccessMask = 0x001F_0000
-)
-
-type FileShareMode uint32
-
-//nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API.
-const (
- FILE_SHARE_NONE FileShareMode = 0x00
- FILE_SHARE_READ FileShareMode = 0x01
- FILE_SHARE_WRITE FileShareMode = 0x02
- FILE_SHARE_DELETE FileShareMode = 0x04
- FILE_SHARE_VALID_FLAGS FileShareMode = 0x07
-)
-
-type FileCreationDisposition uint32
-
-//nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API.
-const (
- // from winbase.h
-
- CREATE_NEW FileCreationDisposition = 0x01
- CREATE_ALWAYS FileCreationDisposition = 0x02
- OPEN_EXISTING FileCreationDisposition = 0x03
- OPEN_ALWAYS FileCreationDisposition = 0x04
- TRUNCATE_EXISTING FileCreationDisposition = 0x05
-)
-
-// Create disposition values for NtCreate*
-type NTFileCreationDisposition uint32
-
-//nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API.
-const (
- // From ntioapi.h
-
- FILE_SUPERSEDE NTFileCreationDisposition = 0x00
- FILE_OPEN NTFileCreationDisposition = 0x01
- FILE_CREATE NTFileCreationDisposition = 0x02
- FILE_OPEN_IF NTFileCreationDisposition = 0x03
- FILE_OVERWRITE NTFileCreationDisposition = 0x04
- FILE_OVERWRITE_IF NTFileCreationDisposition = 0x05
- FILE_MAXIMUM_DISPOSITION NTFileCreationDisposition = 0x05
-)
-
-// CreateFile and co. take flags or attributes together as one parameter.
-// Define alias until we can use generics to allow both
-//
-// https://learn.microsoft.com/en-us/windows/win32/fileio/file-attribute-constants
-type FileFlagOrAttribute uint32
-
-//nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API.
-const (
- // from winnt.h
-
- FILE_FLAG_WRITE_THROUGH FileFlagOrAttribute = 0x8000_0000
- FILE_FLAG_OVERLAPPED FileFlagOrAttribute = 0x4000_0000
- FILE_FLAG_NO_BUFFERING FileFlagOrAttribute = 0x2000_0000
- FILE_FLAG_RANDOM_ACCESS FileFlagOrAttribute = 0x1000_0000
- FILE_FLAG_SEQUENTIAL_SCAN FileFlagOrAttribute = 0x0800_0000
- FILE_FLAG_DELETE_ON_CLOSE FileFlagOrAttribute = 0x0400_0000
- FILE_FLAG_BACKUP_SEMANTICS FileFlagOrAttribute = 0x0200_0000
- FILE_FLAG_POSIX_SEMANTICS FileFlagOrAttribute = 0x0100_0000
- FILE_FLAG_OPEN_REPARSE_POINT FileFlagOrAttribute = 0x0020_0000
- FILE_FLAG_OPEN_NO_RECALL FileFlagOrAttribute = 0x0010_0000
- FILE_FLAG_FIRST_PIPE_INSTANCE FileFlagOrAttribute = 0x0008_0000
-)
-
-// NtCreate* functions take a dedicated CreateOptions parameter.
-//
-// https://learn.microsoft.com/en-us/windows/win32/api/Winternl/nf-winternl-ntcreatefile
-//
-// https://learn.microsoft.com/en-us/windows/win32/devnotes/nt-create-named-pipe-file
-type NTCreateOptions uint32
-
-//nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API.
-const (
- // From ntioapi.h
-
- FILE_DIRECTORY_FILE NTCreateOptions = 0x0000_0001
- FILE_WRITE_THROUGH NTCreateOptions = 0x0000_0002
- FILE_SEQUENTIAL_ONLY NTCreateOptions = 0x0000_0004
- FILE_NO_INTERMEDIATE_BUFFERING NTCreateOptions = 0x0000_0008
-
- FILE_SYNCHRONOUS_IO_ALERT NTCreateOptions = 0x0000_0010
- FILE_SYNCHRONOUS_IO_NONALERT NTCreateOptions = 0x0000_0020
- FILE_NON_DIRECTORY_FILE NTCreateOptions = 0x0000_0040
- FILE_CREATE_TREE_CONNECTION NTCreateOptions = 0x0000_0080
-
- FILE_COMPLETE_IF_OPLOCKED NTCreateOptions = 0x0000_0100
- FILE_NO_EA_KNOWLEDGE NTCreateOptions = 0x0000_0200
- FILE_DISABLE_TUNNELING NTCreateOptions = 0x0000_0400
- FILE_RANDOM_ACCESS NTCreateOptions = 0x0000_0800
-
- FILE_DELETE_ON_CLOSE NTCreateOptions = 0x0000_1000
- FILE_OPEN_BY_FILE_ID NTCreateOptions = 0x0000_2000
- FILE_OPEN_FOR_BACKUP_INTENT NTCreateOptions = 0x0000_4000
- FILE_NO_COMPRESSION NTCreateOptions = 0x0000_8000
-)
-
-type FileSQSFlag = FileFlagOrAttribute
-
-//nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API.
-const (
- // from winbase.h
-
- SECURITY_ANONYMOUS FileSQSFlag = FileSQSFlag(SecurityAnonymous << 16)
- SECURITY_IDENTIFICATION FileSQSFlag = FileSQSFlag(SecurityIdentification << 16)
- SECURITY_IMPERSONATION FileSQSFlag = FileSQSFlag(SecurityImpersonation << 16)
- SECURITY_DELEGATION FileSQSFlag = FileSQSFlag(SecurityDelegation << 16)
-
- SECURITY_SQOS_PRESENT FileSQSFlag = 0x0010_0000
- SECURITY_VALID_SQOS_FLAGS FileSQSFlag = 0x001F_0000
-)
-
-// GetFinalPathNameByHandle flags
-//
-// https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getfinalpathnamebyhandlew#parameters
-type GetFinalPathFlag uint32
-
-//nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API.
-const (
- GetFinalPathDefaultFlag GetFinalPathFlag = 0x0
-
- FILE_NAME_NORMALIZED GetFinalPathFlag = 0x0
- FILE_NAME_OPENED GetFinalPathFlag = 0x8
-
- VOLUME_NAME_DOS GetFinalPathFlag = 0x0
- VOLUME_NAME_GUID GetFinalPathFlag = 0x1
- VOLUME_NAME_NT GetFinalPathFlag = 0x2
- VOLUME_NAME_NONE GetFinalPathFlag = 0x4
-)
-
-// getFinalPathNameByHandle facilitates calling the Windows API GetFinalPathNameByHandle
-// with the given handle and flags. It transparently takes care of creating a buffer of the
-// correct size for the call.
-//
-// https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getfinalpathnamebyhandlew
-func GetFinalPathNameByHandle(h windows.Handle, flags GetFinalPathFlag) (string, error) {
- b := stringbuffer.NewWString()
- //TODO: can loop infinitely if Win32 keeps returning the same (or a larger) n?
- for {
- n, err := windows.GetFinalPathNameByHandle(h, b.Pointer(), b.Cap(), uint32(flags))
- if err != nil {
- return "", err
- }
- // If the buffer wasn't large enough, n will be the total size needed (including null terminator).
- // Resize and try again.
- if n > b.Cap() {
- b.ResizeTo(n)
- continue
- }
- // If the buffer is large enough, n will be the size not including the null terminator.
- // Convert to a Go string and return.
- return b.String(), nil
- }
-}
diff --git a/vendor/github.com/Microsoft/go-winio/internal/fs/security.go b/vendor/github.com/Microsoft/go-winio/internal/fs/security.go
deleted file mode 100644
index 81760ac6..00000000
--- a/vendor/github.com/Microsoft/go-winio/internal/fs/security.go
+++ /dev/null
@@ -1,12 +0,0 @@
-package fs
-
-// https://learn.microsoft.com/en-us/windows/win32/api/winnt/ne-winnt-security_impersonation_level
-type SecurityImpersonationLevel int32 // C default enums underlying type is `int`, which is Go `int32`
-
-// Impersonation levels
-const (
- SecurityAnonymous SecurityImpersonationLevel = 0
- SecurityIdentification SecurityImpersonationLevel = 1
- SecurityImpersonation SecurityImpersonationLevel = 2
- SecurityDelegation SecurityImpersonationLevel = 3
-)
diff --git a/vendor/github.com/Microsoft/go-winio/internal/fs/zsyscall_windows.go b/vendor/github.com/Microsoft/go-winio/internal/fs/zsyscall_windows.go
deleted file mode 100644
index a94e234c..00000000
--- a/vendor/github.com/Microsoft/go-winio/internal/fs/zsyscall_windows.go
+++ /dev/null
@@ -1,61 +0,0 @@
-//go:build windows
-
-// Code generated by 'go generate' using "github.com/Microsoft/go-winio/tools/mkwinsyscall"; DO NOT EDIT.
-
-package fs
-
-import (
- "syscall"
- "unsafe"
-
- "golang.org/x/sys/windows"
-)
-
-var _ unsafe.Pointer
-
-// Do the interface allocations only once for common
-// Errno values.
-const (
- errnoERROR_IO_PENDING = 997
-)
-
-var (
- errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING)
- errERROR_EINVAL error = syscall.EINVAL
-)
-
-// errnoErr returns common boxed Errno values, to prevent
-// allocations at runtime.
-func errnoErr(e syscall.Errno) error {
- switch e {
- case 0:
- return errERROR_EINVAL
- case errnoERROR_IO_PENDING:
- return errERROR_IO_PENDING
- }
- return e
-}
-
-var (
- modkernel32 = windows.NewLazySystemDLL("kernel32.dll")
-
- procCreateFileW = modkernel32.NewProc("CreateFileW")
-)
-
-func CreateFile(name string, access AccessMask, mode FileShareMode, sa *windows.SecurityAttributes, createmode FileCreationDisposition, attrs FileFlagOrAttribute, templatefile windows.Handle) (handle windows.Handle, err error) {
- var _p0 *uint16
- _p0, err = syscall.UTF16PtrFromString(name)
- if err != nil {
- return
- }
- return _CreateFile(_p0, access, mode, sa, createmode, attrs, templatefile)
-}
-
-func _CreateFile(name *uint16, access AccessMask, mode FileShareMode, sa *windows.SecurityAttributes, createmode FileCreationDisposition, attrs FileFlagOrAttribute, templatefile windows.Handle) (handle windows.Handle, err error) {
- r0, _, e1 := syscall.SyscallN(procCreateFileW.Addr(), uintptr(unsafe.Pointer(name)), uintptr(access), uintptr(mode), uintptr(unsafe.Pointer(sa)), uintptr(createmode), uintptr(attrs), uintptr(templatefile))
- handle = windows.Handle(r0)
- if handle == windows.InvalidHandle {
- err = errnoErr(e1)
- }
- return
-}
diff --git a/vendor/github.com/Microsoft/go-winio/internal/socket/rawaddr.go b/vendor/github.com/Microsoft/go-winio/internal/socket/rawaddr.go
deleted file mode 100644
index 7e82f9af..00000000
--- a/vendor/github.com/Microsoft/go-winio/internal/socket/rawaddr.go
+++ /dev/null
@@ -1,20 +0,0 @@
-package socket
-
-import (
- "unsafe"
-)
-
-// RawSockaddr allows structs to be used with [Bind] and [ConnectEx]. The
-// struct must meet the Win32 sockaddr requirements specified here:
-// https://docs.microsoft.com/en-us/windows/win32/winsock/sockaddr-2
-//
-// Specifically, the struct size must be least larger than an int16 (unsigned short)
-// for the address family.
-type RawSockaddr interface {
- // Sockaddr returns a pointer to the RawSockaddr and its struct size, allowing
- // for the RawSockaddr's data to be overwritten by syscalls (if necessary).
- //
- // It is the callers responsibility to validate that the values are valid; invalid
- // pointers or size can cause a panic.
- Sockaddr() (unsafe.Pointer, int32, error)
-}
diff --git a/vendor/github.com/Microsoft/go-winio/internal/socket/socket.go b/vendor/github.com/Microsoft/go-winio/internal/socket/socket.go
deleted file mode 100644
index 88580d97..00000000
--- a/vendor/github.com/Microsoft/go-winio/internal/socket/socket.go
+++ /dev/null
@@ -1,177 +0,0 @@
-//go:build windows
-
-package socket
-
-import (
- "errors"
- "fmt"
- "net"
- "sync"
- "syscall"
- "unsafe"
-
- "github.com/Microsoft/go-winio/pkg/guid"
- "golang.org/x/sys/windows"
-)
-
-//go:generate go run github.com/Microsoft/go-winio/tools/mkwinsyscall -output zsyscall_windows.go socket.go
-
-//sys getsockname(s windows.Handle, name unsafe.Pointer, namelen *int32) (err error) [failretval==socketError] = ws2_32.getsockname
-//sys getpeername(s windows.Handle, name unsafe.Pointer, namelen *int32) (err error) [failretval==socketError] = ws2_32.getpeername
-//sys bind(s windows.Handle, name unsafe.Pointer, namelen int32) (err error) [failretval==socketError] = ws2_32.bind
-
-const socketError = uintptr(^uint32(0))
-
-var (
- // todo(helsaawy): create custom error types to store the desired vs actual size and addr family?
-
- ErrBufferSize = errors.New("buffer size")
- ErrAddrFamily = errors.New("address family")
- ErrInvalidPointer = errors.New("invalid pointer")
- ErrSocketClosed = fmt.Errorf("socket closed: %w", net.ErrClosed)
-)
-
-// todo(helsaawy): replace these with generics, ie: GetSockName[S RawSockaddr](s windows.Handle) (S, error)
-
-// GetSockName writes the local address of socket s to the [RawSockaddr] rsa.
-// If rsa is not large enough, the [windows.WSAEFAULT] is returned.
-func GetSockName(s windows.Handle, rsa RawSockaddr) error {
- ptr, l, err := rsa.Sockaddr()
- if err != nil {
- return fmt.Errorf("could not retrieve socket pointer and size: %w", err)
- }
-
- // although getsockname returns WSAEFAULT if the buffer is too small, it does not set
- // &l to the correct size, so--apart from doubling the buffer repeatedly--there is no remedy
- return getsockname(s, ptr, &l)
-}
-
-// GetPeerName returns the remote address the socket is connected to.
-//
-// See [GetSockName] for more information.
-func GetPeerName(s windows.Handle, rsa RawSockaddr) error {
- ptr, l, err := rsa.Sockaddr()
- if err != nil {
- return fmt.Errorf("could not retrieve socket pointer and size: %w", err)
- }
-
- return getpeername(s, ptr, &l)
-}
-
-func Bind(s windows.Handle, rsa RawSockaddr) (err error) {
- ptr, l, err := rsa.Sockaddr()
- if err != nil {
- return fmt.Errorf("could not retrieve socket pointer and size: %w", err)
- }
-
- return bind(s, ptr, l)
-}
-
-// "golang.org/x/sys/windows".ConnectEx and .Bind only accept internal implementations of the
-// their sockaddr interface, so they cannot be used with HvsockAddr
-// Replicate functionality here from
-// https://cs.opensource.google/go/x/sys/+/master:windows/syscall_windows.go
-
-// The function pointers to `AcceptEx`, `ConnectEx` and `GetAcceptExSockaddrs` must be loaded at
-// runtime via a WSAIoctl call:
-// https://docs.microsoft.com/en-us/windows/win32/api/Mswsock/nc-mswsock-lpfn_connectex#remarks
-
-type runtimeFunc struct {
- id guid.GUID
- once sync.Once
- addr uintptr
- err error
-}
-
-func (f *runtimeFunc) Load() error {
- f.once.Do(func() {
- var s windows.Handle
- s, f.err = windows.Socket(windows.AF_INET, windows.SOCK_STREAM, windows.IPPROTO_TCP)
- if f.err != nil {
- return
- }
- defer windows.CloseHandle(s) //nolint:errcheck
-
- var n uint32
- f.err = windows.WSAIoctl(s,
- windows.SIO_GET_EXTENSION_FUNCTION_POINTER,
- (*byte)(unsafe.Pointer(&f.id)),
- uint32(unsafe.Sizeof(f.id)),
- (*byte)(unsafe.Pointer(&f.addr)),
- uint32(unsafe.Sizeof(f.addr)),
- &n,
- nil, // overlapped
- 0, // completionRoutine
- )
- })
- return f.err
-}
-
-var (
- // todo: add `AcceptEx` and `GetAcceptExSockaddrs`
- WSAID_CONNECTEX = guid.GUID{ //revive:disable-line:var-naming ALL_CAPS
- Data1: 0x25a207b9,
- Data2: 0xddf3,
- Data3: 0x4660,
- Data4: [8]byte{0x8e, 0xe9, 0x76, 0xe5, 0x8c, 0x74, 0x06, 0x3e},
- }
-
- connectExFunc = runtimeFunc{id: WSAID_CONNECTEX}
-)
-
-func ConnectEx(
- fd windows.Handle,
- rsa RawSockaddr,
- sendBuf *byte,
- sendDataLen uint32,
- bytesSent *uint32,
- overlapped *windows.Overlapped,
-) error {
- if err := connectExFunc.Load(); err != nil {
- return fmt.Errorf("failed to load ConnectEx function pointer: %w", err)
- }
- ptr, n, err := rsa.Sockaddr()
- if err != nil {
- return err
- }
- return connectEx(fd, ptr, n, sendBuf, sendDataLen, bytesSent, overlapped)
-}
-
-// BOOL LpfnConnectex(
-// [in] SOCKET s,
-// [in] const sockaddr *name,
-// [in] int namelen,
-// [in, optional] PVOID lpSendBuffer,
-// [in] DWORD dwSendDataLength,
-// [out] LPDWORD lpdwBytesSent,
-// [in] LPOVERLAPPED lpOverlapped
-// )
-
-func connectEx(
- s windows.Handle,
- name unsafe.Pointer,
- namelen int32,
- sendBuf *byte,
- sendDataLen uint32,
- bytesSent *uint32,
- overlapped *windows.Overlapped,
-) (err error) {
- r1, _, e1 := syscall.SyscallN(connectExFunc.addr,
- uintptr(s),
- uintptr(name),
- uintptr(namelen),
- uintptr(unsafe.Pointer(sendBuf)),
- uintptr(sendDataLen),
- uintptr(unsafe.Pointer(bytesSent)),
- uintptr(unsafe.Pointer(overlapped)),
- )
-
- if r1 == 0 {
- if e1 != 0 {
- err = error(e1)
- } else {
- err = syscall.EINVAL
- }
- }
- return err
-}
diff --git a/vendor/github.com/Microsoft/go-winio/internal/socket/zsyscall_windows.go b/vendor/github.com/Microsoft/go-winio/internal/socket/zsyscall_windows.go
deleted file mode 100644
index e1504126..00000000
--- a/vendor/github.com/Microsoft/go-winio/internal/socket/zsyscall_windows.go
+++ /dev/null
@@ -1,69 +0,0 @@
-//go:build windows
-
-// Code generated by 'go generate' using "github.com/Microsoft/go-winio/tools/mkwinsyscall"; DO NOT EDIT.
-
-package socket
-
-import (
- "syscall"
- "unsafe"
-
- "golang.org/x/sys/windows"
-)
-
-var _ unsafe.Pointer
-
-// Do the interface allocations only once for common
-// Errno values.
-const (
- errnoERROR_IO_PENDING = 997
-)
-
-var (
- errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING)
- errERROR_EINVAL error = syscall.EINVAL
-)
-
-// errnoErr returns common boxed Errno values, to prevent
-// allocations at runtime.
-func errnoErr(e syscall.Errno) error {
- switch e {
- case 0:
- return errERROR_EINVAL
- case errnoERROR_IO_PENDING:
- return errERROR_IO_PENDING
- }
- return e
-}
-
-var (
- modws2_32 = windows.NewLazySystemDLL("ws2_32.dll")
-
- procbind = modws2_32.NewProc("bind")
- procgetpeername = modws2_32.NewProc("getpeername")
- procgetsockname = modws2_32.NewProc("getsockname")
-)
-
-func bind(s windows.Handle, name unsafe.Pointer, namelen int32) (err error) {
- r1, _, e1 := syscall.SyscallN(procbind.Addr(), uintptr(s), uintptr(name), uintptr(namelen))
- if r1 == socketError {
- err = errnoErr(e1)
- }
- return
-}
-
-func getpeername(s windows.Handle, name unsafe.Pointer, namelen *int32) (err error) {
- r1, _, e1 := syscall.SyscallN(procgetpeername.Addr(), uintptr(s), uintptr(name), uintptr(unsafe.Pointer(namelen)))
- if r1 == socketError {
- err = errnoErr(e1)
- }
- return
-}
-
-func getsockname(s windows.Handle, name unsafe.Pointer, namelen *int32) (err error) {
- r1, _, e1 := syscall.SyscallN(procgetsockname.Addr(), uintptr(s), uintptr(name), uintptr(unsafe.Pointer(namelen)))
- if r1 == socketError {
- err = errnoErr(e1)
- }
- return
-}
diff --git a/vendor/github.com/Microsoft/go-winio/internal/stringbuffer/wstring.go b/vendor/github.com/Microsoft/go-winio/internal/stringbuffer/wstring.go
deleted file mode 100644
index 42ebc019..00000000
--- a/vendor/github.com/Microsoft/go-winio/internal/stringbuffer/wstring.go
+++ /dev/null
@@ -1,132 +0,0 @@
-package stringbuffer
-
-import (
- "sync"
- "unicode/utf16"
-)
-
-// TODO: worth exporting and using in mkwinsyscall?
-
-// Uint16BufferSize is the buffer size in the pool, chosen somewhat arbitrarily to accommodate
-// large path strings:
-// MAX_PATH (260) + size of volume GUID prefix (49) + null terminator = 310.
-const MinWStringCap = 310
-
-// use *[]uint16 since []uint16 creates an extra allocation where the slice header
-// is copied to heap and then referenced via pointer in the interface header that sync.Pool
-// stores.
-var pathPool = sync.Pool{ // if go1.18+ adds Pool[T], use that to store []uint16 directly
- New: func() interface{} {
- b := make([]uint16, MinWStringCap)
- return &b
- },
-}
-
-func newBuffer() []uint16 { return *(pathPool.Get().(*[]uint16)) }
-
-// freeBuffer copies the slice header data, and puts a pointer to that in the pool.
-// This avoids taking a pointer to the slice header in WString, which can be set to nil.
-func freeBuffer(b []uint16) { pathPool.Put(&b) }
-
-// WString is a wide string buffer ([]uint16) meant for storing UTF-16 encoded strings
-// for interacting with Win32 APIs.
-// Sizes are specified as uint32 and not int.
-//
-// It is not thread safe.
-type WString struct {
- // type-def allows casting to []uint16 directly, use struct to prevent that and allow adding fields in the future.
-
- // raw buffer
- b []uint16
-}
-
-// NewWString returns a [WString] allocated from a shared pool with an
-// initial capacity of at least [MinWStringCap].
-// Since the buffer may have been previously used, its contents are not guaranteed to be empty.
-//
-// The buffer should be freed via [WString.Free]
-func NewWString() *WString {
- return &WString{
- b: newBuffer(),
- }
-}
-
-func (b *WString) Free() {
- if b.empty() {
- return
- }
- freeBuffer(b.b)
- b.b = nil
-}
-
-// ResizeTo grows the buffer to at least c and returns the new capacity, freeing the
-// previous buffer back into pool.
-func (b *WString) ResizeTo(c uint32) uint32 {
- // already sufficient (or n is 0)
- if c <= b.Cap() {
- return b.Cap()
- }
-
- if c <= MinWStringCap {
- c = MinWStringCap
- }
- // allocate at-least double buffer size, as is done in [bytes.Buffer] and other places
- if c <= 2*b.Cap() {
- c = 2 * b.Cap()
- }
-
- b2 := make([]uint16, c)
- if !b.empty() {
- copy(b2, b.b)
- freeBuffer(b.b)
- }
- b.b = b2
- return c
-}
-
-// Buffer returns the underlying []uint16 buffer.
-func (b *WString) Buffer() []uint16 {
- if b.empty() {
- return nil
- }
- return b.b
-}
-
-// Pointer returns a pointer to the first uint16 in the buffer.
-// If the [WString.Free] has already been called, the pointer will be nil.
-func (b *WString) Pointer() *uint16 {
- if b.empty() {
- return nil
- }
- return &b.b[0]
-}
-
-// String returns the returns the UTF-8 encoding of the UTF-16 string in the buffer.
-//
-// It assumes that the data is null-terminated.
-func (b *WString) String() string {
- // Using [windows.UTF16ToString] would require importing "golang.org/x/sys/windows"
- // and would make this code Windows-only, which makes no sense.
- // So copy UTF16ToString code into here.
- // If other windows-specific code is added, switch to [windows.UTF16ToString]
-
- s := b.b
- for i, v := range s {
- if v == 0 {
- s = s[:i]
- break
- }
- }
- return string(utf16.Decode(s))
-}
-
-// Cap returns the underlying buffer capacity.
-func (b *WString) Cap() uint32 {
- if b.empty() {
- return 0
- }
- return b.cap()
-}
-
-func (b *WString) cap() uint32 { return uint32(cap(b.b)) }
-func (b *WString) empty() bool { return b == nil || b.cap() == 0 }
diff --git a/vendor/github.com/Microsoft/go-winio/pipe.go b/vendor/github.com/Microsoft/go-winio/pipe.go
deleted file mode 100644
index a2da6639..00000000
--- a/vendor/github.com/Microsoft/go-winio/pipe.go
+++ /dev/null
@@ -1,586 +0,0 @@
-//go:build windows
-// +build windows
-
-package winio
-
-import (
- "context"
- "errors"
- "fmt"
- "io"
- "net"
- "os"
- "runtime"
- "time"
- "unsafe"
-
- "golang.org/x/sys/windows"
-
- "github.com/Microsoft/go-winio/internal/fs"
-)
-
-//sys connectNamedPipe(pipe windows.Handle, o *windows.Overlapped) (err error) = ConnectNamedPipe
-//sys createNamedPipe(name string, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *windows.SecurityAttributes) (handle windows.Handle, err error) [failretval==windows.InvalidHandle] = CreateNamedPipeW
-//sys disconnectNamedPipe(pipe windows.Handle) (err error) = DisconnectNamedPipe
-//sys getNamedPipeInfo(pipe windows.Handle, flags *uint32, outSize *uint32, inSize *uint32, maxInstances *uint32) (err error) = GetNamedPipeInfo
-//sys getNamedPipeHandleState(pipe windows.Handle, state *uint32, curInstances *uint32, maxCollectionCount *uint32, collectDataTimeout *uint32, userName *uint16, maxUserNameSize uint32) (err error) = GetNamedPipeHandleStateW
-//sys ntCreateNamedPipeFile(pipe *windows.Handle, access ntAccessMask, oa *objectAttributes, iosb *ioStatusBlock, share ntFileShareMode, disposition ntFileCreationDisposition, options ntFileOptions, typ uint32, readMode uint32, completionMode uint32, maxInstances uint32, inboundQuota uint32, outputQuota uint32, timeout *int64) (status ntStatus) = ntdll.NtCreateNamedPipeFile
-//sys rtlNtStatusToDosError(status ntStatus) (winerr error) = ntdll.RtlNtStatusToDosErrorNoTeb
-//sys rtlDosPathNameToNtPathName(name *uint16, ntName *unicodeString, filePart uintptr, reserved uintptr) (status ntStatus) = ntdll.RtlDosPathNameToNtPathName_U
-//sys rtlDefaultNpAcl(dacl *uintptr) (status ntStatus) = ntdll.RtlDefaultNpAcl
-
-type PipeConn interface {
- net.Conn
- Disconnect() error
- Flush() error
-}
-
-// type aliases for mkwinsyscall code
-type (
- ntAccessMask = fs.AccessMask
- ntFileShareMode = fs.FileShareMode
- ntFileCreationDisposition = fs.NTFileCreationDisposition
- ntFileOptions = fs.NTCreateOptions
-)
-
-type ioStatusBlock struct {
- Status, Information uintptr
-}
-
-// typedef struct _OBJECT_ATTRIBUTES {
-// ULONG Length;
-// HANDLE RootDirectory;
-// PUNICODE_STRING ObjectName;
-// ULONG Attributes;
-// PVOID SecurityDescriptor;
-// PVOID SecurityQualityOfService;
-// } OBJECT_ATTRIBUTES;
-//
-// https://learn.microsoft.com/en-us/windows/win32/api/ntdef/ns-ntdef-_object_attributes
-type objectAttributes struct {
- Length uintptr
- RootDirectory uintptr
- ObjectName *unicodeString
- Attributes uintptr
- SecurityDescriptor *securityDescriptor
- SecurityQoS uintptr
-}
-
-type unicodeString struct {
- Length uint16
- MaximumLength uint16
- Buffer uintptr
-}
-
-// typedef struct _SECURITY_DESCRIPTOR {
-// BYTE Revision;
-// BYTE Sbz1;
-// SECURITY_DESCRIPTOR_CONTROL Control;
-// PSID Owner;
-// PSID Group;
-// PACL Sacl;
-// PACL Dacl;
-// } SECURITY_DESCRIPTOR, *PISECURITY_DESCRIPTOR;
-//
-// https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-security_descriptor
-type securityDescriptor struct {
- Revision byte
- Sbz1 byte
- Control uint16
- Owner uintptr
- Group uintptr
- Sacl uintptr //revive:disable-line:var-naming SACL, not Sacl
- Dacl uintptr //revive:disable-line:var-naming DACL, not Dacl
-}
-
-type ntStatus int32
-
-func (status ntStatus) Err() error {
- if status >= 0 {
- return nil
- }
- return rtlNtStatusToDosError(status)
-}
-
-var (
- // ErrPipeListenerClosed is returned for pipe operations on listeners that have been closed.
- ErrPipeListenerClosed = net.ErrClosed
-
- errPipeWriteClosed = errors.New("pipe has been closed for write")
-)
-
-type win32Pipe struct {
- *win32File
- path string
-}
-
-var _ PipeConn = (*win32Pipe)(nil)
-
-type win32MessageBytePipe struct {
- win32Pipe
- writeClosed bool
- readEOF bool
-}
-
-type pipeAddress string
-
-func (f *win32Pipe) LocalAddr() net.Addr {
- return pipeAddress(f.path)
-}
-
-func (f *win32Pipe) RemoteAddr() net.Addr {
- return pipeAddress(f.path)
-}
-
-func (f *win32Pipe) SetDeadline(t time.Time) error {
- if err := f.SetReadDeadline(t); err != nil {
- return err
- }
- return f.SetWriteDeadline(t)
-}
-
-func (f *win32Pipe) Disconnect() error {
- return disconnectNamedPipe(f.win32File.handle)
-}
-
-// CloseWrite closes the write side of a message pipe in byte mode.
-func (f *win32MessageBytePipe) CloseWrite() error {
- if f.writeClosed {
- return errPipeWriteClosed
- }
- err := f.win32File.Flush()
- if err != nil {
- return err
- }
- _, err = f.win32File.Write(nil)
- if err != nil {
- return err
- }
- f.writeClosed = true
- return nil
-}
-
-// Write writes bytes to a message pipe in byte mode. Zero-byte writes are ignored, since
-// they are used to implement CloseWrite().
-func (f *win32MessageBytePipe) Write(b []byte) (int, error) {
- if f.writeClosed {
- return 0, errPipeWriteClosed
- }
- if len(b) == 0 {
- return 0, nil
- }
- return f.win32File.Write(b)
-}
-
-// Read reads bytes from a message pipe in byte mode. A read of a zero-byte message on a message
-// mode pipe will return io.EOF, as will all subsequent reads.
-func (f *win32MessageBytePipe) Read(b []byte) (int, error) {
- if f.readEOF {
- return 0, io.EOF
- }
- n, err := f.win32File.Read(b)
- if err == io.EOF { //nolint:errorlint
- // If this was the result of a zero-byte read, then
- // it is possible that the read was due to a zero-size
- // message. Since we are simulating CloseWrite with a
- // zero-byte message, ensure that all future Read() calls
- // also return EOF.
- f.readEOF = true
- } else if err == windows.ERROR_MORE_DATA { //nolint:errorlint // err is Errno
- // ERROR_MORE_DATA indicates that the pipe's read mode is message mode
- // and the message still has more bytes. Treat this as a success, since
- // this package presents all named pipes as byte streams.
- err = nil
- }
- return n, err
-}
-
-func (pipeAddress) Network() string {
- return "pipe"
-}
-
-func (s pipeAddress) String() string {
- return string(s)
-}
-
-// tryDialPipe attempts to dial the pipe at `path` until `ctx` cancellation or timeout.
-func tryDialPipe(ctx context.Context, path *string, access fs.AccessMask, impLevel PipeImpLevel) (windows.Handle, error) {
- for {
- select {
- case <-ctx.Done():
- return windows.Handle(0), ctx.Err()
- default:
- h, err := fs.CreateFile(*path,
- access,
- 0, // mode
- nil, // security attributes
- fs.OPEN_EXISTING,
- fs.FILE_FLAG_OVERLAPPED|fs.SECURITY_SQOS_PRESENT|fs.FileSQSFlag(impLevel),
- 0, // template file handle
- )
- if err == nil {
- return h, nil
- }
- if err != windows.ERROR_PIPE_BUSY { //nolint:errorlint // err is Errno
- return h, &os.PathError{Err: err, Op: "open", Path: *path}
- }
- // Wait 10 msec and try again. This is a rather simplistic
- // view, as we always try each 10 milliseconds.
- time.Sleep(10 * time.Millisecond)
- }
- }
-}
-
-// DialPipe connects to a named pipe by path, timing out if the connection
-// takes longer than the specified duration. If timeout is nil, then we use
-// a default timeout of 2 seconds. (We do not use WaitNamedPipe.)
-func DialPipe(path string, timeout *time.Duration) (net.Conn, error) {
- var absTimeout time.Time
- if timeout != nil {
- absTimeout = time.Now().Add(*timeout)
- } else {
- absTimeout = time.Now().Add(2 * time.Second)
- }
- ctx, cancel := context.WithDeadline(context.Background(), absTimeout)
- defer cancel()
- conn, err := DialPipeContext(ctx, path)
- if errors.Is(err, context.DeadlineExceeded) {
- return nil, ErrTimeout
- }
- return conn, err
-}
-
-// DialPipeContext attempts to connect to a named pipe by `path` until `ctx`
-// cancellation or timeout.
-func DialPipeContext(ctx context.Context, path string) (net.Conn, error) {
- return DialPipeAccess(ctx, path, uint32(fs.GENERIC_READ|fs.GENERIC_WRITE))
-}
-
-// PipeImpLevel is an enumeration of impersonation levels that may be set
-// when calling DialPipeAccessImpersonation.
-type PipeImpLevel uint32
-
-const (
- PipeImpLevelAnonymous = PipeImpLevel(fs.SECURITY_ANONYMOUS)
- PipeImpLevelIdentification = PipeImpLevel(fs.SECURITY_IDENTIFICATION)
- PipeImpLevelImpersonation = PipeImpLevel(fs.SECURITY_IMPERSONATION)
- PipeImpLevelDelegation = PipeImpLevel(fs.SECURITY_DELEGATION)
-)
-
-// DialPipeAccess attempts to connect to a named pipe by `path` with `access` until `ctx`
-// cancellation or timeout.
-func DialPipeAccess(ctx context.Context, path string, access uint32) (net.Conn, error) {
- return DialPipeAccessImpLevel(ctx, path, access, PipeImpLevelAnonymous)
-}
-
-// DialPipeAccessImpLevel attempts to connect to a named pipe by `path` with
-// `access` at `impLevel` until `ctx` cancellation or timeout. The other
-// DialPipe* implementations use PipeImpLevelAnonymous.
-func DialPipeAccessImpLevel(ctx context.Context, path string, access uint32, impLevel PipeImpLevel) (net.Conn, error) {
- var err error
- var h windows.Handle
- h, err = tryDialPipe(ctx, &path, fs.AccessMask(access), impLevel)
- if err != nil {
- return nil, err
- }
-
- var flags uint32
- err = getNamedPipeInfo(h, &flags, nil, nil, nil)
- if err != nil {
- return nil, err
- }
-
- f, err := makeWin32File(h)
- if err != nil {
- windows.Close(h)
- return nil, err
- }
-
- // If the pipe is in message mode, return a message byte pipe, which
- // supports CloseWrite().
- if flags&windows.PIPE_TYPE_MESSAGE != 0 {
- return &win32MessageBytePipe{
- win32Pipe: win32Pipe{win32File: f, path: path},
- }, nil
- }
- return &win32Pipe{win32File: f, path: path}, nil
-}
-
-type acceptResponse struct {
- f *win32File
- err error
-}
-
-type win32PipeListener struct {
- firstHandle windows.Handle
- path string
- config PipeConfig
- acceptCh chan (chan acceptResponse)
- closeCh chan int
- doneCh chan int
-}
-
-func makeServerPipeHandle(path string, sd []byte, c *PipeConfig, first bool) (windows.Handle, error) {
- path16, err := windows.UTF16FromString(path)
- if err != nil {
- return 0, &os.PathError{Op: "open", Path: path, Err: err}
- }
-
- var oa objectAttributes
- oa.Length = unsafe.Sizeof(oa)
-
- var ntPath unicodeString
- if err := rtlDosPathNameToNtPathName(&path16[0],
- &ntPath,
- 0,
- 0,
- ).Err(); err != nil {
- return 0, &os.PathError{Op: "open", Path: path, Err: err}
- }
- defer windows.LocalFree(windows.Handle(ntPath.Buffer)) //nolint:errcheck
- oa.ObjectName = &ntPath
- oa.Attributes = windows.OBJ_CASE_INSENSITIVE
-
- // The security descriptor is only needed for the first pipe.
- if first {
- if sd != nil {
- //todo: does `sdb` need to be allocated on the heap, or can go allocate it?
- l := uint32(len(sd))
- sdb, err := windows.LocalAlloc(0, l)
- if err != nil {
- return 0, fmt.Errorf("LocalAlloc for security descriptor with of length %d: %w", l, err)
- }
- defer windows.LocalFree(windows.Handle(sdb)) //nolint:errcheck
- copy((*[0xffff]byte)(unsafe.Pointer(sdb))[:], sd)
- oa.SecurityDescriptor = (*securityDescriptor)(unsafe.Pointer(sdb))
- } else {
- // Construct the default named pipe security descriptor.
- var dacl uintptr
- if err := rtlDefaultNpAcl(&dacl).Err(); err != nil {
- return 0, fmt.Errorf("getting default named pipe ACL: %w", err)
- }
- defer windows.LocalFree(windows.Handle(dacl)) //nolint:errcheck
-
- sdb := &securityDescriptor{
- Revision: 1,
- Control: windows.SE_DACL_PRESENT,
- Dacl: dacl,
- }
- oa.SecurityDescriptor = sdb
- }
- }
-
- typ := uint32(windows.FILE_PIPE_REJECT_REMOTE_CLIENTS)
- if c.MessageMode {
- typ |= windows.FILE_PIPE_MESSAGE_TYPE
- }
-
- disposition := fs.FILE_OPEN
- access := fs.GENERIC_READ | fs.GENERIC_WRITE | fs.SYNCHRONIZE
- if first {
- disposition = fs.FILE_CREATE
- // By not asking for read or write access, the named pipe file system
- // will put this pipe into an initially disconnected state, blocking
- // client connections until the next call with first == false.
- access = fs.SYNCHRONIZE
- }
-
- timeout := int64(-50 * 10000) // 50ms
-
- var (
- h windows.Handle
- iosb ioStatusBlock
- )
- err = ntCreateNamedPipeFile(&h,
- access,
- &oa,
- &iosb,
- fs.FILE_SHARE_READ|fs.FILE_SHARE_WRITE,
- disposition,
- 0,
- typ,
- 0,
- 0,
- 0xffffffff,
- uint32(c.InputBufferSize),
- uint32(c.OutputBufferSize),
- &timeout).Err()
- if err != nil {
- return 0, &os.PathError{Op: "open", Path: path, Err: err}
- }
-
- runtime.KeepAlive(ntPath)
- return h, nil
-}
-
-func (l *win32PipeListener) makeServerPipe() (*win32File, error) {
- h, err := makeServerPipeHandle(l.path, nil, &l.config, false)
- if err != nil {
- return nil, err
- }
- f, err := makeWin32File(h)
- if err != nil {
- windows.Close(h)
- return nil, err
- }
- return f, nil
-}
-
-func (l *win32PipeListener) makeConnectedServerPipe() (*win32File, error) {
- p, err := l.makeServerPipe()
- if err != nil {
- return nil, err
- }
-
- // Wait for the client to connect.
- ch := make(chan error)
- go func(p *win32File) {
- ch <- connectPipe(p)
- }(p)
-
- select {
- case err = <-ch:
- if err != nil {
- p.Close()
- p = nil
- }
- case <-l.closeCh:
- // Abort the connect request by closing the handle.
- p.Close()
- p = nil
- err = <-ch
- if err == nil || err == ErrFileClosed { //nolint:errorlint // err is Errno
- err = ErrPipeListenerClosed
- }
- }
- return p, err
-}
-
-func (l *win32PipeListener) listenerRoutine() {
- closed := false
- for !closed {
- select {
- case <-l.closeCh:
- closed = true
- case responseCh := <-l.acceptCh:
- var (
- p *win32File
- err error
- )
- for {
- p, err = l.makeConnectedServerPipe()
- // If the connection was immediately closed by the client, try
- // again.
- if err != windows.ERROR_NO_DATA { //nolint:errorlint // err is Errno
- break
- }
- }
- responseCh <- acceptResponse{p, err}
- closed = err == ErrPipeListenerClosed //nolint:errorlint // err is Errno
- }
- }
- windows.Close(l.firstHandle)
- l.firstHandle = 0
- // Notify Close() and Accept() callers that the handle has been closed.
- close(l.doneCh)
-}
-
-// PipeConfig contain configuration for the pipe listener.
-type PipeConfig struct {
- // SecurityDescriptor contains a Windows security descriptor in SDDL format.
- SecurityDescriptor string
-
- // MessageMode determines whether the pipe is in byte or message mode. In either
- // case the pipe is read in byte mode by default. The only practical difference in
- // this implementation is that CloseWrite() is only supported for message mode pipes;
- // CloseWrite() is implemented as a zero-byte write, but zero-byte writes are only
- // transferred to the reader (and returned as io.EOF in this implementation)
- // when the pipe is in message mode.
- MessageMode bool
-
- // InputBufferSize specifies the size of the input buffer, in bytes.
- InputBufferSize int32
-
- // OutputBufferSize specifies the size of the output buffer, in bytes.
- OutputBufferSize int32
-}
-
-// ListenPipe creates a listener on a Windows named pipe path, e.g. \\.\pipe\mypipe.
-// The pipe must not already exist.
-func ListenPipe(path string, c *PipeConfig) (net.Listener, error) {
- var (
- sd []byte
- err error
- )
- if c == nil {
- c = &PipeConfig{}
- }
- if c.SecurityDescriptor != "" {
- sd, err = SddlToSecurityDescriptor(c.SecurityDescriptor)
- if err != nil {
- return nil, err
- }
- }
- h, err := makeServerPipeHandle(path, sd, c, true)
- if err != nil {
- return nil, err
- }
- l := &win32PipeListener{
- firstHandle: h,
- path: path,
- config: *c,
- acceptCh: make(chan (chan acceptResponse)),
- closeCh: make(chan int),
- doneCh: make(chan int),
- }
- go l.listenerRoutine()
- return l, nil
-}
-
-func connectPipe(p *win32File) error {
- c, err := p.prepareIO()
- if err != nil {
- return err
- }
- defer p.wg.Done()
-
- err = connectNamedPipe(p.handle, &c.o)
- _, err = p.asyncIO(c, nil, 0, err)
- if err != nil && err != windows.ERROR_PIPE_CONNECTED { //nolint:errorlint // err is Errno
- return err
- }
- return nil
-}
-
-func (l *win32PipeListener) Accept() (net.Conn, error) {
- ch := make(chan acceptResponse)
- select {
- case l.acceptCh <- ch:
- response := <-ch
- err := response.err
- if err != nil {
- return nil, err
- }
- if l.config.MessageMode {
- return &win32MessageBytePipe{
- win32Pipe: win32Pipe{win32File: response.f, path: l.path},
- }, nil
- }
- return &win32Pipe{win32File: response.f, path: l.path}, nil
- case <-l.doneCh:
- return nil, ErrPipeListenerClosed
- }
-}
-
-func (l *win32PipeListener) Close() error {
- select {
- case l.closeCh <- 1:
- <-l.doneCh
- case <-l.doneCh:
- }
- return nil
-}
-
-func (l *win32PipeListener) Addr() net.Addr {
- return pipeAddress(l.path)
-}
diff --git a/vendor/github.com/Microsoft/go-winio/pkg/guid/guid.go b/vendor/github.com/Microsoft/go-winio/pkg/guid/guid.go
deleted file mode 100644
index 48ce4e92..00000000
--- a/vendor/github.com/Microsoft/go-winio/pkg/guid/guid.go
+++ /dev/null
@@ -1,232 +0,0 @@
-// Package guid provides a GUID type. The backing structure for a GUID is
-// identical to that used by the golang.org/x/sys/windows GUID type.
-// There are two main binary encodings used for a GUID, the big-endian encoding,
-// and the Windows (mixed-endian) encoding. See here for details:
-// https://en.wikipedia.org/wiki/Universally_unique_identifier#Encoding
-package guid
-
-import (
- "crypto/rand"
- "crypto/sha1" //nolint:gosec // not used for secure application
- "encoding"
- "encoding/binary"
- "fmt"
- "strconv"
-)
-
-//go:generate go run golang.org/x/tools/cmd/stringer -type=Variant -trimprefix=Variant -linecomment
-
-// Variant specifies which GUID variant (or "type") of the GUID. It determines
-// how the entirety of the rest of the GUID is interpreted.
-type Variant uint8
-
-// The variants specified by RFC 4122 section 4.1.1.
-const (
- // VariantUnknown specifies a GUID variant which does not conform to one of
- // the variant encodings specified in RFC 4122.
- VariantUnknown Variant = iota
- VariantNCS
- VariantRFC4122 // RFC 4122
- VariantMicrosoft
- VariantFuture
-)
-
-// Version specifies how the bits in the GUID were generated. For instance, a
-// version 4 GUID is randomly generated, and a version 5 is generated from the
-// hash of an input string.
-type Version uint8
-
-func (v Version) String() string {
- return strconv.FormatUint(uint64(v), 10)
-}
-
-var _ = (encoding.TextMarshaler)(GUID{})
-var _ = (encoding.TextUnmarshaler)(&GUID{})
-
-// NewV4 returns a new version 4 (pseudorandom) GUID, as defined by RFC 4122.
-func NewV4() (GUID, error) {
- var b [16]byte
- if _, err := rand.Read(b[:]); err != nil {
- return GUID{}, err
- }
-
- g := FromArray(b)
- g.setVersion(4) // Version 4 means randomly generated.
- g.setVariant(VariantRFC4122)
-
- return g, nil
-}
-
-// NewV5 returns a new version 5 (generated from a string via SHA-1 hashing)
-// GUID, as defined by RFC 4122. The RFC is unclear on the encoding of the name,
-// and the sample code treats it as a series of bytes, so we do the same here.
-//
-// Some implementations, such as those found on Windows, treat the name as a
-// big-endian UTF16 stream of bytes. If that is desired, the string can be
-// encoded as such before being passed to this function.
-func NewV5(namespace GUID, name []byte) (GUID, error) {
- b := sha1.New() //nolint:gosec // not used for secure application
- namespaceBytes := namespace.ToArray()
- b.Write(namespaceBytes[:])
- b.Write(name)
-
- a := [16]byte{}
- copy(a[:], b.Sum(nil))
-
- g := FromArray(a)
- g.setVersion(5) // Version 5 means generated from a string.
- g.setVariant(VariantRFC4122)
-
- return g, nil
-}
-
-func fromArray(b [16]byte, order binary.ByteOrder) GUID {
- var g GUID
- g.Data1 = order.Uint32(b[0:4])
- g.Data2 = order.Uint16(b[4:6])
- g.Data3 = order.Uint16(b[6:8])
- copy(g.Data4[:], b[8:16])
- return g
-}
-
-func (g GUID) toArray(order binary.ByteOrder) [16]byte {
- b := [16]byte{}
- order.PutUint32(b[0:4], g.Data1)
- order.PutUint16(b[4:6], g.Data2)
- order.PutUint16(b[6:8], g.Data3)
- copy(b[8:16], g.Data4[:])
- return b
-}
-
-// FromArray constructs a GUID from a big-endian encoding array of 16 bytes.
-func FromArray(b [16]byte) GUID {
- return fromArray(b, binary.BigEndian)
-}
-
-// ToArray returns an array of 16 bytes representing the GUID in big-endian
-// encoding.
-func (g GUID) ToArray() [16]byte {
- return g.toArray(binary.BigEndian)
-}
-
-// FromWindowsArray constructs a GUID from a Windows encoding array of bytes.
-func FromWindowsArray(b [16]byte) GUID {
- return fromArray(b, binary.LittleEndian)
-}
-
-// ToWindowsArray returns an array of 16 bytes representing the GUID in Windows
-// encoding.
-func (g GUID) ToWindowsArray() [16]byte {
- return g.toArray(binary.LittleEndian)
-}
-
-func (g GUID) String() string {
- return fmt.Sprintf(
- "%08x-%04x-%04x-%04x-%012x",
- g.Data1,
- g.Data2,
- g.Data3,
- g.Data4[:2],
- g.Data4[2:])
-}
-
-// FromString parses a string containing a GUID and returns the GUID. The only
-// format currently supported is the `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`
-// format.
-func FromString(s string) (GUID, error) {
- if len(s) != 36 {
- return GUID{}, fmt.Errorf("invalid GUID %q", s)
- }
- if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' {
- return GUID{}, fmt.Errorf("invalid GUID %q", s)
- }
-
- var g GUID
-
- data1, err := strconv.ParseUint(s[0:8], 16, 32)
- if err != nil {
- return GUID{}, fmt.Errorf("invalid GUID %q", s)
- }
- g.Data1 = uint32(data1)
-
- data2, err := strconv.ParseUint(s[9:13], 16, 16)
- if err != nil {
- return GUID{}, fmt.Errorf("invalid GUID %q", s)
- }
- g.Data2 = uint16(data2)
-
- data3, err := strconv.ParseUint(s[14:18], 16, 16)
- if err != nil {
- return GUID{}, fmt.Errorf("invalid GUID %q", s)
- }
- g.Data3 = uint16(data3)
-
- for i, x := range []int{19, 21, 24, 26, 28, 30, 32, 34} {
- v, err := strconv.ParseUint(s[x:x+2], 16, 8)
- if err != nil {
- return GUID{}, fmt.Errorf("invalid GUID %q", s)
- }
- g.Data4[i] = uint8(v)
- }
-
- return g, nil
-}
-
-func (g *GUID) setVariant(v Variant) {
- d := g.Data4[0]
- switch v {
- case VariantNCS:
- d = (d & 0x7f)
- case VariantRFC4122:
- d = (d & 0x3f) | 0x80
- case VariantMicrosoft:
- d = (d & 0x1f) | 0xc0
- case VariantFuture:
- d = (d & 0x0f) | 0xe0
- case VariantUnknown:
- fallthrough
- default:
- panic(fmt.Sprintf("invalid variant: %d", v))
- }
- g.Data4[0] = d
-}
-
-// Variant returns the GUID variant, as defined in RFC 4122.
-func (g GUID) Variant() Variant {
- b := g.Data4[0]
- if b&0x80 == 0 {
- return VariantNCS
- } else if b&0xc0 == 0x80 {
- return VariantRFC4122
- } else if b&0xe0 == 0xc0 {
- return VariantMicrosoft
- } else if b&0xe0 == 0xe0 {
- return VariantFuture
- }
- return VariantUnknown
-}
-
-func (g *GUID) setVersion(v Version) {
- g.Data3 = (g.Data3 & 0x0fff) | (uint16(v) << 12)
-}
-
-// Version returns the GUID version, as defined in RFC 4122.
-func (g GUID) Version() Version {
- return Version((g.Data3 & 0xF000) >> 12)
-}
-
-// MarshalText returns the textual representation of the GUID.
-func (g GUID) MarshalText() ([]byte, error) {
- return []byte(g.String()), nil
-}
-
-// UnmarshalText takes the textual representation of a GUID, and unmarhals it
-// into this GUID.
-func (g *GUID) UnmarshalText(text []byte) error {
- g2, err := FromString(string(text))
- if err != nil {
- return err
- }
- *g = g2
- return nil
-}
diff --git a/vendor/github.com/Microsoft/go-winio/pkg/guid/guid_nonwindows.go b/vendor/github.com/Microsoft/go-winio/pkg/guid/guid_nonwindows.go
deleted file mode 100644
index 805bd354..00000000
--- a/vendor/github.com/Microsoft/go-winio/pkg/guid/guid_nonwindows.go
+++ /dev/null
@@ -1,16 +0,0 @@
-//go:build !windows
-// +build !windows
-
-package guid
-
-// GUID represents a GUID/UUID. It has the same structure as
-// golang.org/x/sys/windows.GUID so that it can be used with functions expecting
-// that type. It is defined as its own type as that is only available to builds
-// targeted at `windows`. The representation matches that used by native Windows
-// code.
-type GUID struct {
- Data1 uint32
- Data2 uint16
- Data3 uint16
- Data4 [8]byte
-}
diff --git a/vendor/github.com/Microsoft/go-winio/pkg/guid/guid_windows.go b/vendor/github.com/Microsoft/go-winio/pkg/guid/guid_windows.go
deleted file mode 100644
index 27e45ee5..00000000
--- a/vendor/github.com/Microsoft/go-winio/pkg/guid/guid_windows.go
+++ /dev/null
@@ -1,13 +0,0 @@
-//go:build windows
-// +build windows
-
-package guid
-
-import "golang.org/x/sys/windows"
-
-// GUID represents a GUID/UUID. It has the same structure as
-// golang.org/x/sys/windows.GUID so that it can be used with functions expecting
-// that type. It is defined as its own type so that stringification and
-// marshaling can be supported. The representation matches that used by native
-// Windows code.
-type GUID windows.GUID
diff --git a/vendor/github.com/Microsoft/go-winio/pkg/guid/variant_string.go b/vendor/github.com/Microsoft/go-winio/pkg/guid/variant_string.go
deleted file mode 100644
index 4076d313..00000000
--- a/vendor/github.com/Microsoft/go-winio/pkg/guid/variant_string.go
+++ /dev/null
@@ -1,27 +0,0 @@
-// Code generated by "stringer -type=Variant -trimprefix=Variant -linecomment"; DO NOT EDIT.
-
-package guid
-
-import "strconv"
-
-func _() {
- // An "invalid array index" compiler error signifies that the constant values have changed.
- // Re-run the stringer command to generate them again.
- var x [1]struct{}
- _ = x[VariantUnknown-0]
- _ = x[VariantNCS-1]
- _ = x[VariantRFC4122-2]
- _ = x[VariantMicrosoft-3]
- _ = x[VariantFuture-4]
-}
-
-const _Variant_name = "UnknownNCSRFC 4122MicrosoftFuture"
-
-var _Variant_index = [...]uint8{0, 7, 10, 18, 27, 33}
-
-func (i Variant) String() string {
- if i >= Variant(len(_Variant_index)-1) {
- return "Variant(" + strconv.FormatInt(int64(i), 10) + ")"
- }
- return _Variant_name[_Variant_index[i]:_Variant_index[i+1]]
-}
diff --git a/vendor/github.com/Microsoft/go-winio/privilege.go b/vendor/github.com/Microsoft/go-winio/privilege.go
deleted file mode 100644
index d9b90b6e..00000000
--- a/vendor/github.com/Microsoft/go-winio/privilege.go
+++ /dev/null
@@ -1,196 +0,0 @@
-//go:build windows
-// +build windows
-
-package winio
-
-import (
- "bytes"
- "encoding/binary"
- "fmt"
- "runtime"
- "sync"
- "unicode/utf16"
-
- "golang.org/x/sys/windows"
-)
-
-//sys adjustTokenPrivileges(token windows.Token, releaseAll bool, input *byte, outputSize uint32, output *byte, requiredSize *uint32) (success bool, err error) [true] = advapi32.AdjustTokenPrivileges
-//sys impersonateSelf(level uint32) (err error) = advapi32.ImpersonateSelf
-//sys revertToSelf() (err error) = advapi32.RevertToSelf
-//sys openThreadToken(thread windows.Handle, accessMask uint32, openAsSelf bool, token *windows.Token) (err error) = advapi32.OpenThreadToken
-//sys getCurrentThread() (h windows.Handle) = GetCurrentThread
-//sys lookupPrivilegeValue(systemName string, name string, luid *uint64) (err error) = advapi32.LookupPrivilegeValueW
-//sys lookupPrivilegeName(systemName string, luid *uint64, buffer *uint16, size *uint32) (err error) = advapi32.LookupPrivilegeNameW
-//sys lookupPrivilegeDisplayName(systemName string, name *uint16, buffer *uint16, size *uint32, languageId *uint32) (err error) = advapi32.LookupPrivilegeDisplayNameW
-
-const (
- //revive:disable-next-line:var-naming ALL_CAPS
- SE_PRIVILEGE_ENABLED = windows.SE_PRIVILEGE_ENABLED
-
- //revive:disable-next-line:var-naming ALL_CAPS
- ERROR_NOT_ALL_ASSIGNED windows.Errno = windows.ERROR_NOT_ALL_ASSIGNED
-
- SeBackupPrivilege = "SeBackupPrivilege"
- SeRestorePrivilege = "SeRestorePrivilege"
- SeSecurityPrivilege = "SeSecurityPrivilege"
-)
-
-var (
- privNames = make(map[string]uint64)
- privNameMutex sync.Mutex
-)
-
-// PrivilegeError represents an error enabling privileges.
-type PrivilegeError struct {
- privileges []uint64
-}
-
-func (e *PrivilegeError) Error() string {
- s := "Could not enable privilege "
- if len(e.privileges) > 1 {
- s = "Could not enable privileges "
- }
- for i, p := range e.privileges {
- if i != 0 {
- s += ", "
- }
- s += `"`
- s += getPrivilegeName(p)
- s += `"`
- }
- return s
-}
-
-// RunWithPrivilege enables a single privilege for a function call.
-func RunWithPrivilege(name string, fn func() error) error {
- return RunWithPrivileges([]string{name}, fn)
-}
-
-// RunWithPrivileges enables privileges for a function call.
-func RunWithPrivileges(names []string, fn func() error) error {
- privileges, err := mapPrivileges(names)
- if err != nil {
- return err
- }
- runtime.LockOSThread()
- defer runtime.UnlockOSThread()
- token, err := newThreadToken()
- if err != nil {
- return err
- }
- defer releaseThreadToken(token)
- err = adjustPrivileges(token, privileges, SE_PRIVILEGE_ENABLED)
- if err != nil {
- return err
- }
- return fn()
-}
-
-func mapPrivileges(names []string) ([]uint64, error) {
- privileges := make([]uint64, 0, len(names))
- privNameMutex.Lock()
- defer privNameMutex.Unlock()
- for _, name := range names {
- p, ok := privNames[name]
- if !ok {
- err := lookupPrivilegeValue("", name, &p)
- if err != nil {
- return nil, err
- }
- privNames[name] = p
- }
- privileges = append(privileges, p)
- }
- return privileges, nil
-}
-
-// EnableProcessPrivileges enables privileges globally for the process.
-func EnableProcessPrivileges(names []string) error {
- return enableDisableProcessPrivilege(names, SE_PRIVILEGE_ENABLED)
-}
-
-// DisableProcessPrivileges disables privileges globally for the process.
-func DisableProcessPrivileges(names []string) error {
- return enableDisableProcessPrivilege(names, 0)
-}
-
-func enableDisableProcessPrivilege(names []string, action uint32) error {
- privileges, err := mapPrivileges(names)
- if err != nil {
- return err
- }
-
- p := windows.CurrentProcess()
- var token windows.Token
- err = windows.OpenProcessToken(p, windows.TOKEN_ADJUST_PRIVILEGES|windows.TOKEN_QUERY, &token)
- if err != nil {
- return err
- }
-
- defer token.Close()
- return adjustPrivileges(token, privileges, action)
-}
-
-func adjustPrivileges(token windows.Token, privileges []uint64, action uint32) error {
- var b bytes.Buffer
- _ = binary.Write(&b, binary.LittleEndian, uint32(len(privileges)))
- for _, p := range privileges {
- _ = binary.Write(&b, binary.LittleEndian, p)
- _ = binary.Write(&b, binary.LittleEndian, action)
- }
- prevState := make([]byte, b.Len())
- reqSize := uint32(0)
- success, err := adjustTokenPrivileges(token, false, &b.Bytes()[0], uint32(len(prevState)), &prevState[0], &reqSize)
- if !success {
- return err
- }
- if err == ERROR_NOT_ALL_ASSIGNED { //nolint:errorlint // err is Errno
- return &PrivilegeError{privileges}
- }
- return nil
-}
-
-func getPrivilegeName(luid uint64) string {
- var nameBuffer [256]uint16
- bufSize := uint32(len(nameBuffer))
- err := lookupPrivilegeName("", &luid, &nameBuffer[0], &bufSize)
- if err != nil {
- return fmt.Sprintf("", luid)
- }
-
- var displayNameBuffer [256]uint16
- displayBufSize := uint32(len(displayNameBuffer))
- var langID uint32
- err = lookupPrivilegeDisplayName("", &nameBuffer[0], &displayNameBuffer[0], &displayBufSize, &langID)
- if err != nil {
- return fmt.Sprintf("", string(utf16.Decode(nameBuffer[:bufSize])))
- }
-
- return string(utf16.Decode(displayNameBuffer[:displayBufSize]))
-}
-
-func newThreadToken() (windows.Token, error) {
- err := impersonateSelf(windows.SecurityImpersonation)
- if err != nil {
- return 0, err
- }
-
- var token windows.Token
- err = openThreadToken(getCurrentThread(), windows.TOKEN_ADJUST_PRIVILEGES|windows.TOKEN_QUERY, false, &token)
- if err != nil {
- rerr := revertToSelf()
- if rerr != nil {
- panic(rerr)
- }
- return 0, err
- }
- return token, nil
-}
-
-func releaseThreadToken(h windows.Token) {
- err := revertToSelf()
- if err != nil {
- panic(err)
- }
- h.Close()
-}
diff --git a/vendor/github.com/Microsoft/go-winio/reparse.go b/vendor/github.com/Microsoft/go-winio/reparse.go
deleted file mode 100644
index 67d1a104..00000000
--- a/vendor/github.com/Microsoft/go-winio/reparse.go
+++ /dev/null
@@ -1,131 +0,0 @@
-//go:build windows
-// +build windows
-
-package winio
-
-import (
- "bytes"
- "encoding/binary"
- "fmt"
- "strings"
- "unicode/utf16"
- "unsafe"
-)
-
-const (
- reparseTagMountPoint = 0xA0000003
- reparseTagSymlink = 0xA000000C
-)
-
-type reparseDataBuffer struct {
- ReparseTag uint32
- ReparseDataLength uint16
- Reserved uint16
- SubstituteNameOffset uint16
- SubstituteNameLength uint16
- PrintNameOffset uint16
- PrintNameLength uint16
-}
-
-// ReparsePoint describes a Win32 symlink or mount point.
-type ReparsePoint struct {
- Target string
- IsMountPoint bool
-}
-
-// UnsupportedReparsePointError is returned when trying to decode a non-symlink or
-// mount point reparse point.
-type UnsupportedReparsePointError struct {
- Tag uint32
-}
-
-func (e *UnsupportedReparsePointError) Error() string {
- return fmt.Sprintf("unsupported reparse point %x", e.Tag)
-}
-
-// DecodeReparsePoint decodes a Win32 REPARSE_DATA_BUFFER structure containing either a symlink
-// or a mount point.
-func DecodeReparsePoint(b []byte) (*ReparsePoint, error) {
- tag := binary.LittleEndian.Uint32(b[0:4])
- return DecodeReparsePointData(tag, b[8:])
-}
-
-func DecodeReparsePointData(tag uint32, b []byte) (*ReparsePoint, error) {
- isMountPoint := false
- switch tag {
- case reparseTagMountPoint:
- isMountPoint = true
- case reparseTagSymlink:
- default:
- return nil, &UnsupportedReparsePointError{tag}
- }
- nameOffset := 8 + binary.LittleEndian.Uint16(b[4:6])
- if !isMountPoint {
- nameOffset += 4
- }
- nameLength := binary.LittleEndian.Uint16(b[6:8])
- name := make([]uint16, nameLength/2)
- err := binary.Read(bytes.NewReader(b[nameOffset:nameOffset+nameLength]), binary.LittleEndian, &name)
- if err != nil {
- return nil, err
- }
- return &ReparsePoint{string(utf16.Decode(name)), isMountPoint}, nil
-}
-
-func isDriveLetter(c byte) bool {
- return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
-}
-
-// EncodeReparsePoint encodes a Win32 REPARSE_DATA_BUFFER structure describing a symlink or
-// mount point.
-func EncodeReparsePoint(rp *ReparsePoint) []byte {
- // Generate an NT path and determine if this is a relative path.
- var ntTarget string
- relative := false
- if strings.HasPrefix(rp.Target, `\\?\`) {
- ntTarget = `\??\` + rp.Target[4:]
- } else if strings.HasPrefix(rp.Target, `\\`) {
- ntTarget = `\??\UNC\` + rp.Target[2:]
- } else if len(rp.Target) >= 2 && isDriveLetter(rp.Target[0]) && rp.Target[1] == ':' {
- ntTarget = `\??\` + rp.Target
- } else {
- ntTarget = rp.Target
- relative = true
- }
-
- // The paths must be NUL-terminated even though they are counted strings.
- target16 := utf16.Encode([]rune(rp.Target + "\x00"))
- ntTarget16 := utf16.Encode([]rune(ntTarget + "\x00"))
-
- size := int(unsafe.Sizeof(reparseDataBuffer{})) - 8
- size += len(ntTarget16)*2 + len(target16)*2
-
- tag := uint32(reparseTagMountPoint)
- if !rp.IsMountPoint {
- tag = reparseTagSymlink
- size += 4 // Add room for symlink flags
- }
-
- data := reparseDataBuffer{
- ReparseTag: tag,
- ReparseDataLength: uint16(size),
- SubstituteNameOffset: 0,
- SubstituteNameLength: uint16((len(ntTarget16) - 1) * 2),
- PrintNameOffset: uint16(len(ntTarget16) * 2),
- PrintNameLength: uint16((len(target16) - 1) * 2),
- }
-
- var b bytes.Buffer
- _ = binary.Write(&b, binary.LittleEndian, &data)
- if !rp.IsMountPoint {
- flags := uint32(0)
- if relative {
- flags |= 1
- }
- _ = binary.Write(&b, binary.LittleEndian, flags)
- }
-
- _ = binary.Write(&b, binary.LittleEndian, ntTarget16)
- _ = binary.Write(&b, binary.LittleEndian, target16)
- return b.Bytes()
-}
diff --git a/vendor/github.com/Microsoft/go-winio/sd.go b/vendor/github.com/Microsoft/go-winio/sd.go
deleted file mode 100644
index c3685e98..00000000
--- a/vendor/github.com/Microsoft/go-winio/sd.go
+++ /dev/null
@@ -1,133 +0,0 @@
-//go:build windows
-// +build windows
-
-package winio
-
-import (
- "errors"
- "fmt"
- "unsafe"
-
- "golang.org/x/sys/windows"
-)
-
-//sys lookupAccountName(systemName *uint16, accountName string, sid *byte, sidSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) = advapi32.LookupAccountNameW
-//sys lookupAccountSid(systemName *uint16, sid *byte, name *uint16, nameSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) = advapi32.LookupAccountSidW
-//sys convertSidToStringSid(sid *byte, str **uint16) (err error) = advapi32.ConvertSidToStringSidW
-//sys convertStringSidToSid(str *uint16, sid **byte) (err error) = advapi32.ConvertStringSidToSidW
-
-type AccountLookupError struct {
- Name string
- Err error
-}
-
-func (e *AccountLookupError) Error() string {
- if e.Name == "" {
- return "lookup account: empty account name specified"
- }
- var s string
- switch {
- case errors.Is(e.Err, windows.ERROR_INVALID_SID):
- s = "the security ID structure is invalid"
- case errors.Is(e.Err, windows.ERROR_NONE_MAPPED):
- s = "not found"
- default:
- s = e.Err.Error()
- }
- return "lookup account " + e.Name + ": " + s
-}
-
-func (e *AccountLookupError) Unwrap() error { return e.Err }
-
-type SddlConversionError struct {
- Sddl string
- Err error
-}
-
-func (e *SddlConversionError) Error() string {
- return "convert " + e.Sddl + ": " + e.Err.Error()
-}
-
-func (e *SddlConversionError) Unwrap() error { return e.Err }
-
-// LookupSidByName looks up the SID of an account by name
-//
-//revive:disable-next-line:var-naming SID, not Sid
-func LookupSidByName(name string) (sid string, err error) {
- if name == "" {
- return "", &AccountLookupError{name, windows.ERROR_NONE_MAPPED}
- }
-
- var sidSize, sidNameUse, refDomainSize uint32
- err = lookupAccountName(nil, name, nil, &sidSize, nil, &refDomainSize, &sidNameUse)
- if err != nil && err != windows.ERROR_INSUFFICIENT_BUFFER { //nolint:errorlint // err is Errno
- return "", &AccountLookupError{name, err}
- }
- sidBuffer := make([]byte, sidSize)
- refDomainBuffer := make([]uint16, refDomainSize)
- err = lookupAccountName(nil, name, &sidBuffer[0], &sidSize, &refDomainBuffer[0], &refDomainSize, &sidNameUse)
- if err != nil {
- return "", &AccountLookupError{name, err}
- }
- var strBuffer *uint16
- err = convertSidToStringSid(&sidBuffer[0], &strBuffer)
- if err != nil {
- return "", &AccountLookupError{name, err}
- }
- sid = windows.UTF16ToString((*[0xffff]uint16)(unsafe.Pointer(strBuffer))[:])
- _, _ = windows.LocalFree(windows.Handle(unsafe.Pointer(strBuffer)))
- return sid, nil
-}
-
-// LookupNameBySid looks up the name of an account by SID
-//
-//revive:disable-next-line:var-naming SID, not Sid
-func LookupNameBySid(sid string) (name string, err error) {
- if sid == "" {
- return "", &AccountLookupError{sid, windows.ERROR_NONE_MAPPED}
- }
-
- sidBuffer, err := windows.UTF16PtrFromString(sid)
- if err != nil {
- return "", &AccountLookupError{sid, err}
- }
-
- var sidPtr *byte
- if err = convertStringSidToSid(sidBuffer, &sidPtr); err != nil {
- return "", &AccountLookupError{sid, err}
- }
- defer windows.LocalFree(windows.Handle(unsafe.Pointer(sidPtr))) //nolint:errcheck
-
- var nameSize, refDomainSize, sidNameUse uint32
- err = lookupAccountSid(nil, sidPtr, nil, &nameSize, nil, &refDomainSize, &sidNameUse)
- if err != nil && err != windows.ERROR_INSUFFICIENT_BUFFER { //nolint:errorlint // err is Errno
- return "", &AccountLookupError{sid, err}
- }
-
- nameBuffer := make([]uint16, nameSize)
- refDomainBuffer := make([]uint16, refDomainSize)
- err = lookupAccountSid(nil, sidPtr, &nameBuffer[0], &nameSize, &refDomainBuffer[0], &refDomainSize, &sidNameUse)
- if err != nil {
- return "", &AccountLookupError{sid, err}
- }
-
- name = windows.UTF16ToString(nameBuffer)
- return name, nil
-}
-
-func SddlToSecurityDescriptor(sddl string) ([]byte, error) {
- sd, err := windows.SecurityDescriptorFromString(sddl)
- if err != nil {
- return nil, &SddlConversionError{Sddl: sddl, Err: err}
- }
- b := unsafe.Slice((*byte)(unsafe.Pointer(sd)), sd.Length())
- return b, nil
-}
-
-func SecurityDescriptorToSddl(sd []byte) (string, error) {
- if l := int(unsafe.Sizeof(windows.SECURITY_DESCRIPTOR{})); len(sd) < l {
- return "", fmt.Errorf("SecurityDescriptor (%d) smaller than expected (%d): %w", len(sd), l, windows.ERROR_INCORRECT_SIZE)
- }
- s := (*windows.SECURITY_DESCRIPTOR)(unsafe.Pointer(&sd[0]))
- return s.String(), nil
-}
diff --git a/vendor/github.com/Microsoft/go-winio/syscall.go b/vendor/github.com/Microsoft/go-winio/syscall.go
deleted file mode 100644
index a6ca111b..00000000
--- a/vendor/github.com/Microsoft/go-winio/syscall.go
+++ /dev/null
@@ -1,5 +0,0 @@
-//go:build windows
-
-package winio
-
-//go:generate go run github.com/Microsoft/go-winio/tools/mkwinsyscall -output zsyscall_windows.go ./*.go
diff --git a/vendor/github.com/Microsoft/go-winio/zsyscall_windows.go b/vendor/github.com/Microsoft/go-winio/zsyscall_windows.go
deleted file mode 100644
index 89b66eda..00000000
--- a/vendor/github.com/Microsoft/go-winio/zsyscall_windows.go
+++ /dev/null
@@ -1,378 +0,0 @@
-//go:build windows
-
-// Code generated by 'go generate' using "github.com/Microsoft/go-winio/tools/mkwinsyscall"; DO NOT EDIT.
-
-package winio
-
-import (
- "syscall"
- "unsafe"
-
- "golang.org/x/sys/windows"
-)
-
-var _ unsafe.Pointer
-
-// Do the interface allocations only once for common
-// Errno values.
-const (
- errnoERROR_IO_PENDING = 997
-)
-
-var (
- errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING)
- errERROR_EINVAL error = syscall.EINVAL
-)
-
-// errnoErr returns common boxed Errno values, to prevent
-// allocations at runtime.
-func errnoErr(e syscall.Errno) error {
- switch e {
- case 0:
- return errERROR_EINVAL
- case errnoERROR_IO_PENDING:
- return errERROR_IO_PENDING
- }
- return e
-}
-
-var (
- modadvapi32 = windows.NewLazySystemDLL("advapi32.dll")
- modkernel32 = windows.NewLazySystemDLL("kernel32.dll")
- modntdll = windows.NewLazySystemDLL("ntdll.dll")
- modws2_32 = windows.NewLazySystemDLL("ws2_32.dll")
-
- procAdjustTokenPrivileges = modadvapi32.NewProc("AdjustTokenPrivileges")
- procConvertSidToStringSidW = modadvapi32.NewProc("ConvertSidToStringSidW")
- procConvertStringSidToSidW = modadvapi32.NewProc("ConvertStringSidToSidW")
- procImpersonateSelf = modadvapi32.NewProc("ImpersonateSelf")
- procLookupAccountNameW = modadvapi32.NewProc("LookupAccountNameW")
- procLookupAccountSidW = modadvapi32.NewProc("LookupAccountSidW")
- procLookupPrivilegeDisplayNameW = modadvapi32.NewProc("LookupPrivilegeDisplayNameW")
- procLookupPrivilegeNameW = modadvapi32.NewProc("LookupPrivilegeNameW")
- procLookupPrivilegeValueW = modadvapi32.NewProc("LookupPrivilegeValueW")
- procOpenThreadToken = modadvapi32.NewProc("OpenThreadToken")
- procRevertToSelf = modadvapi32.NewProc("RevertToSelf")
- procBackupRead = modkernel32.NewProc("BackupRead")
- procBackupWrite = modkernel32.NewProc("BackupWrite")
- procCancelIoEx = modkernel32.NewProc("CancelIoEx")
- procConnectNamedPipe = modkernel32.NewProc("ConnectNamedPipe")
- procCreateIoCompletionPort = modkernel32.NewProc("CreateIoCompletionPort")
- procCreateNamedPipeW = modkernel32.NewProc("CreateNamedPipeW")
- procDisconnectNamedPipe = modkernel32.NewProc("DisconnectNamedPipe")
- procGetCurrentThread = modkernel32.NewProc("GetCurrentThread")
- procGetNamedPipeHandleStateW = modkernel32.NewProc("GetNamedPipeHandleStateW")
- procGetNamedPipeInfo = modkernel32.NewProc("GetNamedPipeInfo")
- procGetQueuedCompletionStatus = modkernel32.NewProc("GetQueuedCompletionStatus")
- procSetFileCompletionNotificationModes = modkernel32.NewProc("SetFileCompletionNotificationModes")
- procNtCreateNamedPipeFile = modntdll.NewProc("NtCreateNamedPipeFile")
- procRtlDefaultNpAcl = modntdll.NewProc("RtlDefaultNpAcl")
- procRtlDosPathNameToNtPathName_U = modntdll.NewProc("RtlDosPathNameToNtPathName_U")
- procRtlNtStatusToDosErrorNoTeb = modntdll.NewProc("RtlNtStatusToDosErrorNoTeb")
- procWSAGetOverlappedResult = modws2_32.NewProc("WSAGetOverlappedResult")
-)
-
-func adjustTokenPrivileges(token windows.Token, releaseAll bool, input *byte, outputSize uint32, output *byte, requiredSize *uint32) (success bool, err error) {
- var _p0 uint32
- if releaseAll {
- _p0 = 1
- }
- r0, _, e1 := syscall.SyscallN(procAdjustTokenPrivileges.Addr(), uintptr(token), uintptr(_p0), uintptr(unsafe.Pointer(input)), uintptr(outputSize), uintptr(unsafe.Pointer(output)), uintptr(unsafe.Pointer(requiredSize)))
- success = r0 != 0
- if true {
- err = errnoErr(e1)
- }
- return
-}
-
-func convertSidToStringSid(sid *byte, str **uint16) (err error) {
- r1, _, e1 := syscall.SyscallN(procConvertSidToStringSidW.Addr(), uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(str)))
- if r1 == 0 {
- err = errnoErr(e1)
- }
- return
-}
-
-func convertStringSidToSid(str *uint16, sid **byte) (err error) {
- r1, _, e1 := syscall.SyscallN(procConvertStringSidToSidW.Addr(), uintptr(unsafe.Pointer(str)), uintptr(unsafe.Pointer(sid)))
- if r1 == 0 {
- err = errnoErr(e1)
- }
- return
-}
-
-func impersonateSelf(level uint32) (err error) {
- r1, _, e1 := syscall.SyscallN(procImpersonateSelf.Addr(), uintptr(level))
- if r1 == 0 {
- err = errnoErr(e1)
- }
- return
-}
-
-func lookupAccountName(systemName *uint16, accountName string, sid *byte, sidSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) {
- var _p0 *uint16
- _p0, err = syscall.UTF16PtrFromString(accountName)
- if err != nil {
- return
- }
- return _lookupAccountName(systemName, _p0, sid, sidSize, refDomain, refDomainSize, sidNameUse)
-}
-
-func _lookupAccountName(systemName *uint16, accountName *uint16, sid *byte, sidSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) {
- r1, _, e1 := syscall.SyscallN(procLookupAccountNameW.Addr(), uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(accountName)), uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(sidSize)), uintptr(unsafe.Pointer(refDomain)), uintptr(unsafe.Pointer(refDomainSize)), uintptr(unsafe.Pointer(sidNameUse)))
- if r1 == 0 {
- err = errnoErr(e1)
- }
- return
-}
-
-func lookupAccountSid(systemName *uint16, sid *byte, name *uint16, nameSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) {
- r1, _, e1 := syscall.SyscallN(procLookupAccountSidW.Addr(), uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(nameSize)), uintptr(unsafe.Pointer(refDomain)), uintptr(unsafe.Pointer(refDomainSize)), uintptr(unsafe.Pointer(sidNameUse)))
- if r1 == 0 {
- err = errnoErr(e1)
- }
- return
-}
-
-func lookupPrivilegeDisplayName(systemName string, name *uint16, buffer *uint16, size *uint32, languageId *uint32) (err error) {
- var _p0 *uint16
- _p0, err = syscall.UTF16PtrFromString(systemName)
- if err != nil {
- return
- }
- return _lookupPrivilegeDisplayName(_p0, name, buffer, size, languageId)
-}
-
-func _lookupPrivilegeDisplayName(systemName *uint16, name *uint16, buffer *uint16, size *uint32, languageId *uint32) (err error) {
- r1, _, e1 := syscall.SyscallN(procLookupPrivilegeDisplayNameW.Addr(), uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(buffer)), uintptr(unsafe.Pointer(size)), uintptr(unsafe.Pointer(languageId)))
- if r1 == 0 {
- err = errnoErr(e1)
- }
- return
-}
-
-func lookupPrivilegeName(systemName string, luid *uint64, buffer *uint16, size *uint32) (err error) {
- var _p0 *uint16
- _p0, err = syscall.UTF16PtrFromString(systemName)
- if err != nil {
- return
- }
- return _lookupPrivilegeName(_p0, luid, buffer, size)
-}
-
-func _lookupPrivilegeName(systemName *uint16, luid *uint64, buffer *uint16, size *uint32) (err error) {
- r1, _, e1 := syscall.SyscallN(procLookupPrivilegeNameW.Addr(), uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(luid)), uintptr(unsafe.Pointer(buffer)), uintptr(unsafe.Pointer(size)))
- if r1 == 0 {
- err = errnoErr(e1)
- }
- return
-}
-
-func lookupPrivilegeValue(systemName string, name string, luid *uint64) (err error) {
- var _p0 *uint16
- _p0, err = syscall.UTF16PtrFromString(systemName)
- if err != nil {
- return
- }
- var _p1 *uint16
- _p1, err = syscall.UTF16PtrFromString(name)
- if err != nil {
- return
- }
- return _lookupPrivilegeValue(_p0, _p1, luid)
-}
-
-func _lookupPrivilegeValue(systemName *uint16, name *uint16, luid *uint64) (err error) {
- r1, _, e1 := syscall.SyscallN(procLookupPrivilegeValueW.Addr(), uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(luid)))
- if r1 == 0 {
- err = errnoErr(e1)
- }
- return
-}
-
-func openThreadToken(thread windows.Handle, accessMask uint32, openAsSelf bool, token *windows.Token) (err error) {
- var _p0 uint32
- if openAsSelf {
- _p0 = 1
- }
- r1, _, e1 := syscall.SyscallN(procOpenThreadToken.Addr(), uintptr(thread), uintptr(accessMask), uintptr(_p0), uintptr(unsafe.Pointer(token)))
- if r1 == 0 {
- err = errnoErr(e1)
- }
- return
-}
-
-func revertToSelf() (err error) {
- r1, _, e1 := syscall.SyscallN(procRevertToSelf.Addr())
- if r1 == 0 {
- err = errnoErr(e1)
- }
- return
-}
-
-func backupRead(h windows.Handle, b []byte, bytesRead *uint32, abort bool, processSecurity bool, context *uintptr) (err error) {
- var _p0 *byte
- if len(b) > 0 {
- _p0 = &b[0]
- }
- var _p1 uint32
- if abort {
- _p1 = 1
- }
- var _p2 uint32
- if processSecurity {
- _p2 = 1
- }
- r1, _, e1 := syscall.SyscallN(procBackupRead.Addr(), uintptr(h), uintptr(unsafe.Pointer(_p0)), uintptr(len(b)), uintptr(unsafe.Pointer(bytesRead)), uintptr(_p1), uintptr(_p2), uintptr(unsafe.Pointer(context)))
- if r1 == 0 {
- err = errnoErr(e1)
- }
- return
-}
-
-func backupWrite(h windows.Handle, b []byte, bytesWritten *uint32, abort bool, processSecurity bool, context *uintptr) (err error) {
- var _p0 *byte
- if len(b) > 0 {
- _p0 = &b[0]
- }
- var _p1 uint32
- if abort {
- _p1 = 1
- }
- var _p2 uint32
- if processSecurity {
- _p2 = 1
- }
- r1, _, e1 := syscall.SyscallN(procBackupWrite.Addr(), uintptr(h), uintptr(unsafe.Pointer(_p0)), uintptr(len(b)), uintptr(unsafe.Pointer(bytesWritten)), uintptr(_p1), uintptr(_p2), uintptr(unsafe.Pointer(context)))
- if r1 == 0 {
- err = errnoErr(e1)
- }
- return
-}
-
-func cancelIoEx(file windows.Handle, o *windows.Overlapped) (err error) {
- r1, _, e1 := syscall.SyscallN(procCancelIoEx.Addr(), uintptr(file), uintptr(unsafe.Pointer(o)))
- if r1 == 0 {
- err = errnoErr(e1)
- }
- return
-}
-
-func connectNamedPipe(pipe windows.Handle, o *windows.Overlapped) (err error) {
- r1, _, e1 := syscall.SyscallN(procConnectNamedPipe.Addr(), uintptr(pipe), uintptr(unsafe.Pointer(o)))
- if r1 == 0 {
- err = errnoErr(e1)
- }
- return
-}
-
-func createIoCompletionPort(file windows.Handle, port windows.Handle, key uintptr, threadCount uint32) (newport windows.Handle, err error) {
- r0, _, e1 := syscall.SyscallN(procCreateIoCompletionPort.Addr(), uintptr(file), uintptr(port), uintptr(key), uintptr(threadCount))
- newport = windows.Handle(r0)
- if newport == 0 {
- err = errnoErr(e1)
- }
- return
-}
-
-func createNamedPipe(name string, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *windows.SecurityAttributes) (handle windows.Handle, err error) {
- var _p0 *uint16
- _p0, err = syscall.UTF16PtrFromString(name)
- if err != nil {
- return
- }
- return _createNamedPipe(_p0, flags, pipeMode, maxInstances, outSize, inSize, defaultTimeout, sa)
-}
-
-func _createNamedPipe(name *uint16, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *windows.SecurityAttributes) (handle windows.Handle, err error) {
- r0, _, e1 := syscall.SyscallN(procCreateNamedPipeW.Addr(), uintptr(unsafe.Pointer(name)), uintptr(flags), uintptr(pipeMode), uintptr(maxInstances), uintptr(outSize), uintptr(inSize), uintptr(defaultTimeout), uintptr(unsafe.Pointer(sa)))
- handle = windows.Handle(r0)
- if handle == windows.InvalidHandle {
- err = errnoErr(e1)
- }
- return
-}
-
-func disconnectNamedPipe(pipe windows.Handle) (err error) {
- r1, _, e1 := syscall.SyscallN(procDisconnectNamedPipe.Addr(), uintptr(pipe))
- if r1 == 0 {
- err = errnoErr(e1)
- }
- return
-}
-
-func getCurrentThread() (h windows.Handle) {
- r0, _, _ := syscall.SyscallN(procGetCurrentThread.Addr())
- h = windows.Handle(r0)
- return
-}
-
-func getNamedPipeHandleState(pipe windows.Handle, state *uint32, curInstances *uint32, maxCollectionCount *uint32, collectDataTimeout *uint32, userName *uint16, maxUserNameSize uint32) (err error) {
- r1, _, e1 := syscall.SyscallN(procGetNamedPipeHandleStateW.Addr(), uintptr(pipe), uintptr(unsafe.Pointer(state)), uintptr(unsafe.Pointer(curInstances)), uintptr(unsafe.Pointer(maxCollectionCount)), uintptr(unsafe.Pointer(collectDataTimeout)), uintptr(unsafe.Pointer(userName)), uintptr(maxUserNameSize))
- if r1 == 0 {
- err = errnoErr(e1)
- }
- return
-}
-
-func getNamedPipeInfo(pipe windows.Handle, flags *uint32, outSize *uint32, inSize *uint32, maxInstances *uint32) (err error) {
- r1, _, e1 := syscall.SyscallN(procGetNamedPipeInfo.Addr(), uintptr(pipe), uintptr(unsafe.Pointer(flags)), uintptr(unsafe.Pointer(outSize)), uintptr(unsafe.Pointer(inSize)), uintptr(unsafe.Pointer(maxInstances)))
- if r1 == 0 {
- err = errnoErr(e1)
- }
- return
-}
-
-func getQueuedCompletionStatus(port windows.Handle, bytes *uint32, key *uintptr, o **ioOperation, timeout uint32) (err error) {
- r1, _, e1 := syscall.SyscallN(procGetQueuedCompletionStatus.Addr(), uintptr(port), uintptr(unsafe.Pointer(bytes)), uintptr(unsafe.Pointer(key)), uintptr(unsafe.Pointer(o)), uintptr(timeout))
- if r1 == 0 {
- err = errnoErr(e1)
- }
- return
-}
-
-func setFileCompletionNotificationModes(h windows.Handle, flags uint8) (err error) {
- r1, _, e1 := syscall.SyscallN(procSetFileCompletionNotificationModes.Addr(), uintptr(h), uintptr(flags))
- if r1 == 0 {
- err = errnoErr(e1)
- }
- return
-}
-
-func ntCreateNamedPipeFile(pipe *windows.Handle, access ntAccessMask, oa *objectAttributes, iosb *ioStatusBlock, share ntFileShareMode, disposition ntFileCreationDisposition, options ntFileOptions, typ uint32, readMode uint32, completionMode uint32, maxInstances uint32, inboundQuota uint32, outputQuota uint32, timeout *int64) (status ntStatus) {
- r0, _, _ := syscall.SyscallN(procNtCreateNamedPipeFile.Addr(), uintptr(unsafe.Pointer(pipe)), uintptr(access), uintptr(unsafe.Pointer(oa)), uintptr(unsafe.Pointer(iosb)), uintptr(share), uintptr(disposition), uintptr(options), uintptr(typ), uintptr(readMode), uintptr(completionMode), uintptr(maxInstances), uintptr(inboundQuota), uintptr(outputQuota), uintptr(unsafe.Pointer(timeout)))
- status = ntStatus(r0)
- return
-}
-
-func rtlDefaultNpAcl(dacl *uintptr) (status ntStatus) {
- r0, _, _ := syscall.SyscallN(procRtlDefaultNpAcl.Addr(), uintptr(unsafe.Pointer(dacl)))
- status = ntStatus(r0)
- return
-}
-
-func rtlDosPathNameToNtPathName(name *uint16, ntName *unicodeString, filePart uintptr, reserved uintptr) (status ntStatus) {
- r0, _, _ := syscall.SyscallN(procRtlDosPathNameToNtPathName_U.Addr(), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(ntName)), uintptr(filePart), uintptr(reserved))
- status = ntStatus(r0)
- return
-}
-
-func rtlNtStatusToDosError(status ntStatus) (winerr error) {
- r0, _, _ := syscall.SyscallN(procRtlNtStatusToDosErrorNoTeb.Addr(), uintptr(status))
- if r0 != 0 {
- winerr = syscall.Errno(r0)
- }
- return
-}
-
-func wsaGetOverlappedResult(h windows.Handle, o *windows.Overlapped, bytes *uint32, wait bool, flags *uint32) (err error) {
- var _p0 uint32
- if wait {
- _p0 = 1
- }
- r1, _, e1 := syscall.SyscallN(procWSAGetOverlappedResult.Addr(), uintptr(h), uintptr(unsafe.Pointer(o)), uintptr(unsafe.Pointer(bytes)), uintptr(_p0), uintptr(unsafe.Pointer(flags)))
- if r1 == 0 {
- err = errnoErr(e1)
- }
- return
-}
diff --git a/vendor/github.com/containerd/errdefs/LICENSE b/vendor/github.com/containerd/errdefs/LICENSE
deleted file mode 100644
index 584149b6..00000000
--- a/vendor/github.com/containerd/errdefs/LICENSE
+++ /dev/null
@@ -1,191 +0,0 @@
-
- Apache License
- Version 2.0, January 2004
- https://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
-
- Copyright The containerd Authors
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- https://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
diff --git a/vendor/github.com/containerd/errdefs/README.md b/vendor/github.com/containerd/errdefs/README.md
deleted file mode 100644
index bd418c63..00000000
--- a/vendor/github.com/containerd/errdefs/README.md
+++ /dev/null
@@ -1,13 +0,0 @@
-# errdefs
-
-A Go package for defining and checking common containerd errors.
-
-## Project details
-
-**errdefs** is a containerd sub-project, licensed under the [Apache 2.0 license](./LICENSE).
-As a containerd sub-project, you will find the:
- * [Project governance](https://github.com/containerd/project/blob/main/GOVERNANCE.md),
- * [Maintainers](https://github.com/containerd/project/blob/main/MAINTAINERS),
- * and [Contributing guidelines](https://github.com/containerd/project/blob/main/CONTRIBUTING.md)
-
-information in our [`containerd/project`](https://github.com/containerd/project) repository.
diff --git a/vendor/github.com/containerd/errdefs/errors.go b/vendor/github.com/containerd/errdefs/errors.go
deleted file mode 100644
index f654d196..00000000
--- a/vendor/github.com/containerd/errdefs/errors.go
+++ /dev/null
@@ -1,443 +0,0 @@
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-// Package errdefs defines the common errors used throughout containerd
-// packages.
-//
-// Use with fmt.Errorf to add context to an error.
-//
-// To detect an error class, use the IsXXX functions to tell whether an error
-// is of a certain type.
-package errdefs
-
-import (
- "context"
- "errors"
-)
-
-// Definitions of common error types used throughout containerd. All containerd
-// errors returned by most packages will map into one of these errors classes.
-// Packages should return errors of these types when they want to instruct a
-// client to take a particular action.
-//
-// These errors map closely to grpc errors.
-var (
- ErrUnknown = errUnknown{}
- ErrInvalidArgument = errInvalidArgument{}
- ErrNotFound = errNotFound{}
- ErrAlreadyExists = errAlreadyExists{}
- ErrPermissionDenied = errPermissionDenied{}
- ErrResourceExhausted = errResourceExhausted{}
- ErrFailedPrecondition = errFailedPrecondition{}
- ErrConflict = errConflict{}
- ErrNotModified = errNotModified{}
- ErrAborted = errAborted{}
- ErrOutOfRange = errOutOfRange{}
- ErrNotImplemented = errNotImplemented{}
- ErrInternal = errInternal{}
- ErrUnavailable = errUnavailable{}
- ErrDataLoss = errDataLoss{}
- ErrUnauthenticated = errUnauthorized{}
-)
-
-// cancelled maps to Moby's "ErrCancelled"
-type cancelled interface {
- Cancelled()
-}
-
-// IsCanceled returns true if the error is due to `context.Canceled`.
-func IsCanceled(err error) bool {
- return errors.Is(err, context.Canceled) || isInterface[cancelled](err)
-}
-
-type errUnknown struct{}
-
-func (errUnknown) Error() string { return "unknown" }
-
-func (errUnknown) Unknown() {}
-
-func (e errUnknown) WithMessage(msg string) error {
- return customMessage{e, msg}
-}
-
-// unknown maps to Moby's "ErrUnknown"
-type unknown interface {
- Unknown()
-}
-
-// IsUnknown returns true if the error is due to an unknown error,
-// unhandled condition or unexpected response.
-func IsUnknown(err error) bool {
- return errors.Is(err, errUnknown{}) || isInterface[unknown](err)
-}
-
-type errInvalidArgument struct{}
-
-func (errInvalidArgument) Error() string { return "invalid argument" }
-
-func (errInvalidArgument) InvalidParameter() {}
-
-func (e errInvalidArgument) WithMessage(msg string) error {
- return customMessage{e, msg}
-}
-
-// invalidParameter maps to Moby's "ErrInvalidParameter"
-type invalidParameter interface {
- InvalidParameter()
-}
-
-// IsInvalidArgument returns true if the error is due to an invalid argument
-func IsInvalidArgument(err error) bool {
- return errors.Is(err, ErrInvalidArgument) || isInterface[invalidParameter](err)
-}
-
-// deadlineExceed maps to Moby's "ErrDeadline"
-type deadlineExceeded interface {
- DeadlineExceeded()
-}
-
-// IsDeadlineExceeded returns true if the error is due to
-// `context.DeadlineExceeded`.
-func IsDeadlineExceeded(err error) bool {
- return errors.Is(err, context.DeadlineExceeded) || isInterface[deadlineExceeded](err)
-}
-
-type errNotFound struct{}
-
-func (errNotFound) Error() string { return "not found" }
-
-func (errNotFound) NotFound() {}
-
-func (e errNotFound) WithMessage(msg string) error {
- return customMessage{e, msg}
-}
-
-// notFound maps to Moby's "ErrNotFound"
-type notFound interface {
- NotFound()
-}
-
-// IsNotFound returns true if the error is due to a missing object
-func IsNotFound(err error) bool {
- return errors.Is(err, ErrNotFound) || isInterface[notFound](err)
-}
-
-type errAlreadyExists struct{}
-
-func (errAlreadyExists) Error() string { return "already exists" }
-
-func (errAlreadyExists) AlreadyExists() {}
-
-func (e errAlreadyExists) WithMessage(msg string) error {
- return customMessage{e, msg}
-}
-
-type alreadyExists interface {
- AlreadyExists()
-}
-
-// IsAlreadyExists returns true if the error is due to an already existing
-// metadata item
-func IsAlreadyExists(err error) bool {
- return errors.Is(err, ErrAlreadyExists) || isInterface[alreadyExists](err)
-}
-
-type errPermissionDenied struct{}
-
-func (errPermissionDenied) Error() string { return "permission denied" }
-
-func (errPermissionDenied) Forbidden() {}
-
-func (e errPermissionDenied) WithMessage(msg string) error {
- return customMessage{e, msg}
-}
-
-// forbidden maps to Moby's "ErrForbidden"
-type forbidden interface {
- Forbidden()
-}
-
-// IsPermissionDenied returns true if the error is due to permission denied
-// or forbidden (403) response
-func IsPermissionDenied(err error) bool {
- return errors.Is(err, ErrPermissionDenied) || isInterface[forbidden](err)
-}
-
-type errResourceExhausted struct{}
-
-func (errResourceExhausted) Error() string { return "resource exhausted" }
-
-func (errResourceExhausted) ResourceExhausted() {}
-
-func (e errResourceExhausted) WithMessage(msg string) error {
- return customMessage{e, msg}
-}
-
-type resourceExhausted interface {
- ResourceExhausted()
-}
-
-// IsResourceExhausted returns true if the error is due to
-// a lack of resources or too many attempts.
-func IsResourceExhausted(err error) bool {
- return errors.Is(err, errResourceExhausted{}) || isInterface[resourceExhausted](err)
-}
-
-type errFailedPrecondition struct{}
-
-func (e errFailedPrecondition) Error() string { return "failed precondition" }
-
-func (errFailedPrecondition) FailedPrecondition() {}
-
-func (e errFailedPrecondition) WithMessage(msg string) error {
- return customMessage{e, msg}
-}
-
-type failedPrecondition interface {
- FailedPrecondition()
-}
-
-// IsFailedPrecondition returns true if an operation could not proceed due to
-// the lack of a particular condition
-func IsFailedPrecondition(err error) bool {
- return errors.Is(err, errFailedPrecondition{}) || isInterface[failedPrecondition](err)
-}
-
-type errConflict struct{}
-
-func (errConflict) Error() string { return "conflict" }
-
-func (errConflict) Conflict() {}
-
-func (e errConflict) WithMessage(msg string) error {
- return customMessage{e, msg}
-}
-
-// conflict maps to Moby's "ErrConflict"
-type conflict interface {
- Conflict()
-}
-
-// IsConflict returns true if an operation could not proceed due to
-// a conflict.
-func IsConflict(err error) bool {
- return errors.Is(err, errConflict{}) || isInterface[conflict](err)
-}
-
-type errNotModified struct{}
-
-func (errNotModified) Error() string { return "not modified" }
-
-func (errNotModified) NotModified() {}
-
-func (e errNotModified) WithMessage(msg string) error {
- return customMessage{e, msg}
-}
-
-// notModified maps to Moby's "ErrNotModified"
-type notModified interface {
- NotModified()
-}
-
-// IsNotModified returns true if an operation could not proceed due
-// to an object not modified from a previous state.
-func IsNotModified(err error) bool {
- return errors.Is(err, errNotModified{}) || isInterface[notModified](err)
-}
-
-type errAborted struct{}
-
-func (errAborted) Error() string { return "aborted" }
-
-func (errAborted) Aborted() {}
-
-func (e errAborted) WithMessage(msg string) error {
- return customMessage{e, msg}
-}
-
-type aborted interface {
- Aborted()
-}
-
-// IsAborted returns true if an operation was aborted.
-func IsAborted(err error) bool {
- return errors.Is(err, errAborted{}) || isInterface[aborted](err)
-}
-
-type errOutOfRange struct{}
-
-func (errOutOfRange) Error() string { return "out of range" }
-
-func (errOutOfRange) OutOfRange() {}
-
-func (e errOutOfRange) WithMessage(msg string) error {
- return customMessage{e, msg}
-}
-
-type outOfRange interface {
- OutOfRange()
-}
-
-// IsOutOfRange returns true if an operation could not proceed due
-// to data being out of the expected range.
-func IsOutOfRange(err error) bool {
- return errors.Is(err, errOutOfRange{}) || isInterface[outOfRange](err)
-}
-
-type errNotImplemented struct{}
-
-func (errNotImplemented) Error() string { return "not implemented" }
-
-func (errNotImplemented) NotImplemented() {}
-
-func (e errNotImplemented) WithMessage(msg string) error {
- return customMessage{e, msg}
-}
-
-// notImplemented maps to Moby's "ErrNotImplemented"
-type notImplemented interface {
- NotImplemented()
-}
-
-// IsNotImplemented returns true if the error is due to not being implemented
-func IsNotImplemented(err error) bool {
- return errors.Is(err, errNotImplemented{}) || isInterface[notImplemented](err)
-}
-
-type errInternal struct{}
-
-func (errInternal) Error() string { return "internal" }
-
-func (errInternal) System() {}
-
-func (e errInternal) WithMessage(msg string) error {
- return customMessage{e, msg}
-}
-
-// system maps to Moby's "ErrSystem"
-type system interface {
- System()
-}
-
-// IsInternal returns true if the error returns to an internal or system error
-func IsInternal(err error) bool {
- return errors.Is(err, errInternal{}) || isInterface[system](err)
-}
-
-type errUnavailable struct{}
-
-func (errUnavailable) Error() string { return "unavailable" }
-
-func (errUnavailable) Unavailable() {}
-
-func (e errUnavailable) WithMessage(msg string) error {
- return customMessage{e, msg}
-}
-
-// unavailable maps to Moby's "ErrUnavailable"
-type unavailable interface {
- Unavailable()
-}
-
-// IsUnavailable returns true if the error is due to a resource being unavailable
-func IsUnavailable(err error) bool {
- return errors.Is(err, errUnavailable{}) || isInterface[unavailable](err)
-}
-
-type errDataLoss struct{}
-
-func (errDataLoss) Error() string { return "data loss" }
-
-func (errDataLoss) DataLoss() {}
-
-func (e errDataLoss) WithMessage(msg string) error {
- return customMessage{e, msg}
-}
-
-// dataLoss maps to Moby's "ErrDataLoss"
-type dataLoss interface {
- DataLoss()
-}
-
-// IsDataLoss returns true if data during an operation was lost or corrupted
-func IsDataLoss(err error) bool {
- return errors.Is(err, errDataLoss{}) || isInterface[dataLoss](err)
-}
-
-type errUnauthorized struct{}
-
-func (errUnauthorized) Error() string { return "unauthorized" }
-
-func (errUnauthorized) Unauthorized() {}
-
-func (e errUnauthorized) WithMessage(msg string) error {
- return customMessage{e, msg}
-}
-
-// unauthorized maps to Moby's "ErrUnauthorized"
-type unauthorized interface {
- Unauthorized()
-}
-
-// IsUnauthorized returns true if the error indicates that the user was
-// unauthenticated or unauthorized.
-func IsUnauthorized(err error) bool {
- return errors.Is(err, errUnauthorized{}) || isInterface[unauthorized](err)
-}
-
-func isInterface[T any](err error) bool {
- for {
- switch x := err.(type) {
- case T:
- return true
- case customMessage:
- err = x.err
- case interface{ Unwrap() error }:
- err = x.Unwrap()
- if err == nil {
- return false
- }
- case interface{ Unwrap() []error }:
- for _, err := range x.Unwrap() {
- if isInterface[T](err) {
- return true
- }
- }
- return false
- default:
- return false
- }
- }
-}
-
-// customMessage is used to provide a defined error with a custom message.
-// The message is not wrapped but can be compared by the `Is(error) bool` interface.
-type customMessage struct {
- err error
- msg string
-}
-
-func (c customMessage) Is(err error) bool {
- return c.err == err
-}
-
-func (c customMessage) As(target any) bool {
- return errors.As(c.err, target)
-}
-
-func (c customMessage) Error() string {
- return c.msg
-}
diff --git a/vendor/github.com/containerd/errdefs/pkg/LICENSE b/vendor/github.com/containerd/errdefs/pkg/LICENSE
deleted file mode 100644
index 584149b6..00000000
--- a/vendor/github.com/containerd/errdefs/pkg/LICENSE
+++ /dev/null
@@ -1,191 +0,0 @@
-
- Apache License
- Version 2.0, January 2004
- https://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
-
- Copyright The containerd Authors
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- https://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
diff --git a/vendor/github.com/containerd/errdefs/pkg/errhttp/http.go b/vendor/github.com/containerd/errdefs/pkg/errhttp/http.go
deleted file mode 100644
index d7cd2b8c..00000000
--- a/vendor/github.com/containerd/errdefs/pkg/errhttp/http.go
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-// Package errhttp provides utility functions for translating errors to
-// and from a HTTP context.
-//
-// The functions ToHTTP and ToNative can be used to map server-side and
-// client-side errors to the correct types.
-package errhttp
-
-import (
- "errors"
- "net/http"
-
- "github.com/containerd/errdefs"
- "github.com/containerd/errdefs/pkg/internal/cause"
-)
-
-// ToHTTP returns the best status code for the given error
-func ToHTTP(err error) int {
- switch {
- case errdefs.IsNotFound(err):
- return http.StatusNotFound
- case errdefs.IsInvalidArgument(err):
- return http.StatusBadRequest
- case errdefs.IsConflict(err):
- return http.StatusConflict
- case errdefs.IsNotModified(err):
- return http.StatusNotModified
- case errdefs.IsFailedPrecondition(err):
- return http.StatusPreconditionFailed
- case errdefs.IsUnauthorized(err):
- return http.StatusUnauthorized
- case errdefs.IsPermissionDenied(err):
- return http.StatusForbidden
- case errdefs.IsResourceExhausted(err):
- return http.StatusTooManyRequests
- case errdefs.IsInternal(err):
- return http.StatusInternalServerError
- case errdefs.IsNotImplemented(err):
- return http.StatusNotImplemented
- case errdefs.IsUnavailable(err):
- return http.StatusServiceUnavailable
- case errdefs.IsUnknown(err):
- var unexpected cause.ErrUnexpectedStatus
- if errors.As(err, &unexpected) && unexpected.Status >= 200 && unexpected.Status < 600 {
- return unexpected.Status
- }
- return http.StatusInternalServerError
- default:
- return http.StatusInternalServerError
- }
-}
-
-// ToNative returns the error best matching the HTTP status code
-func ToNative(statusCode int) error {
- switch statusCode {
- case http.StatusNotFound:
- return errdefs.ErrNotFound
- case http.StatusBadRequest:
- return errdefs.ErrInvalidArgument
- case http.StatusConflict:
- return errdefs.ErrConflict
- case http.StatusPreconditionFailed:
- return errdefs.ErrFailedPrecondition
- case http.StatusUnauthorized:
- return errdefs.ErrUnauthenticated
- case http.StatusForbidden:
- return errdefs.ErrPermissionDenied
- case http.StatusNotModified:
- return errdefs.ErrNotModified
- case http.StatusTooManyRequests:
- return errdefs.ErrResourceExhausted
- case http.StatusInternalServerError:
- return errdefs.ErrInternal
- case http.StatusNotImplemented:
- return errdefs.ErrNotImplemented
- case http.StatusServiceUnavailable:
- return errdefs.ErrUnavailable
- default:
- return cause.ErrUnexpectedStatus{Status: statusCode}
- }
-}
diff --git a/vendor/github.com/containerd/errdefs/pkg/internal/cause/cause.go b/vendor/github.com/containerd/errdefs/pkg/internal/cause/cause.go
deleted file mode 100644
index d88756bb..00000000
--- a/vendor/github.com/containerd/errdefs/pkg/internal/cause/cause.go
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-// Package cause is used to define root causes for errors
-// common to errors packages like grpc and http.
-package cause
-
-import "fmt"
-
-type ErrUnexpectedStatus struct {
- Status int
-}
-
-const UnexpectedStatusPrefix = "unexpected status "
-
-func (e ErrUnexpectedStatus) Error() string {
- return fmt.Sprintf("%s%d", UnexpectedStatusPrefix, e.Status)
-}
-
-func (ErrUnexpectedStatus) Unknown() {}
diff --git a/vendor/github.com/containerd/errdefs/resolve.go b/vendor/github.com/containerd/errdefs/resolve.go
deleted file mode 100644
index c02d4a73..00000000
--- a/vendor/github.com/containerd/errdefs/resolve.go
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package errdefs
-
-import "context"
-
-// Resolve returns the first error found in the error chain which matches an
-// error defined in this package or context error. A raw, unwrapped error is
-// returned or ErrUnknown if no matching error is found.
-//
-// This is useful for determining a response code based on the outermost wrapped
-// error rather than the original cause. For example, a not found error deep
-// in the code may be wrapped as an invalid argument. When determining status
-// code from Is* functions, the depth or ordering of the error is not
-// considered.
-//
-// The search order is depth first, a wrapped error returned from any part of
-// the chain from `Unwrap() error` will be returned before any joined errors
-// as returned by `Unwrap() []error`.
-func Resolve(err error) error {
- if err == nil {
- return nil
- }
- err = firstError(err)
- if err == nil {
- err = ErrUnknown
- }
- return err
-}
-
-func firstError(err error) error {
- for {
- switch err {
- case ErrUnknown,
- ErrInvalidArgument,
- ErrNotFound,
- ErrAlreadyExists,
- ErrPermissionDenied,
- ErrResourceExhausted,
- ErrFailedPrecondition,
- ErrConflict,
- ErrNotModified,
- ErrAborted,
- ErrOutOfRange,
- ErrNotImplemented,
- ErrInternal,
- ErrUnavailable,
- ErrDataLoss,
- ErrUnauthenticated,
- context.DeadlineExceeded,
- context.Canceled:
- return err
- }
- switch e := err.(type) {
- case customMessage:
- err = e.err
- case unknown:
- return ErrUnknown
- case invalidParameter:
- return ErrInvalidArgument
- case notFound:
- return ErrNotFound
- case alreadyExists:
- return ErrAlreadyExists
- case forbidden:
- return ErrPermissionDenied
- case resourceExhausted:
- return ErrResourceExhausted
- case failedPrecondition:
- return ErrFailedPrecondition
- case conflict:
- return ErrConflict
- case notModified:
- return ErrNotModified
- case aborted:
- return ErrAborted
- case errOutOfRange:
- return ErrOutOfRange
- case notImplemented:
- return ErrNotImplemented
- case system:
- return ErrInternal
- case unavailable:
- return ErrUnavailable
- case dataLoss:
- return ErrDataLoss
- case unauthorized:
- return ErrUnauthenticated
- case deadlineExceeded:
- return context.DeadlineExceeded
- case cancelled:
- return context.Canceled
- case interface{ Unwrap() error }:
- err = e.Unwrap()
- if err == nil {
- return nil
- }
- case interface{ Unwrap() []error }:
- for _, ue := range e.Unwrap() {
- if fe := firstError(ue); fe != nil {
- return fe
- }
- }
- return nil
- case interface{ Is(error) bool }:
- for _, target := range []error{ErrUnknown,
- ErrInvalidArgument,
- ErrNotFound,
- ErrAlreadyExists,
- ErrPermissionDenied,
- ErrResourceExhausted,
- ErrFailedPrecondition,
- ErrConflict,
- ErrNotModified,
- ErrAborted,
- ErrOutOfRange,
- ErrNotImplemented,
- ErrInternal,
- ErrUnavailable,
- ErrDataLoss,
- ErrUnauthenticated,
- context.DeadlineExceeded,
- context.Canceled} {
- if e.Is(target) {
- return target
- }
- }
- return nil
- default:
- return nil
- }
- }
-}
diff --git a/vendor/github.com/containerd/log/.golangci.yml b/vendor/github.com/containerd/log/.golangci.yml
deleted file mode 100644
index a695775d..00000000
--- a/vendor/github.com/containerd/log/.golangci.yml
+++ /dev/null
@@ -1,30 +0,0 @@
-linters:
- enable:
- - exportloopref # Checks for pointers to enclosing loop variables
- - gofmt
- - goimports
- - gosec
- - ineffassign
- - misspell
- - nolintlint
- - revive
- - staticcheck
- - tenv # Detects using os.Setenv instead of t.Setenv since Go 1.17
- - unconvert
- - unused
- - vet
- - dupword # Checks for duplicate words in the source code
- disable:
- - errcheck
-
-run:
- timeout: 5m
- skip-dirs:
- - api
- - cluster
- - design
- - docs
- - docs/man
- - releases
- - reports
- - test # e2e scripts
diff --git a/vendor/github.com/containerd/log/LICENSE b/vendor/github.com/containerd/log/LICENSE
deleted file mode 100644
index 584149b6..00000000
--- a/vendor/github.com/containerd/log/LICENSE
+++ /dev/null
@@ -1,191 +0,0 @@
-
- Apache License
- Version 2.0, January 2004
- https://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
-
- Copyright The containerd Authors
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- https://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
diff --git a/vendor/github.com/containerd/log/README.md b/vendor/github.com/containerd/log/README.md
deleted file mode 100644
index 00e08498..00000000
--- a/vendor/github.com/containerd/log/README.md
+++ /dev/null
@@ -1,17 +0,0 @@
-# log
-
-A Go package providing a common logging interface across containerd repositories and a way for clients to use and configure logging in containerd packages.
-
-This package is not intended to be used as a standalone logging package outside of the containerd ecosystem and is intended as an interface wrapper around a logging implementation.
-In the future this package may be replaced with a common go logging interface.
-
-## Project details
-
-**log** is a containerd sub-project, licensed under the [Apache 2.0 license](./LICENSE).
-As a containerd sub-project, you will find the:
- * [Project governance](https://github.com/containerd/project/blob/main/GOVERNANCE.md),
- * [Maintainers](https://github.com/containerd/project/blob/main/MAINTAINERS),
- * and [Contributing guidelines](https://github.com/containerd/project/blob/main/CONTRIBUTING.md)
-
-information in our [`containerd/project`](https://github.com/containerd/project) repository.
-
diff --git a/vendor/github.com/containerd/log/context.go b/vendor/github.com/containerd/log/context.go
deleted file mode 100644
index 20153066..00000000
--- a/vendor/github.com/containerd/log/context.go
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-// Package log provides types and functions related to logging, passing
-// loggers through a context, and attaching context to the logger.
-//
-// # Transitional types
-//
-// This package contains various types that are aliases for types in [logrus].
-// These aliases are intended for transitioning away from hard-coding logrus
-// as logging implementation. Consumers of this package are encouraged to use
-// the type-aliases from this package instead of directly using their logrus
-// equivalent.
-//
-// The intent is to replace these aliases with locally defined types and
-// interfaces once all consumers are no longer directly importing logrus
-// types.
-//
-// IMPORTANT: due to the transitional purpose of this package, it is not
-// guaranteed for the full logrus API to be provided in the future. As
-// outlined, these aliases are provided as a step to transition away from
-// a specific implementation which, as a result, exposes the full logrus API.
-// While no decisions have been made on the ultimate design and interface
-// provided by this package, we do not expect carrying "less common" features.
-package log
-
-import (
- "context"
- "fmt"
-
- "github.com/sirupsen/logrus"
-)
-
-// G is a shorthand for [GetLogger].
-//
-// We may want to define this locally to a package to get package tagged log
-// messages.
-var G = GetLogger
-
-// L is an alias for the standard logger.
-var L = &Entry{
- Logger: logrus.StandardLogger(),
- // Default is three fields plus a little extra room.
- Data: make(Fields, 6),
-}
-
-type loggerKey struct{}
-
-// Fields type to pass to "WithFields".
-type Fields = map[string]any
-
-// Entry is a logging entry. It contains all the fields passed with
-// [Entry.WithFields]. It's finally logged when Trace, Debug, Info, Warn,
-// Error, Fatal or Panic is called on it. These objects can be reused and
-// passed around as much as you wish to avoid field duplication.
-//
-// Entry is a transitional type, and currently an alias for [logrus.Entry].
-type Entry = logrus.Entry
-
-// RFC3339NanoFixed is [time.RFC3339Nano] with nanoseconds padded using
-// zeros to ensure the formatted time is always the same number of
-// characters.
-const RFC3339NanoFixed = "2006-01-02T15:04:05.000000000Z07:00"
-
-// Level is a logging level.
-type Level = logrus.Level
-
-// Supported log levels.
-const (
- // TraceLevel level. Designates finer-grained informational events
- // than [DebugLevel].
- TraceLevel Level = logrus.TraceLevel
-
- // DebugLevel level. Usually only enabled when debugging. Very verbose
- // logging.
- DebugLevel Level = logrus.DebugLevel
-
- // InfoLevel level. General operational entries about what's going on
- // inside the application.
- InfoLevel Level = logrus.InfoLevel
-
- // WarnLevel level. Non-critical entries that deserve eyes.
- WarnLevel Level = logrus.WarnLevel
-
- // ErrorLevel level. Logs errors that should definitely be noted.
- // Commonly used for hooks to send errors to an error tracking service.
- ErrorLevel Level = logrus.ErrorLevel
-
- // FatalLevel level. Logs and then calls "logger.Exit(1)". It exits
- // even if the logging level is set to Panic.
- FatalLevel Level = logrus.FatalLevel
-
- // PanicLevel level. This is the highest level of severity. Logs and
- // then calls panic with the message passed to Debug, Info, ...
- PanicLevel Level = logrus.PanicLevel
-)
-
-// SetLevel sets log level globally. It returns an error if the given
-// level is not supported.
-//
-// level can be one of:
-//
-// - "trace" ([TraceLevel])
-// - "debug" ([DebugLevel])
-// - "info" ([InfoLevel])
-// - "warn" ([WarnLevel])
-// - "error" ([ErrorLevel])
-// - "fatal" ([FatalLevel])
-// - "panic" ([PanicLevel])
-func SetLevel(level string) error {
- lvl, err := logrus.ParseLevel(level)
- if err != nil {
- return err
- }
-
- L.Logger.SetLevel(lvl)
- return nil
-}
-
-// GetLevel returns the current log level.
-func GetLevel() Level {
- return L.Logger.GetLevel()
-}
-
-// OutputFormat specifies a log output format.
-type OutputFormat string
-
-// Supported log output formats.
-const (
- // TextFormat represents the text logging format.
- TextFormat OutputFormat = "text"
-
- // JSONFormat represents the JSON logging format.
- JSONFormat OutputFormat = "json"
-)
-
-// SetFormat sets the log output format ([TextFormat] or [JSONFormat]).
-func SetFormat(format OutputFormat) error {
- switch format {
- case TextFormat:
- L.Logger.SetFormatter(&logrus.TextFormatter{
- TimestampFormat: RFC3339NanoFixed,
- FullTimestamp: true,
- })
- return nil
- case JSONFormat:
- L.Logger.SetFormatter(&logrus.JSONFormatter{
- TimestampFormat: RFC3339NanoFixed,
- })
- return nil
- default:
- return fmt.Errorf("unknown log format: %s", format)
- }
-}
-
-// WithLogger returns a new context with the provided logger. Use in
-// combination with logger.WithField(s) for great effect.
-func WithLogger(ctx context.Context, logger *Entry) context.Context {
- return context.WithValue(ctx, loggerKey{}, logger.WithContext(ctx))
-}
-
-// GetLogger retrieves the current logger from the context. If no logger is
-// available, the default logger is returned.
-func GetLogger(ctx context.Context) *Entry {
- if logger := ctx.Value(loggerKey{}); logger != nil {
- return logger.(*Entry)
- }
- return L.WithContext(ctx)
-}
diff --git a/vendor/github.com/distribution/reference/.gitattributes b/vendor/github.com/distribution/reference/.gitattributes
deleted file mode 100644
index d207b180..00000000
--- a/vendor/github.com/distribution/reference/.gitattributes
+++ /dev/null
@@ -1 +0,0 @@
-*.go text eol=lf
diff --git a/vendor/github.com/distribution/reference/.gitignore b/vendor/github.com/distribution/reference/.gitignore
deleted file mode 100644
index dc07e6b0..00000000
--- a/vendor/github.com/distribution/reference/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-# Cover profiles
-*.out
diff --git a/vendor/github.com/distribution/reference/.golangci.yml b/vendor/github.com/distribution/reference/.golangci.yml
deleted file mode 100644
index 793f0bb7..00000000
--- a/vendor/github.com/distribution/reference/.golangci.yml
+++ /dev/null
@@ -1,18 +0,0 @@
-linters:
- enable:
- - bodyclose
- - dupword # Checks for duplicate words in the source code
- - gofmt
- - goimports
- - ineffassign
- - misspell
- - revive
- - staticcheck
- - unconvert
- - unused
- - vet
- disable:
- - errcheck
-
-run:
- deadline: 2m
diff --git a/vendor/github.com/distribution/reference/CODE-OF-CONDUCT.md b/vendor/github.com/distribution/reference/CODE-OF-CONDUCT.md
deleted file mode 100644
index 48f6704c..00000000
--- a/vendor/github.com/distribution/reference/CODE-OF-CONDUCT.md
+++ /dev/null
@@ -1,5 +0,0 @@
-# Code of Conduct
-
-We follow the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/main/code-of-conduct.md).
-
-Please contact the [CNCF Code of Conduct Committee](mailto:conduct@cncf.io) in order to report violations of the Code of Conduct.
diff --git a/vendor/github.com/distribution/reference/CONTRIBUTING.md b/vendor/github.com/distribution/reference/CONTRIBUTING.md
deleted file mode 100644
index ab219466..00000000
--- a/vendor/github.com/distribution/reference/CONTRIBUTING.md
+++ /dev/null
@@ -1,114 +0,0 @@
-# Contributing to the reference library
-
-## Community help
-
-If you need help, please ask in the [#distribution](https://cloud-native.slack.com/archives/C01GVR8SY4R) channel on CNCF community slack.
-[Click here for an invite to the CNCF community slack](https://slack.cncf.io/)
-
-## Reporting security issues
-
-The maintainers take security seriously. If you discover a security
-issue, please bring it to their attention right away!
-
-Please **DO NOT** file a public issue, instead send your report privately to
-[cncf-distribution-security@lists.cncf.io](mailto:cncf-distribution-security@lists.cncf.io).
-
-## Reporting an issue properly
-
-By following these simple rules you will get better and faster feedback on your issue.
-
- - search the bugtracker for an already reported issue
-
-### If you found an issue that describes your problem:
-
- - please read other user comments first, and confirm this is the same issue: a given error condition might be indicative of different problems - you may also find a workaround in the comments
- - please refrain from adding "same thing here" or "+1" comments
- - you don't need to comment on an issue to get notified of updates: just hit the "subscribe" button
- - comment if you have some new, technical and relevant information to add to the case
- - __DO NOT__ comment on closed issues or merged PRs. If you think you have a related problem, open up a new issue and reference the PR or issue.
-
-### If you have not found an existing issue that describes your problem:
-
- 1. create a new issue, with a succinct title that describes your issue:
- - bad title: "It doesn't work with my docker"
- - good title: "Private registry push fail: 400 error with E_INVALID_DIGEST"
- 2. copy the output of (or similar for other container tools):
- - `docker version`
- - `docker info`
- - `docker exec registry --version`
- 3. copy the command line you used to launch your Registry
- 4. restart your docker daemon in debug mode (add `-D` to the daemon launch arguments)
- 5. reproduce your problem and get your docker daemon logs showing the error
- 6. if relevant, copy your registry logs that show the error
- 7. provide any relevant detail about your specific Registry configuration (e.g., storage backend used)
- 8. indicate if you are using an enterprise proxy, Nginx, or anything else between you and your Registry
-
-## Contributing Code
-
-Contributions should be made via pull requests. Pull requests will be reviewed
-by one or more maintainers or reviewers and merged when acceptable.
-
-You should follow the basic GitHub workflow:
-
- 1. Use your own [fork](https://help.github.com/en/articles/about-forks)
- 2. Create your [change](https://github.com/containerd/project/blob/master/CONTRIBUTING.md#successful-changes)
- 3. Test your code
- 4. [Commit](https://github.com/containerd/project/blob/master/CONTRIBUTING.md#commit-messages) your work, always [sign your commits](https://github.com/containerd/project/blob/master/CONTRIBUTING.md#commit-messages)
- 5. Push your change to your fork and create a [Pull Request](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/creating-a-pull-request-from-a-fork)
-
-Refer to [containerd's contribution guide](https://github.com/containerd/project/blob/master/CONTRIBUTING.md#successful-changes)
-for tips on creating a successful contribution.
-
-## Sign your work
-
-The sign-off is a simple line at the end of the explanation for the patch. Your
-signature certifies that you wrote the patch or otherwise have the right to pass
-it on as an open-source patch. The rules are pretty simple: if you can certify
-the below (from [developercertificate.org](http://developercertificate.org/)):
-
-```
-Developer Certificate of Origin
-Version 1.1
-
-Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
-660 York Street, Suite 102,
-San Francisco, CA 94110 USA
-
-Everyone is permitted to copy and distribute verbatim copies of this
-license document, but changing it is not allowed.
-
-Developer's Certificate of Origin 1.1
-
-By making a contribution to this project, I certify that:
-
-(a) The contribution was created in whole or in part by me and I
- have the right to submit it under the open source license
- indicated in the file; or
-
-(b) The contribution is based upon previous work that, to the best
- of my knowledge, is covered under an appropriate open source
- license and I have the right under that license to submit that
- work with modifications, whether created in whole or in part
- by me, under the same open source license (unless I am
- permitted to submit under a different license), as indicated
- in the file; or
-
-(c) The contribution was provided directly to me by some other
- person who certified (a), (b) or (c) and I have not modified
- it.
-
-(d) I understand and agree that this project and the contribution
- are public and that a record of the contribution (including all
- personal information I submit with it, including my sign-off) is
- maintained indefinitely and may be redistributed consistent with
- this project or the open source license(s) involved.
-```
-
-Then you just add a line to every git commit message:
-
- Signed-off-by: Joe Smith
-
-Use your real name (sorry, no pseudonyms or anonymous contributions.)
-
-If you set your `user.name` and `user.email` git configs, you can sign your
-commit automatically with `git commit -s`.
diff --git a/vendor/github.com/distribution/reference/GOVERNANCE.md b/vendor/github.com/distribution/reference/GOVERNANCE.md
deleted file mode 100644
index 200045b0..00000000
--- a/vendor/github.com/distribution/reference/GOVERNANCE.md
+++ /dev/null
@@ -1,144 +0,0 @@
-# distribution/reference Project Governance
-
-Distribution [Code of Conduct](./CODE-OF-CONDUCT.md) can be found here.
-
-For specific guidance on practical contribution steps please
-see our [CONTRIBUTING.md](./CONTRIBUTING.md) guide.
-
-## Maintainership
-
-There are different types of maintainers, with different responsibilities, but
-all maintainers have 3 things in common:
-
-1) They share responsibility in the project's success.
-2) They have made a long-term, recurring time investment to improve the project.
-3) They spend that time doing whatever needs to be done, not necessarily what
-is the most interesting or fun.
-
-Maintainers are often under-appreciated, because their work is harder to appreciate.
-It's easy to appreciate a really cool and technically advanced feature. It's harder
-to appreciate the absence of bugs, the slow but steady improvement in stability,
-or the reliability of a release process. But those things distinguish a good
-project from a great one.
-
-## Reviewers
-
-A reviewer is a core role within the project.
-They share in reviewing issues and pull requests and their LGTM counts towards the
-required LGTM count to merge a code change into the project.
-
-Reviewers are part of the organization but do not have write access.
-Becoming a reviewer is a core aspect in the journey to becoming a maintainer.
-
-## Adding maintainers
-
-Maintainers are first and foremost contributors that have shown they are
-committed to the long term success of a project. Contributors wanting to become
-maintainers are expected to be deeply involved in contributing code, pull
-request review, and triage of issues in the project for more than three months.
-
-Just contributing does not make you a maintainer, it is about building trust
-with the current maintainers of the project and being a person that they can
-depend on and trust to make decisions in the best interest of the project.
-
-Periodically, the existing maintainers curate a list of contributors that have
-shown regular activity on the project over the prior months. From this list,
-maintainer candidates are selected and proposed in a pull request or a
-maintainers communication channel.
-
-After a candidate has been announced to the maintainers, the existing
-maintainers are given five business days to discuss the candidate, raise
-objections and cast their vote. Votes may take place on the communication
-channel or via pull request comment. Candidates must be approved by at least 66%
-of the current maintainers by adding their vote on the mailing list. The
-reviewer role has the same process but only requires 33% of current maintainers.
-Only maintainers of the repository that the candidate is proposed for are
-allowed to vote.
-
-If a candidate is approved, a maintainer will contact the candidate to invite
-the candidate to open a pull request that adds the contributor to the
-MAINTAINERS file. The voting process may take place inside a pull request if a
-maintainer has already discussed the candidacy with the candidate and a
-maintainer is willing to be a sponsor by opening the pull request. The candidate
-becomes a maintainer once the pull request is merged.
-
-## Stepping down policy
-
-Life priorities, interests, and passions can change. If you're a maintainer but
-feel you must remove yourself from the list, inform other maintainers that you
-intend to step down, and if possible, help find someone to pick up your work.
-At the very least, ensure your work can be continued where you left off.
-
-After you've informed other maintainers, create a pull request to remove
-yourself from the MAINTAINERS file.
-
-## Removal of inactive maintainers
-
-Similar to the procedure for adding new maintainers, existing maintainers can
-be removed from the list if they do not show significant activity on the
-project. Periodically, the maintainers review the list of maintainers and their
-activity over the last three months.
-
-If a maintainer has shown insufficient activity over this period, a neutral
-person will contact the maintainer to ask if they want to continue being
-a maintainer. If the maintainer decides to step down as a maintainer, they
-open a pull request to be removed from the MAINTAINERS file.
-
-If the maintainer wants to remain a maintainer, but is unable to perform the
-required duties they can be removed with a vote of at least 66% of the current
-maintainers. In this case, maintainers should first propose the change to
-maintainers via the maintainers communication channel, then open a pull request
-for voting. The voting period is five business days. The voting pull request
-should not come as a surpise to any maintainer and any discussion related to
-performance must not be discussed on the pull request.
-
-## How are decisions made?
-
-Docker distribution is an open-source project with an open design philosophy.
-This means that the repository is the source of truth for EVERY aspect of the
-project, including its philosophy, design, road map, and APIs. *If it's part of
-the project, it's in the repo. If it's in the repo, it's part of the project.*
-
-As a result, all decisions can be expressed as changes to the repository. An
-implementation change is a change to the source code. An API change is a change
-to the API specification. A philosophy change is a change to the philosophy
-manifesto, and so on.
-
-All decisions affecting distribution, big and small, follow the same 3 steps:
-
-* Step 1: Open a pull request. Anyone can do this.
-
-* Step 2: Discuss the pull request. Anyone can do this.
-
-* Step 3: Merge or refuse the pull request. Who does this depends on the nature
-of the pull request and which areas of the project it affects.
-
-## Helping contributors with the DCO
-
-The [DCO or `Sign your work`](./CONTRIBUTING.md#sign-your-work)
-requirement is not intended as a roadblock or speed bump.
-
-Some contributors are not as familiar with `git`, or have used a web
-based editor, and thus asking them to `git commit --amend -s` is not the best
-way forward.
-
-In this case, maintainers can update the commits based on clause (c) of the DCO.
-The most trivial way for a contributor to allow the maintainer to do this, is to
-add a DCO signature in a pull requests's comment, or a maintainer can simply
-note that the change is sufficiently trivial that it does not substantially
-change the existing contribution - i.e., a spelling change.
-
-When you add someone's DCO, please also add your own to keep a log.
-
-## I'm a maintainer. Should I make pull requests too?
-
-Yes. Nobody should ever push to master directly. All changes should be
-made through a pull request.
-
-## Conflict Resolution
-
-If you have a technical dispute that you feel has reached an impasse with a
-subset of the community, any contributor may open an issue, specifically
-calling for a resolution vote of the current core maintainers to resolve the
-dispute. The same voting quorums required (2/3) for adding and removing
-maintainers will apply to conflict resolution.
diff --git a/vendor/github.com/distribution/reference/LICENSE b/vendor/github.com/distribution/reference/LICENSE
deleted file mode 100644
index e06d2081..00000000
--- a/vendor/github.com/distribution/reference/LICENSE
+++ /dev/null
@@ -1,202 +0,0 @@
-Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
-
- APPENDIX: How to apply the Apache License to your work.
-
- To apply the Apache License to your work, attach the following
- boilerplate notice, with the fields enclosed by brackets "{}"
- replaced with your own identifying information. (Don't include
- the brackets!) The text should be enclosed in the appropriate
- comment syntax for the file format. We also recommend that a
- file or class name and description of purpose be included on the
- same "printed page" as the copyright notice for easier
- identification within third-party archives.
-
- Copyright {yyyy} {name of copyright owner}
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
diff --git a/vendor/github.com/distribution/reference/MAINTAINERS b/vendor/github.com/distribution/reference/MAINTAINERS
deleted file mode 100644
index 9e0a60c8..00000000
--- a/vendor/github.com/distribution/reference/MAINTAINERS
+++ /dev/null
@@ -1,26 +0,0 @@
-# Distribution project maintainers & reviewers
-#
-# See GOVERNANCE.md for maintainer versus reviewer roles
-#
-# MAINTAINERS (cncf-distribution-maintainers@lists.cncf.io)
-# GitHub ID, Name, Email address
-"chrispat","Chris Patterson","chrispat@github.com"
-"clarkbw","Bryan Clark","clarkbw@github.com"
-"corhere","Cory Snider","csnider@mirantis.com"
-"deleteriousEffect","Hayley Swimelar","hswimelar@gitlab.com"
-"heww","He Weiwei","hweiwei@vmware.com"
-"joaodrp","João Pereira","jpereira@gitlab.com"
-"justincormack","Justin Cormack","justin.cormack@docker.com"
-"squizzi","Kyle Squizzato","ksquizzato@mirantis.com"
-"milosgajdos","Milos Gajdos","milosthegajdos@gmail.com"
-"sargun","Sargun Dhillon","sargun@sargun.me"
-"wy65701436","Wang Yan","wangyan@vmware.com"
-"stevelasker","Steve Lasker","steve.lasker@microsoft.com"
-#
-# REVIEWERS
-# GitHub ID, Name, Email address
-"dmcgowan","Derek McGowan","derek@mcgstyle.net"
-"stevvooe","Stephen Day","stevvooe@gmail.com"
-"thajeztah","Sebastiaan van Stijn","github@gone.nl"
-"DavidSpek", "David van der Spek", "vanderspek.david@gmail.com"
-"Jamstah", "James Hewitt", "james.hewitt@gmail.com"
diff --git a/vendor/github.com/distribution/reference/Makefile b/vendor/github.com/distribution/reference/Makefile
deleted file mode 100644
index c78576b7..00000000
--- a/vendor/github.com/distribution/reference/Makefile
+++ /dev/null
@@ -1,25 +0,0 @@
-# Project packages.
-PACKAGES=$(shell go list ./...)
-
-# Flags passed to `go test`
-BUILDFLAGS ?=
-TESTFLAGS ?=
-
-.PHONY: all build test coverage
-.DEFAULT: all
-
-all: build
-
-build: ## no binaries to build, so just check compilation suceeds
- go build ${BUILDFLAGS} ./...
-
-test: ## run tests
- go test ${TESTFLAGS} ./...
-
-coverage: ## generate coverprofiles from the unit tests
- rm -f coverage.txt
- go test ${TESTFLAGS} -cover -coverprofile=cover.out ./...
-
-.PHONY: help
-help:
- @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m\033[0m\n"} /^[a-zA-Z_\/%-]+:.*?##/ { printf " \033[36m%-27s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST)
diff --git a/vendor/github.com/distribution/reference/README.md b/vendor/github.com/distribution/reference/README.md
deleted file mode 100644
index 172a02e0..00000000
--- a/vendor/github.com/distribution/reference/README.md
+++ /dev/null
@@ -1,30 +0,0 @@
-# Distribution reference
-
-Go library to handle references to container images.
-
-
-
-[](https://github.com/distribution/reference/actions?query=workflow%3ACI)
-[](https://pkg.go.dev/github.com/distribution/reference)
-[](LICENSE)
-[](https://codecov.io/gh/distribution/reference)
-[](https://app.fossa.com/projects/custom%2B162%2Fgithub.com%2Fdistribution%2Freference?ref=badge_shield)
-
-This repository contains a library for handling references to container images held in container registries. Please see [godoc](https://pkg.go.dev/github.com/distribution/reference) for details.
-
-## Contribution
-
-Please see [CONTRIBUTING.md](CONTRIBUTING.md) for details on how to contribute
-issues, fixes, and patches to this project.
-
-## Communication
-
-For async communication and long running discussions please use issues and pull requests on the github repo.
-This will be the best place to discuss design and implementation.
-
-For sync communication we have a #distribution channel in the [CNCF Slack](https://slack.cncf.io/)
-that everyone is welcome to join and chat about development.
-
-## Licenses
-
-The distribution codebase is released under the [Apache 2.0 license](LICENSE).
diff --git a/vendor/github.com/distribution/reference/SECURITY.md b/vendor/github.com/distribution/reference/SECURITY.md
deleted file mode 100644
index aaf983c0..00000000
--- a/vendor/github.com/distribution/reference/SECURITY.md
+++ /dev/null
@@ -1,7 +0,0 @@
-# Security Policy
-
-## Reporting a Vulnerability
-
-The maintainers take security seriously. If you discover a security issue, please bring it to their attention right away!
-
-Please DO NOT file a public issue, instead send your report privately to cncf-distribution-security@lists.cncf.io.
diff --git a/vendor/github.com/distribution/reference/distribution-logo.svg b/vendor/github.com/distribution/reference/distribution-logo.svg
deleted file mode 100644
index cc9f4073..00000000
--- a/vendor/github.com/distribution/reference/distribution-logo.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/vendor/github.com/distribution/reference/helpers.go b/vendor/github.com/distribution/reference/helpers.go
deleted file mode 100644
index d10c7ef8..00000000
--- a/vendor/github.com/distribution/reference/helpers.go
+++ /dev/null
@@ -1,42 +0,0 @@
-package reference
-
-import "path"
-
-// IsNameOnly returns true if reference only contains a repo name.
-func IsNameOnly(ref Named) bool {
- if _, ok := ref.(NamedTagged); ok {
- return false
- }
- if _, ok := ref.(Canonical); ok {
- return false
- }
- return true
-}
-
-// FamiliarName returns the familiar name string
-// for the given named, familiarizing if needed.
-func FamiliarName(ref Named) string {
- if nn, ok := ref.(normalizedNamed); ok {
- return nn.Familiar().Name()
- }
- return ref.Name()
-}
-
-// FamiliarString returns the familiar string representation
-// for the given reference, familiarizing if needed.
-func FamiliarString(ref Reference) string {
- if nn, ok := ref.(normalizedNamed); ok {
- return nn.Familiar().String()
- }
- return ref.String()
-}
-
-// FamiliarMatch reports whether ref matches the specified pattern.
-// See [path.Match] for supported patterns.
-func FamiliarMatch(pattern string, ref Reference) (bool, error) {
- matched, err := path.Match(pattern, FamiliarString(ref))
- if namedRef, isNamed := ref.(Named); isNamed && !matched {
- matched, _ = path.Match(pattern, FamiliarName(namedRef))
- }
- return matched, err
-}
diff --git a/vendor/github.com/distribution/reference/normalize.go b/vendor/github.com/distribution/reference/normalize.go
deleted file mode 100644
index f4128314..00000000
--- a/vendor/github.com/distribution/reference/normalize.go
+++ /dev/null
@@ -1,255 +0,0 @@
-package reference
-
-import (
- "fmt"
- "strings"
-
- "github.com/opencontainers/go-digest"
-)
-
-const (
- // legacyDefaultDomain is the legacy domain for Docker Hub (which was
- // originally named "the Docker Index"). This domain is still used for
- // authentication and image search, which were part of the "v1" Docker
- // registry specification.
- //
- // This domain will continue to be supported, but there are plans to consolidate
- // legacy domains to new "canonical" domains. Once those domains are decided
- // on, we must update the normalization functions, but preserve compatibility
- // with existing installs, clients, and user configuration.
- legacyDefaultDomain = "index.docker.io"
-
- // defaultDomain is the default domain used for images on Docker Hub.
- // It is used to normalize "familiar" names to canonical names, for example,
- // to convert "ubuntu" to "docker.io/library/ubuntu:latest".
- //
- // Note that actual domain of Docker Hub's registry is registry-1.docker.io.
- // This domain will continue to be supported, but there are plans to consolidate
- // legacy domains to new "canonical" domains. Once those domains are decided
- // on, we must update the normalization functions, but preserve compatibility
- // with existing installs, clients, and user configuration.
- defaultDomain = "docker.io"
-
- // officialRepoPrefix is the namespace used for official images on Docker Hub.
- // It is used to normalize "familiar" names to canonical names, for example,
- // to convert "ubuntu" to "docker.io/library/ubuntu:latest".
- officialRepoPrefix = "library/"
-
- // defaultTag is the default tag if no tag is provided.
- defaultTag = "latest"
-)
-
-// normalizedNamed represents a name which has been
-// normalized and has a familiar form. A familiar name
-// is what is used in Docker UI. An example normalized
-// name is "docker.io/library/ubuntu" and corresponding
-// familiar name of "ubuntu".
-type normalizedNamed interface {
- Named
- Familiar() Named
-}
-
-// ParseNormalizedNamed parses a string into a named reference
-// transforming a familiar name from Docker UI to a fully
-// qualified reference. If the value may be an identifier
-// use ParseAnyReference.
-func ParseNormalizedNamed(s string) (Named, error) {
- if ok := anchoredIdentifierRegexp.MatchString(s); ok {
- return nil, fmt.Errorf("invalid repository name (%s), cannot specify 64-byte hexadecimal strings", s)
- }
- domain, remainder := splitDockerDomain(s)
- var remote string
- if tagSep := strings.IndexRune(remainder, ':'); tagSep > -1 {
- remote = remainder[:tagSep]
- } else {
- remote = remainder
- }
- if strings.ToLower(remote) != remote {
- return nil, fmt.Errorf("invalid reference format: repository name (%s) must be lowercase", remote)
- }
-
- ref, err := Parse(domain + "/" + remainder)
- if err != nil {
- return nil, err
- }
- named, isNamed := ref.(Named)
- if !isNamed {
- return nil, fmt.Errorf("reference %s has no name", ref.String())
- }
- return named, nil
-}
-
-// namedTaggedDigested is a reference that has both a tag and a digest.
-type namedTaggedDigested interface {
- NamedTagged
- Digested
-}
-
-// ParseDockerRef normalizes the image reference following the docker convention,
-// which allows for references to contain both a tag and a digest. It returns a
-// reference that is either tagged or digested. For references containing both
-// a tag and a digest, it returns a digested reference. For example, the following
-// reference:
-//
-// docker.io/library/busybox:latest@sha256:7cc4b5aefd1d0cadf8d97d4350462ba51c694ebca145b08d7d41b41acc8db5aa
-//
-// Is returned as a digested reference (with the ":latest" tag removed):
-//
-// docker.io/library/busybox@sha256:7cc4b5aefd1d0cadf8d97d4350462ba51c694ebca145b08d7d41b41acc8db5aa
-//
-// References that are already "tagged" or "digested" are returned unmodified:
-//
-// // Already a digested reference
-// docker.io/library/busybox@sha256:7cc4b5aefd1d0cadf8d97d4350462ba51c694ebca145b08d7d41b41acc8db5aa
-//
-// // Already a named reference
-// docker.io/library/busybox:latest
-func ParseDockerRef(ref string) (Named, error) {
- named, err := ParseNormalizedNamed(ref)
- if err != nil {
- return nil, err
- }
- if canonical, ok := named.(namedTaggedDigested); ok {
- // The reference is both tagged and digested; only return digested.
- newNamed, err := WithName(canonical.Name())
- if err != nil {
- return nil, err
- }
- return WithDigest(newNamed, canonical.Digest())
- }
- return TagNameOnly(named), nil
-}
-
-// splitDockerDomain splits a repository name to domain and remote-name.
-// If no valid domain is found, the default domain is used. Repository name
-// needs to be already validated before.
-func splitDockerDomain(name string) (domain, remoteName string) {
- maybeDomain, maybeRemoteName, ok := strings.Cut(name, "/")
- if !ok {
- // Fast-path for single element ("familiar" names), such as "ubuntu"
- // or "ubuntu:latest". Familiar names must be handled separately, to
- // prevent them from being handled as "hostname:port".
- //
- // Canonicalize them as "docker.io/library/name[:tag]"
-
- // FIXME(thaJeztah): account for bare "localhost" or "example.com" names, which SHOULD be considered a domain.
- return defaultDomain, officialRepoPrefix + name
- }
-
- switch {
- case maybeDomain == localhost:
- // localhost is a reserved namespace and always considered a domain.
- domain, remoteName = maybeDomain, maybeRemoteName
- case maybeDomain == legacyDefaultDomain:
- // canonicalize the Docker Hub and legacy "Docker Index" domains.
- domain, remoteName = defaultDomain, maybeRemoteName
- case strings.ContainsAny(maybeDomain, ".:"):
- // Likely a domain or IP-address:
- //
- // - contains a "." (e.g., "example.com" or "127.0.0.1")
- // - contains a ":" (e.g., "example:5000", "::1", or "[::1]:5000")
- domain, remoteName = maybeDomain, maybeRemoteName
- case strings.ToLower(maybeDomain) != maybeDomain:
- // Uppercase namespaces are not allowed, so if the first element
- // is not lowercase, we assume it to be a domain-name.
- domain, remoteName = maybeDomain, maybeRemoteName
- default:
- // None of the above: it's not a domain, so use the default, and
- // use the name input the remote-name.
- domain, remoteName = defaultDomain, name
- }
-
- if domain == defaultDomain && !strings.ContainsRune(remoteName, '/') {
- // Canonicalize "familiar" names, but only on Docker Hub, not
- // on other domains:
- //
- // "docker.io/ubuntu[:tag]" => "docker.io/library/ubuntu[:tag]"
- remoteName = officialRepoPrefix + remoteName
- }
-
- return domain, remoteName
-}
-
-// familiarizeName returns a shortened version of the name familiar
-// to the Docker UI. Familiar names have the default domain
-// "docker.io" and "library/" repository prefix removed.
-// For example, "docker.io/library/redis" will have the familiar
-// name "redis" and "docker.io/dmcgowan/myapp" will be "dmcgowan/myapp".
-// Returns a familiarized named only reference.
-func familiarizeName(named namedRepository) repository {
- repo := repository{
- domain: named.Domain(),
- path: named.Path(),
- }
-
- if repo.domain == defaultDomain {
- repo.domain = ""
- // Handle official repositories which have the pattern "library/"
- if strings.HasPrefix(repo.path, officialRepoPrefix) {
- // TODO(thaJeztah): this check may be too strict, as it assumes the
- // "library/" namespace does not have nested namespaces. While this
- // is true (currently), technically it would be possible for Docker
- // Hub to use those (e.g. "library/distros/ubuntu:latest").
- // See https://github.com/distribution/distribution/pull/3769#issuecomment-1302031785.
- if remainder := strings.TrimPrefix(repo.path, officialRepoPrefix); !strings.ContainsRune(remainder, '/') {
- repo.path = remainder
- }
- }
- }
- return repo
-}
-
-func (r reference) Familiar() Named {
- return reference{
- namedRepository: familiarizeName(r.namedRepository),
- tag: r.tag,
- digest: r.digest,
- }
-}
-
-func (r repository) Familiar() Named {
- return familiarizeName(r)
-}
-
-func (t taggedReference) Familiar() Named {
- return taggedReference{
- namedRepository: familiarizeName(t.namedRepository),
- tag: t.tag,
- }
-}
-
-func (c canonicalReference) Familiar() Named {
- return canonicalReference{
- namedRepository: familiarizeName(c.namedRepository),
- digest: c.digest,
- }
-}
-
-// TagNameOnly adds the default tag "latest" to a reference if it only has
-// a repo name.
-func TagNameOnly(ref Named) Named {
- if IsNameOnly(ref) {
- namedTagged, err := WithTag(ref, defaultTag)
- if err != nil {
- // Default tag must be valid, to create a NamedTagged
- // type with non-validated input the WithTag function
- // should be used instead
- panic(err)
- }
- return namedTagged
- }
- return ref
-}
-
-// ParseAnyReference parses a reference string as a possible identifier,
-// full digest, or familiar name.
-func ParseAnyReference(ref string) (Reference, error) {
- if ok := anchoredIdentifierRegexp.MatchString(ref); ok {
- return digestReference("sha256:" + ref), nil
- }
- if dgst, err := digest.Parse(ref); err == nil {
- return digestReference(dgst), nil
- }
-
- return ParseNormalizedNamed(ref)
-}
diff --git a/vendor/github.com/distribution/reference/reference.go b/vendor/github.com/distribution/reference/reference.go
deleted file mode 100644
index 900398bd..00000000
--- a/vendor/github.com/distribution/reference/reference.go
+++ /dev/null
@@ -1,432 +0,0 @@
-// Package reference provides a general type to represent any way of referencing images within the registry.
-// Its main purpose is to abstract tags and digests (content-addressable hash).
-//
-// Grammar
-//
-// reference := name [ ":" tag ] [ "@" digest ]
-// name := [domain '/'] remote-name
-// domain := host [':' port-number]
-// host := domain-name | IPv4address | \[ IPv6address \] ; rfc3986 appendix-A
-// domain-name := domain-component ['.' domain-component]*
-// domain-component := /([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])/
-// port-number := /[0-9]+/
-// path-component := alpha-numeric [separator alpha-numeric]*
-// path (or "remote-name") := path-component ['/' path-component]*
-// alpha-numeric := /[a-z0-9]+/
-// separator := /[_.]|__|[-]*/
-//
-// tag := /[\w][\w.-]{0,127}/
-//
-// digest := digest-algorithm ":" digest-hex
-// digest-algorithm := digest-algorithm-component [ digest-algorithm-separator digest-algorithm-component ]*
-// digest-algorithm-separator := /[+.-_]/
-// digest-algorithm-component := /[A-Za-z][A-Za-z0-9]*/
-// digest-hex := /[0-9a-fA-F]{32,}/ ; At least 128 bit digest value
-//
-// identifier := /[a-f0-9]{64}/
-package reference
-
-import (
- "errors"
- "fmt"
- "strings"
-
- "github.com/opencontainers/go-digest"
-)
-
-const (
- // RepositoryNameTotalLengthMax is the maximum total number of characters in a repository name.
- RepositoryNameTotalLengthMax = 255
-
- // NameTotalLengthMax is the maximum total number of characters in a repository name.
- //
- // Deprecated: use [RepositoryNameTotalLengthMax] instead.
- NameTotalLengthMax = RepositoryNameTotalLengthMax
-)
-
-var (
- // ErrReferenceInvalidFormat represents an error while trying to parse a string as a reference.
- ErrReferenceInvalidFormat = errors.New("invalid reference format")
-
- // ErrTagInvalidFormat represents an error while trying to parse a string as a tag.
- ErrTagInvalidFormat = errors.New("invalid tag format")
-
- // ErrDigestInvalidFormat represents an error while trying to parse a string as a tag.
- ErrDigestInvalidFormat = errors.New("invalid digest format")
-
- // ErrNameContainsUppercase is returned for invalid repository names that contain uppercase characters.
- ErrNameContainsUppercase = errors.New("repository name must be lowercase")
-
- // ErrNameEmpty is returned for empty, invalid repository names.
- ErrNameEmpty = errors.New("repository name must have at least one component")
-
- // ErrNameTooLong is returned when a repository name is longer than RepositoryNameTotalLengthMax.
- ErrNameTooLong = fmt.Errorf("repository name must not be more than %v characters", RepositoryNameTotalLengthMax)
-
- // ErrNameNotCanonical is returned when a name is not canonical.
- ErrNameNotCanonical = errors.New("repository name must be canonical")
-)
-
-// Reference is an opaque object reference identifier that may include
-// modifiers such as a hostname, name, tag, and digest.
-type Reference interface {
- // String returns the full reference
- String() string
-}
-
-// Field provides a wrapper type for resolving correct reference types when
-// working with encoding.
-type Field struct {
- reference Reference
-}
-
-// AsField wraps a reference in a Field for encoding.
-func AsField(reference Reference) Field {
- return Field{reference}
-}
-
-// Reference unwraps the reference type from the field to
-// return the Reference object. This object should be
-// of the appropriate type to further check for different
-// reference types.
-func (f Field) Reference() Reference {
- return f.reference
-}
-
-// MarshalText serializes the field to byte text which
-// is the string of the reference.
-func (f Field) MarshalText() (p []byte, err error) {
- return []byte(f.reference.String()), nil
-}
-
-// UnmarshalText parses text bytes by invoking the
-// reference parser to ensure the appropriately
-// typed reference object is wrapped by field.
-func (f *Field) UnmarshalText(p []byte) error {
- r, err := Parse(string(p))
- if err != nil {
- return err
- }
-
- f.reference = r
- return nil
-}
-
-// Named is an object with a full name
-type Named interface {
- Reference
- Name() string
-}
-
-// Tagged is an object which has a tag
-type Tagged interface {
- Reference
- Tag() string
-}
-
-// NamedTagged is an object including a name and tag.
-type NamedTagged interface {
- Named
- Tag() string
-}
-
-// Digested is an object which has a digest
-// in which it can be referenced by
-type Digested interface {
- Reference
- Digest() digest.Digest
-}
-
-// Canonical reference is an object with a fully unique
-// name including a name with domain and digest
-type Canonical interface {
- Named
- Digest() digest.Digest
-}
-
-// namedRepository is a reference to a repository with a name.
-// A namedRepository has both domain and path components.
-type namedRepository interface {
- Named
- Domain() string
- Path() string
-}
-
-// Domain returns the domain part of the [Named] reference.
-func Domain(named Named) string {
- if r, ok := named.(namedRepository); ok {
- return r.Domain()
- }
- domain, _ := splitDomain(named.Name())
- return domain
-}
-
-// Path returns the name without the domain part of the [Named] reference.
-func Path(named Named) (name string) {
- if r, ok := named.(namedRepository); ok {
- return r.Path()
- }
- _, path := splitDomain(named.Name())
- return path
-}
-
-// splitDomain splits a named reference into a hostname and path string.
-// If no valid hostname is found, the hostname is empty and the full value
-// is returned as name
-func splitDomain(name string) (string, string) {
- match := anchoredNameRegexp.FindStringSubmatch(name)
- if len(match) != 3 {
- return "", name
- }
- return match[1], match[2]
-}
-
-// Parse parses s and returns a syntactically valid Reference.
-// If an error was encountered it is returned, along with a nil Reference.
-func Parse(s string) (Reference, error) {
- matches := ReferenceRegexp.FindStringSubmatch(s)
- if matches == nil {
- if s == "" {
- return nil, ErrNameEmpty
- }
- if ReferenceRegexp.FindStringSubmatch(strings.ToLower(s)) != nil {
- return nil, ErrNameContainsUppercase
- }
- return nil, ErrReferenceInvalidFormat
- }
-
- var repo repository
-
- nameMatch := anchoredNameRegexp.FindStringSubmatch(matches[1])
- if len(nameMatch) == 3 {
- repo.domain = nameMatch[1]
- repo.path = nameMatch[2]
- } else {
- repo.domain = ""
- repo.path = matches[1]
- }
-
- if len(repo.path) > RepositoryNameTotalLengthMax {
- return nil, ErrNameTooLong
- }
-
- ref := reference{
- namedRepository: repo,
- tag: matches[2],
- }
- if matches[3] != "" {
- var err error
- ref.digest, err = digest.Parse(matches[3])
- if err != nil {
- return nil, err
- }
- }
-
- r := getBestReferenceType(ref)
- if r == nil {
- return nil, ErrNameEmpty
- }
-
- return r, nil
-}
-
-// ParseNamed parses s and returns a syntactically valid reference implementing
-// the Named interface. The reference must have a name and be in the canonical
-// form, otherwise an error is returned.
-// If an error was encountered it is returned, along with a nil Reference.
-func ParseNamed(s string) (Named, error) {
- named, err := ParseNormalizedNamed(s)
- if err != nil {
- return nil, err
- }
- if named.String() != s {
- return nil, ErrNameNotCanonical
- }
- return named, nil
-}
-
-// WithName returns a named object representing the given string. If the input
-// is invalid ErrReferenceInvalidFormat will be returned.
-func WithName(name string) (Named, error) {
- match := anchoredNameRegexp.FindStringSubmatch(name)
- if match == nil || len(match) != 3 {
- return nil, ErrReferenceInvalidFormat
- }
-
- if len(match[2]) > RepositoryNameTotalLengthMax {
- return nil, ErrNameTooLong
- }
-
- return repository{
- domain: match[1],
- path: match[2],
- }, nil
-}
-
-// WithTag combines the name from "name" and the tag from "tag" to form a
-// reference incorporating both the name and the tag.
-func WithTag(name Named, tag string) (NamedTagged, error) {
- if !anchoredTagRegexp.MatchString(tag) {
- return nil, ErrTagInvalidFormat
- }
- var repo repository
- if r, ok := name.(namedRepository); ok {
- repo.domain = r.Domain()
- repo.path = r.Path()
- } else {
- repo.path = name.Name()
- }
- if canonical, ok := name.(Canonical); ok {
- return reference{
- namedRepository: repo,
- tag: tag,
- digest: canonical.Digest(),
- }, nil
- }
- return taggedReference{
- namedRepository: repo,
- tag: tag,
- }, nil
-}
-
-// WithDigest combines the name from "name" and the digest from "digest" to form
-// a reference incorporating both the name and the digest.
-func WithDigest(name Named, digest digest.Digest) (Canonical, error) {
- if !anchoredDigestRegexp.MatchString(digest.String()) {
- return nil, ErrDigestInvalidFormat
- }
- var repo repository
- if r, ok := name.(namedRepository); ok {
- repo.domain = r.Domain()
- repo.path = r.Path()
- } else {
- repo.path = name.Name()
- }
- if tagged, ok := name.(Tagged); ok {
- return reference{
- namedRepository: repo,
- tag: tagged.Tag(),
- digest: digest,
- }, nil
- }
- return canonicalReference{
- namedRepository: repo,
- digest: digest,
- }, nil
-}
-
-// TrimNamed removes any tag or digest from the named reference.
-func TrimNamed(ref Named) Named {
- repo := repository{}
- if r, ok := ref.(namedRepository); ok {
- repo.domain, repo.path = r.Domain(), r.Path()
- } else {
- repo.domain, repo.path = splitDomain(ref.Name())
- }
- return repo
-}
-
-func getBestReferenceType(ref reference) Reference {
- if ref.Name() == "" {
- // Allow digest only references
- if ref.digest != "" {
- return digestReference(ref.digest)
- }
- return nil
- }
- if ref.tag == "" {
- if ref.digest != "" {
- return canonicalReference{
- namedRepository: ref.namedRepository,
- digest: ref.digest,
- }
- }
- return ref.namedRepository
- }
- if ref.digest == "" {
- return taggedReference{
- namedRepository: ref.namedRepository,
- tag: ref.tag,
- }
- }
-
- return ref
-}
-
-type reference struct {
- namedRepository
- tag string
- digest digest.Digest
-}
-
-func (r reference) String() string {
- return r.Name() + ":" + r.tag + "@" + r.digest.String()
-}
-
-func (r reference) Tag() string {
- return r.tag
-}
-
-func (r reference) Digest() digest.Digest {
- return r.digest
-}
-
-type repository struct {
- domain string
- path string
-}
-
-func (r repository) String() string {
- return r.Name()
-}
-
-func (r repository) Name() string {
- if r.domain == "" {
- return r.path
- }
- return r.domain + "/" + r.path
-}
-
-func (r repository) Domain() string {
- return r.domain
-}
-
-func (r repository) Path() string {
- return r.path
-}
-
-type digestReference digest.Digest
-
-func (d digestReference) String() string {
- return digest.Digest(d).String()
-}
-
-func (d digestReference) Digest() digest.Digest {
- return digest.Digest(d)
-}
-
-type taggedReference struct {
- namedRepository
- tag string
-}
-
-func (t taggedReference) String() string {
- return t.Name() + ":" + t.tag
-}
-
-func (t taggedReference) Tag() string {
- return t.tag
-}
-
-type canonicalReference struct {
- namedRepository
- digest digest.Digest
-}
-
-func (c canonicalReference) String() string {
- return c.Name() + "@" + c.digest.String()
-}
-
-func (c canonicalReference) Digest() digest.Digest {
- return c.digest
-}
diff --git a/vendor/github.com/distribution/reference/regexp.go b/vendor/github.com/distribution/reference/regexp.go
deleted file mode 100644
index 65bc49d7..00000000
--- a/vendor/github.com/distribution/reference/regexp.go
+++ /dev/null
@@ -1,163 +0,0 @@
-package reference
-
-import (
- "regexp"
- "strings"
-)
-
-// DigestRegexp matches well-formed digests, including algorithm (e.g. "sha256:").
-var DigestRegexp = regexp.MustCompile(digestPat)
-
-// DomainRegexp matches hostname or IP-addresses, optionally including a port
-// number. It defines the structure of potential domain components that may be
-// part of image names. This is purposely a subset of what is allowed by DNS to
-// ensure backwards compatibility with Docker image names. It may be a subset of
-// DNS domain name, an IPv4 address in decimal format, or an IPv6 address between
-// square brackets (excluding zone identifiers as defined by [RFC 6874] or special
-// addresses such as IPv4-Mapped).
-//
-// [RFC 6874]: https://www.rfc-editor.org/rfc/rfc6874.
-var DomainRegexp = regexp.MustCompile(domainAndPort)
-
-// IdentifierRegexp is the format for string identifier used as a
-// content addressable identifier using sha256. These identifiers
-// are like digests without the algorithm, since sha256 is used.
-var IdentifierRegexp = regexp.MustCompile(identifier)
-
-// NameRegexp is the format for the name component of references, including
-// an optional domain and port, but without tag or digest suffix.
-var NameRegexp = regexp.MustCompile(namePat)
-
-// ReferenceRegexp is the full supported format of a reference. The regexp
-// is anchored and has capturing groups for name, tag, and digest
-// components.
-var ReferenceRegexp = regexp.MustCompile(referencePat)
-
-// TagRegexp matches valid tag names. From [docker/docker:graph/tags.go].
-//
-// [docker/docker:graph/tags.go]: https://github.com/moby/moby/blob/v1.6.0/graph/tags.go#L26-L28
-var TagRegexp = regexp.MustCompile(tag)
-
-const (
- // alphanumeric defines the alphanumeric atom, typically a
- // component of names. This only allows lower case characters and digits.
- alphanumeric = `[a-z0-9]+`
-
- // separator defines the separators allowed to be embedded in name
- // components. This allows one period, one or two underscore and multiple
- // dashes. Repeated dashes and underscores are intentionally treated
- // differently. In order to support valid hostnames as name components,
- // supporting repeated dash was added. Additionally double underscore is
- // now allowed as a separator to loosen the restriction for previously
- // supported names.
- separator = `(?:[._]|__|[-]+)`
-
- // localhost is treated as a special value for domain-name. Any other
- // domain-name without a "." or a ":port" are considered a path component.
- localhost = `localhost`
-
- // domainNameComponent restricts the registry domain component of a
- // repository name to start with a component as defined by DomainRegexp.
- domainNameComponent = `(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])`
-
- // optionalPort matches an optional port-number including the port separator
- // (e.g. ":80").
- optionalPort = `(?::[0-9]+)?`
-
- // tag matches valid tag names. From docker/docker:graph/tags.go.
- tag = `[\w][\w.-]{0,127}`
-
- // digestPat matches well-formed digests, including algorithm (e.g. "sha256:").
- //
- // TODO(thaJeztah): this should follow the same rules as https://pkg.go.dev/github.com/opencontainers/go-digest@v1.0.0#DigestRegexp
- // so that go-digest defines the canonical format. Note that the go-digest is
- // more relaxed:
- // - it allows multiple algorithms (e.g. "sha256+b64:") to allow
- // future expansion of supported algorithms.
- // - it allows the "" value to use urlsafe base64 encoding as defined
- // in [rfc4648, section 5].
- //
- // [rfc4648, section 5]: https://www.rfc-editor.org/rfc/rfc4648#section-5.
- digestPat = `[A-Za-z][A-Za-z0-9]*(?:[-_+.][A-Za-z][A-Za-z0-9]*)*[:][[:xdigit:]]{32,}`
-
- // identifier is the format for a content addressable identifier using sha256.
- // These identifiers are like digests without the algorithm, since sha256 is used.
- identifier = `([a-f0-9]{64})`
-
- // ipv6address are enclosed between square brackets and may be represented
- // in many ways, see rfc5952. Only IPv6 in compressed or uncompressed format
- // are allowed, IPv6 zone identifiers (rfc6874) or Special addresses such as
- // IPv4-Mapped are deliberately excluded.
- ipv6address = `\[(?:[a-fA-F0-9:]+)\]`
-)
-
-var (
- // domainName defines the structure of potential domain components
- // that may be part of image names. This is purposely a subset of what is
- // allowed by DNS to ensure backwards compatibility with Docker image
- // names. This includes IPv4 addresses on decimal format.
- domainName = domainNameComponent + anyTimes(`\.`+domainNameComponent)
-
- // host defines the structure of potential domains based on the URI
- // Host subcomponent on rfc3986. It may be a subset of DNS domain name,
- // or an IPv4 address in decimal format, or an IPv6 address between square
- // brackets (excluding zone identifiers as defined by rfc6874 or special
- // addresses such as IPv4-Mapped).
- host = `(?:` + domainName + `|` + ipv6address + `)`
-
- // allowed by the URI Host subcomponent on rfc3986 to ensure backwards
- // compatibility with Docker image names.
- domainAndPort = host + optionalPort
-
- // anchoredTagRegexp matches valid tag names, anchored at the start and
- // end of the matched string.
- anchoredTagRegexp = regexp.MustCompile(anchored(tag))
-
- // anchoredDigestRegexp matches valid digests, anchored at the start and
- // end of the matched string.
- anchoredDigestRegexp = regexp.MustCompile(anchored(digestPat))
-
- // pathComponent restricts path-components to start with an alphanumeric
- // character, with following parts able to be separated by a separator
- // (one period, one or two underscore and multiple dashes).
- pathComponent = alphanumeric + anyTimes(separator+alphanumeric)
-
- // remoteName matches the remote-name of a repository. It consists of one
- // or more forward slash (/) delimited path-components:
- //
- // pathComponent[[/pathComponent] ...] // e.g., "library/ubuntu"
- remoteName = pathComponent + anyTimes(`/`+pathComponent)
- namePat = optional(domainAndPort+`/`) + remoteName
-
- // anchoredNameRegexp is used to parse a name value, capturing the
- // domain and trailing components.
- anchoredNameRegexp = regexp.MustCompile(anchored(optional(capture(domainAndPort), `/`), capture(remoteName)))
-
- referencePat = anchored(capture(namePat), optional(`:`, capture(tag)), optional(`@`, capture(digestPat)))
-
- // anchoredIdentifierRegexp is used to check or match an
- // identifier value, anchored at start and end of string.
- anchoredIdentifierRegexp = regexp.MustCompile(anchored(identifier))
-)
-
-// optional wraps the expression in a non-capturing group and makes the
-// production optional.
-func optional(res ...string) string {
- return `(?:` + strings.Join(res, "") + `)?`
-}
-
-// anyTimes wraps the expression in a non-capturing group that can occur
-// any number of times.
-func anyTimes(res ...string) string {
- return `(?:` + strings.Join(res, "") + `)*`
-}
-
-// capture wraps the expression in a capturing group.
-func capture(res ...string) string {
- return `(` + strings.Join(res, "") + `)`
-}
-
-// anchored anchors the regular expression by adding start and end delimiters.
-func anchored(res ...string) string {
- return `^` + strings.Join(res, "") + `$`
-}
diff --git a/vendor/github.com/distribution/reference/sort.go b/vendor/github.com/distribution/reference/sort.go
deleted file mode 100644
index 416c37b0..00000000
--- a/vendor/github.com/distribution/reference/sort.go
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- Copyright The containerd Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-package reference
-
-import (
- "sort"
-)
-
-// Sort sorts string references preferring higher information references.
-//
-// The precedence is as follows:
-//
-// 1. [Named] + [Tagged] + [Digested] (e.g., "docker.io/library/busybox:latest@sha256:")
-// 2. [Named] + [Tagged] (e.g., "docker.io/library/busybox:latest")
-// 3. [Named] + [Digested] (e.g., "docker.io/library/busybo@sha256:")
-// 4. [Named] (e.g., "docker.io/library/busybox")
-// 5. [Digested] (e.g., "docker.io@sha256:")
-// 6. Parse error
-func Sort(references []string) []string {
- var prefs []Reference
- var bad []string
-
- for _, ref := range references {
- pref, err := ParseAnyReference(ref)
- if err != nil {
- bad = append(bad, ref)
- } else {
- prefs = append(prefs, pref)
- }
- }
- sort.Slice(prefs, func(a, b int) bool {
- ar := refRank(prefs[a])
- br := refRank(prefs[b])
- if ar == br {
- return prefs[a].String() < prefs[b].String()
- }
- return ar < br
- })
- sort.Strings(bad)
- var refs []string
- for _, pref := range prefs {
- refs = append(refs, pref.String())
- }
- return append(refs, bad...)
-}
-
-func refRank(ref Reference) uint8 {
- if _, ok := ref.(Named); ok {
- if _, ok = ref.(Tagged); ok {
- if _, ok = ref.(Digested); ok {
- return 1
- }
- return 2
- }
- if _, ok = ref.(Digested); ok {
- return 3
- }
- return 4
- }
- return 5
-}
diff --git a/vendor/github.com/docker/cli/AUTHORS b/vendor/github.com/docker/cli/AUTHORS
deleted file mode 100644
index ad1abd49..00000000
--- a/vendor/github.com/docker/cli/AUTHORS
+++ /dev/null
@@ -1,910 +0,0 @@
-# File @generated by scripts/docs/generate-authors.sh. DO NOT EDIT.
-# This file lists all contributors to the repository.
-# See scripts/docs/generate-authors.sh to make modifications.
-
-A. Lester Buck III
-Aanand Prasad
-Aaron L. Xu
-Aaron Lehmann
-Aaron.L.Xu
-Abdur Rehman
-Abhinandan Prativadi
-Abin Shahab
-Abreto FU
-Ace Tang
-Addam Hardy
-Adolfo Ochagavía
-Adrian Plata
-Adrien Duermael
-Adrien Folie
-Adyanth Hosavalike
-Ahmet Alp Balkan
-Aidan Feldman
-Aidan Hobson Sayers
-AJ Bowen
-Akhil Mohan
-Akihiro Suda
-Akim Demaille
-Alan Thompson
-Alano Terblanche
-Albert Callarisa
-Alberto Roura
-Albin Kerouanton
-Aleksa Sarai
-Aleksander Piotrowski
-Alessandro Boch
-Alex Couture-Beil
-Alex Mavrogiannis
-Alex Mayer
-Alexander Boyd
-Alexander Chneerov
-Alexander Larsson
-Alexander Morozov
-Alexander Ryabov
-Alexandre González
-Alexey Igrychev
-Alexis Couvreur
-Alfred Landrum
-Ali Rostami
-Alicia Lauerman
-Allen Sun
-Alvin Deng
-Amen Belayneh
-Amey Shrivastava <72866602+AmeyShrivastava@users.noreply.github.com>
-Amir Goldstein
-Amit Krishnan
-Amit Shukla
-Amy Lindburg
-Anca Iordache
-Anda Xu
-Andrea Luzzardi
-Andreas Köhler
-Andres G. Aragoneses
-Andres Leon Rangel
-Andrew France
-Andrew Hsu
-Andrew Macpherson
-Andrew McDonnell
-Andrew Po
-Andrew-Zipperer
-Andrey Petrov
-Andrii Berehuliak
-André Martins
-Andy Goldstein
-Andy Rothfusz
-Anil Madhavapeddy
-Ankush Agarwal
-Anne Henmi
-Anton Polonskiy
-Antonio Murdaca
-Antonis Kalipetis
-Anusha Ragunathan
-Ao Li
-Arash Deshmeh
-Arko Dasgupta
-Arnaud Porterie
-Arnaud Rebillout
-Arthur Peka
-Ashly Mathew
-Ashwini Oruganti
-Aslam Ahemad
-Azat Khuyiyakhmetov
-Bardia Keyoumarsi
-Barnaby Gray
-Bastiaan Bakker
-BastianHofmann
-Ben Bodenmiller
-Ben Bonnefoy
-Ben Creasy
-Ben Firshman
-Benjamin Boudreau
-Benjamin Böhmke
-Benjamin Nater
-Benoit Sigoure
-Bhumika Bayani
-Bill Wang
-Bin Liu
-Bingshen Wang
-Bishal Das
-Bjorn Neergaard
-Boaz Shuster
-Boban Acimovic
-Bogdan Anton
-Boris Pruessmann
-Brad Baker
-Bradley Cicenas
-Brandon Mitchell
-Brandon Philips
-Brent Salisbury
-Bret Fisher
-Brian (bex) Exelbierd
-Brian Goff
-Brian Tracy
-Brian Wieder
-Bruno Sousa
-Bryan Bess
-Bryan Boreham
-Bryan Murphy
-bryfry
-Calvin Liu
-Cameron Spear
-Cao Weiwei
-Carlo Mion
-Carlos Alexandro Becker
-Carlos de Paula
-Casey Korver
-Ce Gao
-Cedric Davies
-Cezar Sa Espinola
-Chad Faragher
-Chao Wang
-Charles Chan
-Charles Law
-Charles Smith
-Charlie Drage
-Charlotte Mach
-ChaYoung You
-Chee Hau Lim
-Chen Chuanliang
-Chen Hanxiao
-Chen Mingjie
-Chen Qiu
-Chris Chinchilla
-Chris Couzens
-Chris Gavin
-Chris Gibson
-Chris McKinnel
-Chris Snow
-Chris Vermilion
-Chris Weyl
-Christian Persson
-Christian Stefanescu
-Christophe Robin
-Christophe Vidal
-Christopher Biscardi
-Christopher Crone
-Christopher Jones
-Christopher Petito <47751006+krissetto@users.noreply.github.com>
-Christopher Petito
-Christopher Svensson
-Christy Norman
-Chun Chen
-Clinton Kitson
-Coenraad Loubser
-Colin Hebert
-Collin Guarino
-Colm Hally
-Comical Derskeal <27731088+derskeal@users.noreply.github.com>
-Conner Crosby
-Corey Farrell
-Corey Quon
-Cory Bennet
-Cory Snider
-Craig Osterhout
-Craig Wilhite
-Cristian Staretu
-Daehyeok Mun
-Dafydd Crosby
-Daisuke Ito
-dalanlan
-Damien Nadé
-Dan Cotora
-Danial Gharib
-Daniel Artine
-Daniel Cassidy
-Daniel Dao
-Daniel Farrell
-Daniel Gasienica
-Daniel Goosen
-Daniel Helfand
-Daniel Hiltgen
-Daniel J Walsh
-Daniel Nephin
-Daniel Norberg
-Daniel Watkins
-Daniel Zhang
-Daniil Nikolenko
-Danny Berger
-Darren Shepherd
-Darren Stahl
-Dattatraya Kumbhar
-Dave Goodchild
-Dave Henderson
-Dave Tucker