From 72a4078b3eb0dd242d3eb0fde0c1e622fd30030e Mon Sep 17 00:00:00 2001 From: Aleksandr Britvin Date: Mon, 21 Oct 2024 16:53:54 +0200 Subject: [PATCH] Update logger. Update build process to use Generate. Rearrange code. --- .github/workflows/release.yaml | 68 ++-- .golangci.yaml | 31 +- Makefile | 27 +- README.md | 2 + assets.go | 59 ++++ cmd/launchr/gen.go | 13 + cmd/launchr/launchr.go | 5 +- files.dev.go | 37 -- files.release.go | 31 -- gen.go | 280 +++++++++++++++ go.mod | 66 ++-- go.sum | 501 +++++++++++++++++++++++---- plugin.go | 50 ++- process.go | 13 +- process-unix.go => process_unix.go | 6 +- process-win.go => process_windows.go | 8 +- scripts/prebuild.go | 172 --------- server/api.go | 42 +-- server/openapi.gen.go | 46 +-- server/openapi.yaml | 2 +- server/server.go | 218 +++--------- server/streams.go | 18 +- web-runner.go | 272 --------------- web_runner.go | 356 +++++++++++++++++++ 24 files changed, 1387 insertions(+), 936 deletions(-) create mode 100644 assets.go create mode 100644 cmd/launchr/gen.go delete mode 100644 files.dev.go delete mode 100644 files.release.go create mode 100644 gen.go rename process-unix.go => process_unix.go (76%) rename process-win.go => process_windows.go (78%) delete mode 100644 scripts/prebuild.go delete mode 100644 web-runner.go create mode 100644 web_runner.go diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index f640439..6bf40a6 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -1,58 +1,50 @@ -name: Create Archive on Tag +name: Create release on: push: tags: - - '*' + - "v*.*.*" jobs: - build_and_archive: + create_release: + name: build / push runs-on: ubuntu-latest steps: - name: Checkout repository uses: actions/checkout@v4 - - name: Set up Node.js 21 + - name: Set up Node.js 22 (LTS) uses: actions/setup-node@v4 with: - node-version: '21' + node-version: '22' - - name: Enable Corepack - working-directory: client - run: corepack enable - - - name: Install dependencies - working-directory: client - run: yarn install - - - name: Build project - working-directory: client - run: yarn build - - - name: Create archives of dist folder + - name: Build the artifacts working-directory: client run: | - zip -r dist.zip dist - tar -czvf dist.tar.gz dist + corepack enable + yarn install + yarn build - - name: Upload archives as artifacts - uses: actions/upload-artifact@v4 - with: - name: dist.zip - path: client/dist.zip - - - name: Upload tar.gz as artifact - uses: actions/upload-artifact@v4 - with: - name: dist.tar.gz - path: client/dist.tar.gz + - name: Create web client artifacts + working-directory: client/dist + run: | + tar -czf ../../client-assets.tar.gz * + zip -r ../../client-assets.zip * - - name: Upload dist.zip and dist.tar.gz to the release - uses: svenstaro/upload-release-action@v2 + - name: Determine if prerelease + run: | + TAG="${GITHUB_REF##*/}" + if [[ "$TAG" == *-* ]]; then + echo "PRERELEASE=true" >> $GITHUB_ENV + else + echo "PRERELEASE=false" >> $GITHUB_ENV + fi + + - name: Release + uses: softprops/action-gh-release@v2 with: - file: client/dist.* - file_glob: true - tag: ${{ github.ref_name }} - release_name: ${{ github.ref_name }} - repo_token: ${{ secrets.GITHUB_TOKEN }} + prerelease: ${{ env.PRERELEASE }} + files: | + client-assets.tar.gz + client-assets.zip diff --git a/.golangci.yaml b/.golangci.yaml index 26e5de0..e812364 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -3,25 +3,16 @@ run: deadline: 10s issues-exit-code: 1 tests: true - skip-dirs: - - bin - - vendor - - var - - tmp - skip-files: - - \.pb\.go$ - - \.pb\.goclay\.go$ - - \.gen.go$ - - server/streams.go # @fixme remove when implemented output: - format: colored-line-number + formats: + - format: colored-line-number print-issued-lines: true print-linter-name: true linters-settings: govet: - check-shadowing: true + shadow: true golint: min-confidence: 0 dupl: @@ -29,7 +20,7 @@ linters-settings: goconst: min-len: 2 min-occurrences: 2 - + linters: disable-all: true enable: @@ -43,10 +34,22 @@ linters: - goconst - gosec - goimports - - megacheck # (staticcheck + gosimple + unused in one linter) - enable before push + - gosimple + - staticcheck + - unused issues: exclude-use-default: false + exclude-dirs: + - bin + - vendor + - var + - tmp + exclude-files: + - \.pb\.go$ + - \.pb\.goclay\.go$ + - \.gen.go$ + - server/streams.go # @fixme remove when implemented exclude: # # _ instead of err checks # - G104 diff --git a/Makefile b/Makefile index e971c62..478c29e 100644 --- a/Makefile +++ b/Makefile @@ -1,9 +1,8 @@ -export GOSUMDB=off - GOPATH?=$(HOME)/go FIRST_GOPATH:=$(firstword $(subst :, ,$(GOPATH))) -NODE_TAG=21-alpine3.19 +NODE_TAG=22 # Always use LTS +SWAGGER_UI_DIR:=./swagger-ui # Build available information. GIT_HASH:=$(shell git log --format="%h" -n 1 2> /dev/null) @@ -19,12 +18,6 @@ else LDFLAGS_EXTRA=-s -w BUILD_OPTS=-trimpath endif -DEV?=0 -ifeq ($(DEV), 1) - BUILD_OPTS+=-tags dev -else - BUILD_OPTS+=-tags embed -endif BUILD_ENVPARMS:=CGO_ENABLED=0 @@ -33,25 +26,28 @@ LOCAL_BIN:=$(CURDIR)/bin # Linter config. GOLANGCI_BIN:=$(LOCAL_BIN)/golangci-lint -GOLANGCI_TAG:=1.55.2 - -SWAGGER_UI_DIR:=./swagger-ui +GOLANGCI_TAG:=1.61.0 .PHONY: all -all: deps test build +all: deps front test build # Install go dependencies .PHONY: deps deps: $(info Installing go dependencies...) go mod download + +# Build front dependencies. +.PHONY: front +front: front-install front-build @if [ ! -d "$(SWAGGER_UI_DIR)" ]; then \ echo "Downloading Swagger UI..."; \ curl -Ss https://api.github.com/repos/swagger-api/swagger-ui/releases/latest | grep tarball_url | cut -d '"' -f 4 |\ - xargs curl -LsS -o swagger-ui.tar.gz; \ + xargs curl -LsS -o swagger-ui.tar.gz; \ rm -rf $(SWAGGER_UI_DIR) $(SWAGGER_UI_DIR)-tmp && mkdir $(SWAGGER_UI_DIR)-tmp; \ tar xzf swagger-ui.tar.gz -C $(SWAGGER_UI_DIR)-tmp --strip=1; \ - mv $(SWAGGER_UI_DIR)-tmp/dist $(SWAGGER_UI_DIR) && rm -rf $(SWAGGER_UI_DIR)-tmp && rm swagger-ui.tar.gz; \ + mv $(SWAGGER_UI_DIR)-tmp/dist $(SWAGGER_UI_DIR); \ + rm -rf $(SWAGGER_UI_DIR)-tmp && rm swagger-ui.tar.gz; \ sed -i.bkp "s|https://petstore.swagger.io/v2/swagger.json|/api/swagger.json|g" $(SWAGGER_UI_DIR)/swagger-initializer.js; \ fi @@ -68,6 +64,7 @@ build: # Application related information available on build time. $(eval LDFLAGS:=-X '$(GOPKG).name=launchr' -X '$(GOPKG).version=$(APP_VERSION)' $(LDFLAGS_EXTRA)) $(eval BIN?=$(LOCAL_BIN)/launchr) + go generate ./... $(BUILD_ENVPARMS) go build -ldflags "$(LDFLAGS)" $(BUILD_OPTS) -o $(BIN) ./cmd/launchr # Install launchr diff --git a/README.md b/README.md index c62c8e7..4fb82fe 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,7 @@ bin/launchr web --help The documentation for `launchr` usage can be found in [the main project](https://github.com/launchrctl/launchr). +@todo update readme ## To build the client: ### Node ```shell @@ -79,6 +80,7 @@ Useful make commands: 2. Test the code - `make test` 3. Lint the code - `make lint` +@todo review ## Test for release ```shell diff --git a/assets.go b/assets.go new file mode 100644 index 0000000..5a903cd --- /dev/null +++ b/assets.go @@ -0,0 +1,59 @@ +package web + +import ( + "io/fs" + "os" + "path/filepath" +) + +var clientAssetsFS fs.FS +var swaggerAssetsFS fs.FS + +// SetClientAssetsFS sets the global web client assets filesystem. +func SetClientAssetsFS(f fs.FS) { + clientAssetsFS = f +} + +// SetSwaggerUIAssetsFS sets the filesystem containing the swagger UI assets. +func SetSwaggerUIAssetsFS(f fs.FS) { + swaggerAssetsFS = f +} + +// GetClientAssetsFS returns web client assets. +func GetClientAssetsFS() fs.FS { + if clientAssetsFS != nil { + return clientAssetsFS + } + // If client assets were not set, we are in the development environment. + path := filepath.Join("client", "dist") + _, err := os.Stat(path) + if os.IsNotExist(err) { + panic(path + " assets are not available") + } + SetClientAssetsFS(os.DirFS(path)) + return clientAssetsFS +} + +// GetSwaggerUIAssetsFS returns web assets for swagger-ui. +func GetSwaggerUIAssetsFS() (fs.FS, error) { + if swaggerAssetsFS != nil { + return swaggerAssetsFS, nil + } + // If client assets were not set, we are in the development environment. + path := filepath.Join("swagger-ui") + _, err := os.Stat(path) + if err != nil { + return nil, err + } + SetSwaggerUIAssetsFS(os.DirFS(path)) + return swaggerAssetsFS, nil +} + +// MustSubFS returns fs by subpath. +func MustSubFS(orig fs.FS, path string) fs.FS { + sub, err := fs.Sub(orig, path) + if err != nil { + panic(err) + } + return sub +} diff --git a/cmd/launchr/gen.go b/cmd/launchr/gen.go new file mode 100644 index 0000000..bf1cf0c --- /dev/null +++ b/cmd/launchr/gen.go @@ -0,0 +1,13 @@ +//go:build ignore + +package main + +import ( + "github.com/launchrctl/launchr" + + _ "github.com/launchrctl/web" +) + +func main() { + launchr.GenAndExit() +} diff --git a/cmd/launchr/launchr.go b/cmd/launchr/launchr.go index 94b753b..5592a32 100644 --- a/cmd/launchr/launchr.go +++ b/cmd/launchr/launchr.go @@ -2,12 +2,11 @@ package main import ( - "os" - "github.com/launchrctl/launchr" + _ "github.com/launchrctl/web" ) func main() { - os.Exit(launchr.Run(&launchr.AppOptions{})) + launchr.RunAndExit() } diff --git a/files.dev.go b/files.dev.go deleted file mode 100644 index 03a3789..0000000 --- a/files.dev.go +++ /dev/null @@ -1,37 +0,0 @@ -//go:build dev || embed - -package web - -import ( - "embed" - "io/fs" - - "github.com/launchrctl/web/server" -) - -//go:embed swagger-ui/* -var swaggerUIFS embed.FS - -//go:embed client/dist/* -var clientFS embed.FS - -func prepareRunOption(_ *Plugin, opts *server.RunOptions) { - opts.SwaggerUIFS = defaultSwaggerUIFS() - opts.ClientFS = defaultClientFS() -} - -func defaultSwaggerUIFS() fs.FS { - sub, err := fs.Sub(swaggerUIFS, "swagger-ui") - if err != nil { - panic(err) - } - return sub -} - -func defaultClientFS() fs.FS { - sub, err := fs.Sub(clientFS, "client/dist") - if err != nil { - panic(err) - } - return sub -} diff --git a/files.release.go b/files.release.go deleted file mode 100644 index 5b58e3a..0000000 --- a/files.release.go +++ /dev/null @@ -1,31 +0,0 @@ -//go:build !dev && !embed - -package web - -import ( - "io/fs" - - "github.com/launchrctl/web/server" -) - -func prepareRunOption(p *Plugin, opts *server.RunOptions) { - assetsFs := p.app.GetPluginAssets(p) - opts.SwaggerUIFS = defaultSwaggerUIFS(assetsFs) - opts.ClientFS = defaultClientFS(assetsFs) -} - -func defaultSwaggerUIFS(assets fs.FS) fs.FS { - sub, err := fs.Sub(assets, "swagger-ui") - if err != nil { - panic(err) - } - return sub -} - -func defaultClientFS(assets fs.FS) fs.FS { - sub, err := fs.Sub(assets, "dist") - if err != nil { - panic(err) - } - return sub -} diff --git a/gen.go b/gen.go new file mode 100644 index 0000000..aeb051d --- /dev/null +++ b/gen.go @@ -0,0 +1,280 @@ +package web + +import ( + "archive/tar" + "compress/gzip" + "encoding/json" + "errors" + "fmt" + "io" + "net/http" + "os" + "path/filepath" + "regexp" + "strings" + + "github.com/launchrctl/launchr" +) + +const ( + versionLatest = "latest" + repoName = "launchrctl/web" + + // PkgPath is the plugin module name. + PkgPath = "github.com/" + repoName +) + +// Generate implements [launchr.GeneratePlugin] interface. +func (p *Plugin) Generate(config launchr.GenerateConfig) error { + launchr.Term().Info().Println("Preparing launchrctl/web plugin assets...") + + // Download web client assets. + subdir := "web-plugin" + webPath := filepath.Join(config.BuildDir, subdir) + v := getPluginVersion() + launchr.Log().Debug("web plugin version used in go.mod", "version", v) + err := downloadGithubRelease(webPath, repoName, v) + if err != nil { + return err + } + + // Prepare the generated plugin with embed assets. + launchr.Term().Info().Println("Generating web client embed assets go file") + type templateVars struct { + Pkg string + ClientPath string + SwaggerPath string + } + tpl := launchr.Template{Tmpl: pluginTemplate, Data: templateVars{ + Pkg: PkgPath, + ClientPath: subdir, + }} + err = tpl.WriteFile(filepath.Join(config.BuildDir, "web_assets.gen.go")) + if err != nil { + return err + } + + return nil +} + +func downloadGithubRelease(dir string, project string, version string) error { + // Get release url and download the tarball. + releaseURL, err := getGithubReleaseDownloadURL(project, version) + if err != nil { + return err + } + launchr.Log().Debug("get github release archive stream", "url", releaseURL) + gzippedStream, err := getFileStreamByURL(releaseURL) + if err != nil { + return err + } + defer gzippedStream.Close() + + // Unarchive the release. + launchr.Log().Debug("unarchiving archive from stream", "dir", dir) + err = untar(gzippedStream, untarOptions{ + Destination: dir, + Gzip: true, + }) + if err != nil { + return err + } + + return nil +} + +func getPluginVersion() string { + version := launchr.Version() + branchRelease := regexp.MustCompile(`-0\..*$`) + for _, dep := range version.Plugins { + if strings.HasPrefix(dep, PkgPath+" ") { + // Get version from the string. + i := strings.IndexRune(dep, ' ') + if i == -1 { + panic("incorrect plugin version") + } + v := dep[i+1:] + // 1. Branch releases referenced like `go get repo_url@branch-name` have a version string + // like `v0.1.2-0.[date]-[commit-hash]`. We don't have artifacts for that, don't break and use latest. + // 2. During the development inside the repository, go mod returns (devel). + if len(v) == 0 || v == "(devel)" || branchRelease.MatchString(v) { + return versionLatest + } + return v + } + } + return versionLatest +} + +func getGithubReleaseDownloadURL(repo, version string) (string, error) { + if version != versionLatest { + version = "tags/" + version + } + apiURL := fmt.Sprintf("https://api.github.com/repos/%s/releases/%s", repo, version) + // Get release information. + releaseResp, err := http.Get(apiURL) //nolint G107 // The link is generated above. + if err != nil { + return "", err + } + // Parse release JSON. + defer releaseResp.Body.Close() + body, err := io.ReadAll(releaseResp.Body) + if err != nil { + return "", err + } + type GithubAPIResponse struct { + Assets []struct { + Name string `json:"name"` + DownloadURL string `json:"browser_download_url"` + } + TarballURL string `json:"tarball_url"` + } + var parsedResp GithubAPIResponse + err = json.Unmarshal(body, &parsedResp) + if err != nil { + return "", err + } + for _, asset := range parsedResp.Assets { + if strings.HasSuffix(asset.Name, "tar.gz") { + return asset.DownloadURL, nil + } + } + return parsedResp.TarballURL, nil +} + +func getFileStreamByURL(url string) (io.ReadCloser, error) { + resp, err := http.Get(url) //nolint G107 // The link is generated above. + if err != nil { + return nil, err + } + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("could not download from the url: %s", url) + } + + // Return the body of the file. + return resp.Body, err +} + +type untarOptions struct { + Destination string + PathGlob string + Strip int + Gzip bool +} + +func (u untarOptions) matchPath(p string) bool { + if u.PathGlob == "" { + return true + } + match, err := filepath.Match(u.PathGlob, p) + if err != nil { + // Incorrect pattern. + panic(err) + } + return match +} + +func untar(r io.ReadCloser, opts untarOptions) error { + destDir := filepath.Clean(opts.Destination) + var tr *tar.Reader + if opts.Gzip { + gzr, errRead := gzip.NewReader(r) + if errRead != nil { + return errRead + } + defer gzr.Close() + tr = tar.NewReader(gzr) + } else { + tr = tar.NewReader(r) + } + + for { + header, err := tr.Next() + + switch { + // if no more files are found return + case err == io.EOF: + return nil + // return any other error + case err != nil: + return err + // if the header is nil, just skip it (not sure how this happens) + case header == nil: + continue + // skip unwanted path + case !opts.matchPath(header.Name): + continue + } + + // the target location where the dir/file should be created + target, err := sanitizeArchivePath(destDir, header.Name, opts.Strip) + if err != nil || !strings.HasPrefix(target, destDir) { + return errors.New("invalid filepath") + } + + // check the file type + switch header.Typeflag { + // if it's a dir, and it doesn't exist create it + case tar.TypeDir: + if _, err = os.Stat(target); err != nil { + if err = os.MkdirAll(target, 0750); err != nil { + return err + } + } + // if it's a file create it + case tar.TypeReg: + var f *os.File + filemode := os.FileMode(header.Mode) //nolint 115 // Overflow should never happen + f, err = os.OpenFile(target, os.O_CREATE|os.O_RDWR, filemode) //nolint 304 // Path is clean. + if err != nil { + return err + } + for { + _, err = io.CopyN(f, tr, 1024) + if err != nil { + if err != io.EOF { + return err + } + break + } + } + + // manually close here after each file operation; deferring would cause each file close + // to wait until all operations have completed. + err = f.Close() + if err != nil { + return err + } + } + } +} + +func sanitizeArchivePath(base string, target string, strip int) (v string, err error) { + if strip > 0 { + parts := strings.Split(target, string(filepath.Separator)) + target = strings.Join(parts[strip:], string(filepath.Separator)) + } + v = filepath.Clean(filepath.Join(base, target)) + if strings.HasPrefix(v, base) { + return v, nil + } + + return "", fmt.Errorf("content filepath is tainted: %s", target) +} + +const pluginTemplate = `// Code generated by {{.Pkg}}. DO NOT EDIT. +package main + +import ( + "embed" + + web "{{.Pkg}}" +) + +//go:embed {{.ClientPath}}/* +var webClientFS embed.FS + +func init() { + web.SetClientAssetsFS(web.MustSubFS(webClientFS, "{{.ClientPath}}")) +} +` diff --git a/go.mod b/go.mod index bac2afe..77d8699 100644 --- a/go.mod +++ b/go.mod @@ -1,82 +1,100 @@ module github.com/launchrctl/web -go 1.22.0 +go 1.23.1 -toolchain go1.23.1 +toolchain go1.23.2 require ( - github.com/getkin/kin-openapi v0.127.0 + github.com/getkin/kin-openapi v0.128.0 github.com/go-chi/chi/v5 v5.1.0 github.com/go-chi/cors v1.2.1 github.com/go-chi/render v1.0.3 github.com/gorilla/websocket v1.5.3 - github.com/launchrctl/launchr v0.14.1 + github.com/launchrctl/launchr v0.16.3 github.com/oapi-codegen/nethttp-middleware v1.0.2 github.com/oapi-codegen/oapi-codegen/v2 v2.4.1 github.com/oapi-codegen/runtime v1.1.1 github.com/spf13/cobra v1.8.1 - golang.org/x/sys v0.25.0 + golang.org/x/sys v0.26.0 gopkg.in/yaml.v3 v3.0.1 ) require ( + atomicgo.dev/cursor v0.2.0 // indirect + atomicgo.dev/keyboard v0.2.9 // indirect + atomicgo.dev/schedule v0.1.0 // indirect github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect - github.com/a8m/envsubst v1.4.2 // indirect github.com/ajg/form v1.5.1 // indirect github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect - github.com/containerd/containerd v1.7.13 // indirect + github.com/cenkalti/backoff/v4 v4.3.0 // indirect + github.com/containerd/console v1.0.4 // indirect github.com/containerd/log v0.1.0 // indirect github.com/distribution/reference v0.6.0 // indirect - github.com/docker/docker v25.0.3+incompatible // indirect + github.com/docker/docker v27.3.1+incompatible // indirect github.com/docker/go-connections v0.5.0 // indirect github.com/docker/go-units v0.5.0 // indirect - github.com/dprotaso/go-yit v0.0.0-20220510233725-9ba8df137936 // indirect + github.com/dprotaso/go-yit v0.0.0-20240618133044-5a0af90af097 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-openapi/jsonpointer v0.21.0 // indirect github.com/go-openapi/swag v0.23.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/protobuf v1.5.4 // indirect github.com/google/uuid v1.6.0 // indirect + github.com/gookit/color v1.5.4 // indirect github.com/gorilla/mux v1.8.1 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/invopop/yaml v0.3.1 // indirect github.com/josharian/intern v1.0.0 // indirect - github.com/klauspost/compress v1.17.9 // indirect + github.com/klauspost/compress v1.17.11 // indirect + github.com/knadh/koanf v1.5.0 // indirect + github.com/lithammer/fuzzysearch v1.1.8 // indirect github.com/mailru/easyjson v0.7.7 // indirect - github.com/moby/moby v25.0.3+incompatible // indirect + github.com/mattn/go-runewidth v0.0.16 // indirect + github.com/mitchellh/copystructure v1.2.0 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/mitchellh/reflectwalk v1.0.2 // indirect + github.com/moby/docker-image-spec v1.3.1 // indirect github.com/moby/patternmatcher v0.6.0 // indirect github.com/moby/sys/sequential v0.6.0 // indirect github.com/moby/sys/signal v0.7.1 // indirect github.com/moby/sys/user v0.3.0 // indirect + github.com/moby/sys/userns v0.1.0 // indirect github.com/moby/term v0.5.0 // indirect github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/morikuni/aec v1.0.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.0 // indirect github.com/otiai10/copy v1.14.0 // indirect + github.com/pelletier/go-toml v1.9.5 // indirect github.com/perimeterx/marshmallow v1.1.5 // indirect github.com/pkg/errors v0.9.1 // indirect + github.com/pterm/pterm v0.12.79 // indirect + github.com/rivo/uniseg v0.4.7 // indirect + github.com/rogpeppe/go-internal v1.13.1 // indirect github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/speakeasy-api/openapi-overlay v0.9.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/vmware-labs/yaml-jsonpath v0.3.2 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.55.0 // indirect - go.opentelemetry.io/otel v1.30.0 // indirect - go.opentelemetry.io/otel/metric v1.30.0 // indirect - go.opentelemetry.io/otel/sdk v1.21.0 // indirect - go.opentelemetry.io/otel/trace v1.30.0 // indirect - go.uber.org/mock v0.4.0 // indirect + github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 // indirect + go.opentelemetry.io/otel v1.31.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.31.0 // indirect + go.opentelemetry.io/otel/metric v1.31.0 // indirect + go.opentelemetry.io/otel/trace v1.31.0 // indirect + go.opentelemetry.io/proto/otlp v1.3.1 // indirect + go.uber.org/mock v0.5.0 // indirect golang.org/x/mod v0.21.0 // indirect golang.org/x/sync v0.8.0 // indirect - golang.org/x/text v0.18.0 // indirect - golang.org/x/tools v0.25.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20231120223509-83a465c0220f // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20231212172506-995d672761c0 // indirect - google.golang.org/grpc v1.59.0 // indirect - google.golang.org/protobuf v1.33.0 // indirect + golang.org/x/term v0.25.0 // indirect + golang.org/x/text v0.19.0 // indirect + golang.org/x/tools v0.26.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9 // indirect + google.golang.org/grpc v1.67.1 // indirect + google.golang.org/protobuf v1.35.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect ) diff --git a/go.sum b/go.sum index 5e27a59..2df79aa 100644 --- a/go.sum +++ b/go.sum @@ -1,28 +1,77 @@ -github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU= -github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= +atomicgo.dev/assert v0.0.2 h1:FiKeMiZSgRrZsPo9qn/7vmr7mCsh5SZyXY4YGYiYwrg= +atomicgo.dev/assert v0.0.2/go.mod h1:ut4NcI3QDdJtlmAxQULOmA13Gz6e2DWbSAS8RUOmNYQ= +atomicgo.dev/cursor v0.2.0 h1:H6XN5alUJ52FZZUkI7AlJbUc1aW38GWZalpYRPpoPOw= +atomicgo.dev/cursor v0.2.0/go.mod h1:Lr4ZJB3U7DfPPOkbH7/6TOtJ4vFGHlgj1nc+n900IpU= +atomicgo.dev/keyboard v0.2.9 h1:tOsIid3nlPLZ3lwgG8KZMp/SFmr7P0ssEN5JUsm78K8= +atomicgo.dev/keyboard v0.2.9/go.mod h1:BC4w9g00XkxH/f1HXhW2sXmJFOCWbKn9xrOunSFtExQ= +atomicgo.dev/schedule v0.1.0 h1:nTthAbhZS5YZmgYbb2+DH8uQIZcTlIrd4eYr3UQxEjs= +atomicgo.dev/schedule v0.1.0/go.mod h1:xeUa3oAkiuHYh8bKiQBRojqAMq3PXXbJujjb0hw8pEU= +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/MarvinJWendt/testza v0.1.0/go.mod h1:7AxNvlfeHP7Z/hDQ5JtE3OKYT3XFUeLCDE2DQninSqs= +github.com/MarvinJWendt/testza v0.2.1/go.mod h1:God7bhG8n6uQxwdScay+gjm9/LnO4D3kkcZX4hv9Rp8= +github.com/MarvinJWendt/testza v0.2.8/go.mod h1:nwIcjmr0Zz+Rcwfh3/4UhBp7ePKVhuBExvZqnKYWlII= +github.com/MarvinJWendt/testza v0.2.10/go.mod h1:pd+VWsoGUiFtq+hRKSU1Bktnn+DMCSrDrXDpX2bG66k= +github.com/MarvinJWendt/testza v0.2.12/go.mod h1:JOIegYyV7rX+7VZ9r77L/eH6CfJHHzXjB69adAhzZkI= +github.com/MarvinJWendt/testza v0.3.0/go.mod h1:eFcL4I0idjtIx8P9C6KkAuLgATNKpX4/2oUqKc6bF2c= +github.com/MarvinJWendt/testza v0.4.2/go.mod h1:mSdhXiKH8sg/gQehJ63bINcCKp7RtYewEjXsvsVUPbE= +github.com/MarvinJWendt/testza v0.5.2 h1:53KDo64C1z/h/d/stCYCPY69bt/OSwjq5KpFNwi+zB4= +github.com/MarvinJWendt/testza v0.5.2/go.mod h1:xu53QFE5sCdjtMCKk8YMQ2MnymimEctc4n3EjyIYvEY= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= -github.com/Microsoft/hcsshim v0.11.4 h1:68vKo2VN8DE9AdN4tnkWnmdhqdbpUFM8OF3Airm7fz8= -github.com/Microsoft/hcsshim v0.11.4/go.mod h1:smjE4dvqPX9Zldna+t5FG3rnoHhaB7QYxPRqGcpAD9w= github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk= -github.com/a8m/envsubst v1.4.2 h1:4yWIHXOLEJHQEFd4UjrWDrYeYlV7ncFWJOCBRLOZHQg= -github.com/a8m/envsubst v1.4.2/go.mod h1:MVUTQNGQ3tsjOOtKCNd+fl8RzhsXcDvvAEzkhGtlsbY= github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ= github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/atomicgo/cursor v0.0.1/go.mod h1:cBON2QmmrysudxNBFthvMtN32r3jxVRIvzkUiF/RuIk= +github.com/aws/aws-sdk-go-v2 v1.9.2/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4= +github.com/aws/aws-sdk-go-v2/config v1.8.3/go.mod h1:4AEiLtAb8kLs7vgw2ZV3p2VZ1+hBavOc84hqxVNpCyw= +github.com/aws/aws-sdk-go-v2/credentials v1.4.3/go.mod h1:FNNC6nQZQUuyhq5aE5c7ata8o9e4ECGmS4lAXC7o1mQ= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.6.0/go.mod h1:gqlclDEZp4aqJOancXK6TN24aKhT0W0Ae9MHk3wzTMM= +github.com/aws/aws-sdk-go-v2/internal/ini v1.2.4/go.mod h1:ZcBrrI3zBKlhGFNYWvju0I3TR93I7YIgAfy82Fh4lcQ= +github.com/aws/aws-sdk-go-v2/service/appconfig v1.4.2/go.mod h1:FZ3HkCe+b10uFZZkFdvf98LHW21k49W8o8J366lqVKY= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.3.2/go.mod h1:72HRZDLMtmVQiLG2tLfQcaWLCssELvGl+Zf2WVxMmR8= +github.com/aws/aws-sdk-go-v2/service/sso v1.4.2/go.mod h1:NBvT9R1MEF+Ud6ApJKM0G+IkPchKS7p7c2YPKwHmBOk= +github.com/aws/aws-sdk-go-v2/service/sts v1.7.2/go.mod h1:8EzeIqfWt2wWT4rJVu3f21TfrhJ8AEMzVybRNSb/b4g= +github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w= -github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= -github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/containerd/containerd v1.7.13 h1:wPYKIeGMN8vaggSKuV1X0wZulpMz4CrgEsZdaCyB6Is= -github.com/containerd/containerd v1.7.13/go.mod h1:zT3up6yTRfEUa6+GsITYIJNgSVL9NQ4x4h1RPzk0Wu4= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= +github.com/containerd/console v1.0.4 h1:F2g4+oChYvBTsASRTz8NP6iIAi97J3TtSAsLbIFn4ro= +github.com/containerd/console v1.0.4/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= @@ -31,29 +80,46 @@ 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/docker v25.0.3+incompatible h1:D5fy/lYmY7bvZa0XTZ5/UJPljor41F+vdyJG5luQLfQ= -github.com/docker/docker v25.0.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v27.3.1+incompatible h1:KttF0XoteNTicmUtBO0L2tP+J7FGRFTjaEF4k6WdhfI= +github.com/docker/docker v27.3.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= github.com/docker/go-connections v0.5.0/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/dprotaso/go-yit v0.0.0-20191028211022-135eb7262960/go.mod h1:9HQzr9D/0PGwMEbC3d5AB7oi67+h4TsQqItC1GVYG58= -github.com/dprotaso/go-yit v0.0.0-20220510233725-9ba8df137936 h1:PRxIJD8XjimM5aTknUK9w6DHLDox2r2M3DI4i2pnd3w= -github.com/dprotaso/go-yit v0.0.0-20220510233725-9ba8df137936/go.mod h1:ttYvX5qlB+mlV1okblJqcSMtR4c52UKxDiX9GRBS8+Q= +github.com/dprotaso/go-yit v0.0.0-20240618133044-5a0af90af097 h1:f5nA5Ys8RXqFXtKc0XofVRiuwNTuJzPIwTmbjLz9vj8= +github.com/dprotaso/go-yit v0.0.0-20240618133044-5a0af90af097/go.mod h1:FTAVyH6t+SlS97rv6EXRVuBDLkQqcIe/xQw9f4IFUI4= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= +github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= 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/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= -github.com/getkin/kin-openapi v0.127.0 h1:Mghqi3Dhryf3F8vR370nN67pAERW+3a95vomb3MAREY= -github.com/getkin/kin-openapi v0.127.0/go.mod h1:OZrfXzUfGrNbsKj+xmFBx6E5c6yH3At/tAKSc2UszXM= +github.com/getkin/kin-openapi v0.128.0 h1:jqq3D9vC9pPq1dGcOCv7yOp1DaEe7c/T1vzcLbITSp4= +github.com/getkin/kin-openapi v0.128.0/go.mod h1:OZrfXzUfGrNbsKj+xmFBx6E5c6yH3At/tAKSc2UszXM= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-chi/chi/v5 v5.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw= github.com/go-chi/chi/v5 v5.1.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4= github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58= github.com/go-chi/render v1.0.3 h1:AsXqd2a1/INaIfUSKq3G5uA8weYx20FOsM7uSoCyyt4= github.com/go-chi/render v1.0.3/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= +github.com/go-ldap/ldap v3.0.2+incompatible/go.mod h1:qfd9rJvER9Q0/D/Sqn1DfHRoBp40uXYvFoEVrNEPqRc= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= 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= @@ -63,63 +129,177 @@ github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1 github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +github.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM= github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= -github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= 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/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gookit/color v1.4.2/go.mod h1:fqRyamkC1W8uxl+lxCQxOT09l/vYfZ+QeiX3rKQHCoQ= +github.com/gookit/color v1.5.0/go.mod h1:43aQb+Zerm/BWh2GnrgOQm7ffz7tvQXEKV6BFMl7wAo= +github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0= +github.com/gookit/color v1.5.4/go.mod h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/QJi9w= github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k= +github.com/hashicorp/consul/api v1.13.0/go.mod h1:ZlVrynguJKcYr54zGaDbaL3fOvKC9m72FhPvA8T35KQ= +github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9uLqI8l75knNv3lV1kA55veR+WUPSiKIWcQHudI= +github.com/hashicorp/go-hclog v0.8.0/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= +github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= +github.com/hashicorp/go-plugin v1.0.1/go.mod h1:++UyYGoz3o5w9ZzAdZxtQKrWWP+iqPBn3cQptSMzBuY= +github.com/hashicorp/go-retryablehttp v0.5.4/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= +github.com/hashicorp/go-rootcerts v1.0.1/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= +github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A= +github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc= +github.com/hashicorp/memberlist v0.3.0/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= +github.com/hashicorp/serf v0.9.6/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4= +github.com/hashicorp/vault/api v1.0.4/go.mod h1:gDcqh3WGcR1cpF5AJz/B1UFheUEneMoIospckxBxk6Q= +github.com/hashicorp/vault/sdk v0.1.13/go.mod h1:B+hVj7TpuQY1Y/GPbCpffmgd+tSEwvhkWnjtSYCaS2M= +github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= +github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= +github.com/hjson/hjson-go/v4 v4.0.0 h1:wlm6IYYqHjOdXH1gHev4VoXCaW20HdQAGCxdOEEg2cs= +github.com/hjson/hjson-go/v4 v4.0.0/go.mod h1:KaYt3bTw3zhBjYqnXkYywcYctk0A2nxeEFTse3rH13E= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/invopop/yaml v0.3.1 h1:f0+ZpmhfBSS4MhG+4HYseMdJhoeeopbSKbq5Rpeelso= github.com/invopop/yaml v0.3.1/go.mod h1:PMOp3nn4/12yEZUFfmOuNHJsZToEEOwoWsT+D81KkeA= +github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= +github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc= +github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= -github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= +github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.0.10/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= +github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= +github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= +github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/knadh/koanf v1.5.0 h1:q2TSd/3Pyc/5yP9ldIrSdIz26MCcyNQzW0pEAugLPNs= +github.com/knadh/koanf v1.5.0/go.mod h1:Hgyjp4y8v44hpZtPzs7JZfRAW5AhN7KfZcwv1RYggDs= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/launchrctl/launchr v0.14.1 h1:v2HhwWGTXBCmhOB8DADT0apU+VkjEwFi9ZfJO2mNYXg= -github.com/launchrctl/launchr v0.14.1/go.mod h1:zgik46S8lFCqjdrw/BXek/hQUmvXLh/ULY7gm/vPYRc= +github.com/launchrctl/launchr v0.16.3 h1:/td/4J3CrNmD0aomD47+C/1n2luVsFFGuJilgUtBisk= +github.com/launchrctl/launchr v0.16.3/go.mod h1:EHcl8EVllzMRwJ1kHJz0OYA11GEKMLLrSCzCHqBenkE= +github.com/lithammer/fuzzysearch v1.1.8 h1:/HIuJnjHuXS8bKaiTMeeDlW2/AyIWk2brx1V8LFgLN4= +github.com/lithammer/fuzzysearch v1.1.8/go.mod h1:IdqeyBClc3FFqSzYq/MXESsS4S0FsZ5ajtkr5xPLts4= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/moby/moby v25.0.3+incompatible h1:Uzxm7JQOHBY8kZY2fa95a9kg0aTOt1cBidSZ+LXCxC4= -github.com/moby/moby v25.0.3+incompatible/go.mod h1:fDXVQ6+S340veQPv35CzDahGBmHsiclFwfEygB/TWMc= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= +github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= +github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= +github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= +github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= +github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= +github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= +github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/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/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk= github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU= @@ -128,12 +308,21 @@ github.com/moby/sys/signal v0.7.1 h1:PrQxdvxcGijdo6UXXo/lU/TvHUWyPhj7UOpSo8tuvk0 github.com/moby/sys/signal v0.7.1/go.mod h1:Se1VGehYokAkrSQwL4tDzHvETwUZlnY7S5XtQ50mQp8= github.com/moby/sys/user v0.3.0 h1:9ni5DlcW5an3SvRSx4MouotOygvzaXbaSrc/wGDFWPo= github.com/moby/sys/user v0.3.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs= +github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g= +github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28= 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/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/npillmayer/nestext v0.1.3/go.mod h1:h2lrijH8jpicr25dFY+oAJLyzlya6jhnuG+zWp9L0Uk= 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= @@ -143,6 +332,7 @@ github.com/oapi-codegen/oapi-codegen/v2 v2.4.1 h1:ykgG34472DWey7TSjd8vIfNykXgjOg github.com/oapi-codegen/oapi-codegen/v2 v2.4.1/go.mod h1:N5+lY1tiTDV3V1BeHtOxeWXHoPVeApvsvjJqegfoaz8= github.com/oapi-codegen/runtime v1.1.1 h1:EXLHh0DXIJnWhdRPN2w4MXAzFyE4CskzhNLUmtpMYro= github.com/oapi-codegen/runtime v1.1.1/go.mod h1:SK9X900oXmPWilYR5/WKPzt3Kqxn/uS/+lbpREv+eCg= +github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.10.2/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= @@ -164,19 +354,66 @@ github.com/otiai10/copy v1.14.0 h1:dCI/t1iTdYGtkvCuBG2BgR6KZa83PTclw4U5n2wAllU= github.com/otiai10/copy v1.14.0/go.mod h1:ECfuL02W+/FkTWZWgQqXPWZgW9oeKCSQ5qVfSc4qc4w= github.com/otiai10/mint v1.5.1 h1:XaPLeE+9vGbuyEHem1JNk3bYc7KKqyI/na0/mLd/Kks= github.com/otiai10/mint v1.5.1/go.mod h1:MJm72SBthJjz8qhefc4z1PYEieWmy8Bku7CjcAqyUSM= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= +github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= +github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s= github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw= +github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= -github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= +github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/pterm/pterm v0.12.27/go.mod h1:PhQ89w4i95rhgE+xedAoqous6K9X+r6aSOI2eFF7DZI= +github.com/pterm/pterm v0.12.29/go.mod h1:WI3qxgvoQFFGKGjGnJR849gU0TsEOvKn5Q8LlY1U7lg= +github.com/pterm/pterm v0.12.30/go.mod h1:MOqLIyMOgmTDz9yorcYbcw+HsgoZo3BQfg2wtl3HEFE= +github.com/pterm/pterm v0.12.31/go.mod h1:32ZAWZVXD7ZfG0s8qqHXePte42kdz8ECtRyEejaWgXU= +github.com/pterm/pterm v0.12.33/go.mod h1:x+h2uL+n7CP/rel9+bImHD5lF3nM9vJj80k9ybiiTTE= +github.com/pterm/pterm v0.12.36/go.mod h1:NjiL09hFhT/vWjQHSj1athJpx6H8cjpHXNAK5bUw8T8= +github.com/pterm/pterm v0.12.40/go.mod h1:ffwPLwlbXxP+rxT0GsgDTzS3y3rmpAO1NMjUkGTYf8s= +github.com/pterm/pterm v0.12.79 h1:lH3yrYMhdpeqX9y5Ep1u7DejyHy7NSQg9qrBjF9dFT4= +github.com/pterm/pterm v0.12.79/go.mod h1:1v/gzOF1N0FsjbgTHZ1wVycRkKiatFvJSJC4IGaQAAo= +github.com/rhnvrm/simples3 v0.6.1/go.mod h1:Y+3vYm2V7Y4VijFoJHHTrja6OgPrJ2cBti8dPGkC3sA= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= +github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 h1:lZUw3E0/J3roVtGQ+SCrUrg3ON6NgVqpn3+iol9aGu4= github.com/santhosh-tekuri/jsonschema/v5 v5.3.1/go.mod h1:uToXkOrWAZ6/Oc07xWQrPOhJotwFIyu2bBVN41fcDUY= -github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= +github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/speakeasy-api/openapi-overlay v0.9.0 h1:Wrz6NO02cNlLzx1fB093lBlYxSI54VRhy1aSutx0PQg= @@ -187,9 +424,12 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= @@ -197,120 +437,251 @@ github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4d github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/vmware-labs/yaml-jsonpath v0.3.2 h1:/5QKeCBGdsInyDCyVNLbXyilb61MXGi9NP674f9Hobk= github.com/vmware-labs/yaml-jsonpath v0.3.2/go.mod h1:U6whw1z03QyqgWdgXxvVnQ90zN1BWz5V+51Ewf8k+rQ= +github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs= +github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= +github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.55.0 h1:ZIg3ZT/aQ7AfKqdwp7ECpOK6vHqquXXuyTjIO8ZdmPs= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.55.0/go.mod h1:DQAwmETtZV00skUwgD6+0U89g80NKsJE3DCKeLLPQMI= -go.opentelemetry.io/otel v1.30.0 h1:F2t8sK4qf1fAmY9ua4ohFS/K+FUuOPemHUIXHtktrts= -go.opentelemetry.io/otel v1.30.0/go.mod h1:tFw4Br9b7fOS+uEao81PJjVMjW/5fvNCbpsDIXqP0pc= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 h1:Mne5On7VWdx7omSrSSZvM4Kw7cS7NQkOOmLcgscI51U= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0/go.mod h1:IPtUMKL4O3tH5y+iXVyAXqpAwMuzC1IrxVS81rummfE= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 h1:IeMeyr1aBvBiPVYihXIaeIZba6b8E1bYp7lbdxK8CQg= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0/go.mod h1:oVdCUtjq9MK9BlS7TtucsQwUcXcymNiEDjgDD2jMtZU= -go.opentelemetry.io/otel/metric v1.30.0 h1:4xNulvn9gjzo4hjg+wzIKG7iNFEaBMX00Qd4QIZs7+w= -go.opentelemetry.io/otel/metric v1.30.0/go.mod h1:aXTfST94tswhWEb+5QjlSqG+cZlmyXy/u8jFpor3WqQ= -go.opentelemetry.io/otel/sdk v1.21.0 h1:FTt8qirL1EysG6sTQRZ5TokkU8d0ugCj8htOgThZXQ8= -go.opentelemetry.io/otel/sdk v1.21.0/go.mod h1:Nna6Yv7PWTdgJHVRD9hIYywQBRx7pbox6nwBnZIxl/E= -go.opentelemetry.io/otel/trace v1.30.0 h1:7UBkkYzeg3C7kQX8VAidWh2biiQbtAKjyIML8dQ9wmc= -go.opentelemetry.io/otel/trace v1.30.0/go.mod h1:5EyKqTzzmyqB9bwtCCq6pDLktPK6fmGf/Dph+8VI02o= -go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= -go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= -go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= -go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +go.etcd.io/etcd/api/v3 v3.5.4/go.mod h1:5GB2vv4A4AOn3yk7MftYGHkUfGtDHnEraIjym4dYz5A= +go.etcd.io/etcd/client/pkg/v3 v3.5.4/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= +go.etcd.io/etcd/client/v3 v3.5.4/go.mod h1:ZaRkVgBZC+L+dLCjTcF1hRXpgZXQPOvnA/Ak/gq3kiY= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 h1:UP6IpuHFkUgOQL9FFQFrZ+5LiwhhYRbi7VZSIx6Nj5s= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0/go.mod h1:qxuZLtbq5QDtdeSHsS7bcf6EH6uO6jUAgk764zd3rhM= +go.opentelemetry.io/otel v1.31.0 h1:NsJcKPIW0D0H3NgzPDHmo0WW6SptzPdqg/L1zsIm2hY= +go.opentelemetry.io/otel v1.31.0/go.mod h1:O0C14Yl9FgkjqcCZAsE053C13OaddMYr/hz6clDkEJE= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.31.0 h1:K0XaT3DwHAcV4nKLzcQvwAgSyisUghWoY20I7huthMk= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.31.0/go.mod h1:B5Ki776z/MBnVha1Nzwp5arlzBbE3+1jk+pGmaP5HME= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.31.0 h1:lUsI2TYsQw2r1IASwoROaCnjdj2cvC2+Jbxvk6nHnWU= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.31.0/go.mod h1:2HpZxxQurfGxJlJDblybejHB6RX6pmExPNe517hREw4= +go.opentelemetry.io/otel/metric v1.31.0 h1:FSErL0ATQAmYHUIzSezZibnyVlft1ybhy4ozRPcF2fE= +go.opentelemetry.io/otel/metric v1.31.0/go.mod h1:C3dEloVbLuYoX41KpmAhOqNriGbA+qqH6PQ5E5mUfnY= +go.opentelemetry.io/otel/sdk v1.31.0 h1:xLY3abVHYZ5HSfOg3l2E5LUj2Cwva5Y7yGxnSW9H5Gk= +go.opentelemetry.io/otel/sdk v1.31.0/go.mod h1:TfRbMdhvxIIr/B2N2LQW2S5v9m3gOQ/08KsbbO5BPT0= +go.opentelemetry.io/otel/trace v1.31.0 h1:ffjsj1aRouKewfr85U2aGagJ46+MvodynlQ1HYdmJys= +go.opentelemetry.io/otel/trace v1.31.0/go.mod h1:TXZkRk7SM2ZQLtR6eoAWQFIHPvzQ06FJAsO1tJg480A= +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.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU= +go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20230811145659-89c5cff77bcb h1:mIKbk8weKhSeLH2GmUTrvx8CjkyJmnU1wFmg59CUjFA= +golang.org/x/exp v0.0.0-20230811145659-89c5cff77bcb/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0= golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= -golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= +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.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= +golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/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-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/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-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211013075003-97ac67df715c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= -golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= +golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24= +golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= -golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= +golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.25.0 h1:oFU9pkj/iJgs+0DT+VMHrx+oBKs/LJMV+Uvg78sl+fE= -golang.org/x/tools v0.25.0/go.mod h1:/vtpO8WL1N9cQC3FN5zPqb//fRXskFHbLKk4OW1Q7rg= +golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ= +golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/genproto/googleapis/api v0.0.0-20231120223509-83a465c0220f h1:2yNACc1O40tTnrsbk9Cv6oxiW8pxI/pXj0wRtdlYmgY= -google.golang.org/genproto/googleapis/api v0.0.0-20231120223509-83a465c0220f/go.mod h1:Uy9bTZJqmfrw2rIBxgGLnamc78euZULUBrLZ9XTITKI= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231212172506-995d672761c0 h1:/jFB8jK5R3Sq3i/lmeZO0cATSzFfZaJq1J2Euan3XKU= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231212172506-995d672761c0/go.mod h1:FUoWkonphQm3RhTS+kOEhF8h0iDpm4tdXolVCeZ9KKA= -google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk= -google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9 h1:T6rh4haD3GVYsgEfWExoCZA2o2FmbNyKpTuAxbEFPTg= +google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9/go.mod h1:wp2WsuBYj6j8wUdo3ToZsdxxixbvQNAHqVJrTgi5E5M= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9 h1:QCqS/PdaHTSWGvupk2F/ehwHtGc0/GYkT+3GAcR1CCc= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI= +google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.22.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E= +google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= -google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA= +google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= 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/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.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20191026110619-0b21df46bc1d/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0/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.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o= -gotest.tools/v3 v3.4.0/go.mod h1:CtbdzLSsqVhDgMtKsx03ird5YTGB3ar27v0u/yKBW5g= +gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= +gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= diff --git a/plugin.go b/plugin.go index 42d87d3..0cf0678 100644 --- a/plugin.go +++ b/plugin.go @@ -11,29 +11,29 @@ import ( const ( pluginName = "web" + stopArg = "stop" + pidFile = "web.pid" // APIPrefix is a default api prefix on the server. APIPrefix = "/api" - - stopArg = "stop" ) func init() { launchr.RegisterPlugin(&Plugin{}) } -// Plugin is launchr plugin providing web ui. +// Plugin is [launchr.Plugin] providing web ui. type Plugin struct { app launchr.App cfg launchr.Config } -// PluginInfo implements launchr.Plugin interface. +// PluginInfo implements [launchr.Plugin] interface. func (p *Plugin) PluginInfo() launchr.PluginInfo { return launchr.PluginInfo{} } -// OnAppInit implements launchr.Plugin interface. +// OnAppInit implements [launchr.OnAppInitPlugin] interface. func (p *Plugin) OnAppInit(app launchr.App) error { app.GetService(&p.cfg) @@ -45,59 +45,57 @@ type webFlags struct { Port int ProxyClient string UseSwaggerUI bool - RunInfoDir string + PluginDir string } -// CobraAddCommands implements launchr.CobraPlugin interface to provide web functionality. -func (p *Plugin) CobraAddCommands(rootCmd *cobra.Command) error { - pluginTmpDir := p.getPluginTempDir() - webPidFile := filepath.Join(pluginTmpDir, "web.pid") +// CobraAddCommands implements [launchr.CobraPlugin] interface to provide web functionality. +func (p *Plugin) CobraAddCommands(rootCmd *launchr.Command) error { + pluginTmpDir := p.cfg.Path(pluginName) + webPidFile := filepath.Join(pluginTmpDir, pidFile) webRunFlags := webFlags{ - RunInfoDir: pluginTmpDir, + PluginDir: pluginTmpDir, } var foreground bool - var cmd = &cobra.Command{ + var cmd = &launchr.Command{ Use: "web [stop]", - Short: "Starts web server", + Short: "Starts Web UI", Args: cobra.MatchAll(cobra.RangeArgs(0, 1), cobra.OnlyValidArgs), ValidArgs: []string{stopArg}, Aliases: []string{"ui"}, Example: `web web --foreground web stop`, - RunE: func(cmd *cobra.Command, args []string) error { + RunE: func(cmd *launchr.Command, args []string) error { // Don't show usage help on a runtime error. cmd.SilenceUsage = true // If 'stop' arg passed, try to interrupt process and remove PID file. if len(args) > 0 && args[0] == stopArg { - return stopWeb(webPidFile, webRunFlags.RunInfoDir) + return stopWeb(webPidFile, webRunFlags.PluginDir) } - if ok, url := isWebRunning(webPidFile, webRunFlags.RunInfoDir); ok { - return fmt.Errorf("another server is already running at the URL: %s. please stop the existing server before starting a new one", url) + if url, _ := getExistingWeb(webPidFile, webRunFlags.PluginDir); url != "" { + return fmt.Errorf("Another web UI is already running at %s\nPlease stop the existing server before starting a new one.", url) } if foreground { - //@TODO refactor to pass only plugin.app instead of full plugin. - return runWeb(cmd.Context(), p, &webRunFlags) + return p.runWeb(cmd.Context(), webRunFlags) } - return runBackgroundWeb(cmd, p, &webRunFlags, webPidFile) + return p.runBackgroundWeb(cmd, webRunFlags, webPidFile) }, } cmd.Flags().IntVarP(&webRunFlags.Port, "port", "p", 8080, `Web server port`) - cmd.Flags().BoolVarP(&webRunFlags.UseSwaggerUI, "swagger-ui", "", false, `Serve swagger.json on /api/swagger.json and Swagger UI on /api/swagger-ui`) + // Check if swagger assets are available before adding option. + if _, err := GetSwaggerUIAssetsFS(); err == nil { + cmd.Flags().BoolVarP(&webRunFlags.UseSwaggerUI, "swagger-ui", "", false, `Serve swagger.json on /api/swagger.json and Swagger UI on /api/swagger-ui`) + } cmd.Flags().StringVarP(&webRunFlags.ProxyClient, "proxy-client", "", "", `Proxies to client web server, useful in local development`) - cmd.Flags().BoolVarP(&foreground, "foreground", "", false, `Run server as foreground process`) + cmd.Flags().BoolVarP(&foreground, "foreground", "", false, `Run server in foreground`) // Command flags. rootCmd.AddCommand(cmd) return nil } - -func (p *Plugin) getPluginTempDir() string { - return p.cfg.Path(pluginName) -} diff --git a/process.go b/process.go index b7aa32c..e3996ae 100644 --- a/process.go +++ b/process.go @@ -7,9 +7,16 @@ import ( "strconv" ) -func pidFileExists(path string) bool { - _, err := os.Stat(path) - return err == nil +func pidFileInfo(path string) (pid int, active bool) { + var err error + if _, err = os.Stat(path); err != nil { + return 0, false + } + pid, err = readPidFile(path) + if err != nil { + return 0, false + } + return pid, isProcessRunning(pid) } func readPidFile(path string) (int, error) { diff --git a/process-unix.go b/process_unix.go similarity index 76% rename from process-unix.go rename to process_unix.go index 3e9b252..f148835 100644 --- a/process-unix.go +++ b/process_unix.go @@ -1,5 +1,4 @@ -//go:build !windows -// +build !windows +//go:build unix package web @@ -7,8 +6,6 @@ import ( "os" "os/exec" "syscall" - - "github.com/launchrctl/launchr/pkg/log" ) func setSysProcAttr(cmd *exec.Cmd) { @@ -20,7 +17,6 @@ func setSysProcAttr(cmd *exec.Cmd) { func isProcessRunning(pid int) bool { process, err := os.FindProcess(pid) if err != nil { - log.Debug("Failed to find process: %s\n", err) return false } diff --git a/process-win.go b/process_windows.go similarity index 78% rename from process-win.go rename to process_windows.go index e8c724b..e7ef9c8 100644 --- a/process-win.go +++ b/process_windows.go @@ -1,5 +1,4 @@ //go:build windows -// +build windows package web @@ -15,8 +14,9 @@ func setSysProcAttr(cmd *exec.Cmd) { } } -func isProcessRunning(pid uint32) bool { - h, err := windows.OpenProcess(windows.PROCESS_QUERY_LIMITED_INFORMATION, false, pid) +func isProcessRunning(pid int) bool { + p := uint32(pid) + h, err := windows.OpenProcess(windows.PROCESS_QUERY_LIMITED_INFORMATION, false, p) if err != nil { return false } @@ -27,7 +27,7 @@ func isProcessRunning(pid uint32) bool { if err != nil { return false } - if code == windows.STILL_ACTIVE { + if windows.NTStatus(code) == windows.STATUS_PENDING { return true } diff --git a/scripts/prebuild.go b/scripts/prebuild.go deleted file mode 100644 index 71923dc..0000000 --- a/scripts/prebuild.go +++ /dev/null @@ -1,172 +0,0 @@ -//go:build ignore - -package main - -import ( - "archive/tar" - "compress/gzip" - "errors" - "fmt" - "io" - "net/http" - "os" - "path/filepath" - "strings" -) - -func handleErr(err error) { - if err != nil { - fmt.Println(err.Error()) - os.Exit(1) - } -} - -var ( - tplDistPath = "https://github.com/launchrctl/web/releases/download/%s/dist.tar.gz" -) - -func main() { - if len(os.Args) < 3 { - fmt.Println("not enough arguments provided") - os.Exit(2) - } - - release := os.Args[1] - dirPath := os.Args[2] - - archivePath := filepath.Clean(filepath.Join(dirPath, "dist.tar.gz")) - resultPath := filepath.Clean(filepath.Join(dirPath, ".")) - - fmt.Println("Trying to download dist archive...") - - downloadURL := fmt.Sprintf(tplDistPath, release) - err := downloadFile(downloadURL, archivePath) - handleErr(err) - - fmt.Println("Trying to unarchive dist archive...") - err = untar(archivePath, resultPath) - handleErr(err) - - fmt.Println("Removing tar file") - err = os.Remove(archivePath) - handleErr(err) - - fmt.Println("Success") -} - -func downloadFile(url string, filePath string) error { - // Create the file - out, err := os.Create(filepath.Clean(filePath)) - if err != nil { - return err - } - defer out.Close() - - // Download the body - client := &http.Client{} - req, err := http.NewRequest(http.MethodGet, url, nil) - if err != nil { - return err - } - - resp, err := client.Do(req) - if err != nil { - resp.Body.Close() //nolint - return err - } - defer resp.Body.Close() - - // Write the body to file - _, err = io.Copy(out, resp.Body) - return err -} - -func untar(fpath, tpath string) error { - r, errOp := os.Open(filepath.Clean(fpath)) - if errOp != nil { - return errOp - } - - gzr, errRead := gzip.NewReader(r) - if errRead != nil { - return errRead - } - defer gzr.Close() - - tr := tar.NewReader(gzr) - - for { - header, err := tr.Next() - - switch { - - // if no more files are found return - case err == io.EOF: - - return nil - - // return any other error - case err != nil: - return err - - // if the header is nil, just skip it (not sure how this happens) - case header == nil: - continue - } - - // the target location where the dir/file should be created - target, err := sanitizeArchivePath(tpath, header.Name) - if err != nil { - return errors.New("invalid filepath") - } - - if !strings.HasPrefix(target, filepath.Clean(tpath)) { - return errors.New("invalid filepath") - } - - // check the file type - switch header.Typeflag { - - // if it's a dir, and it doesn't exist create it - case tar.TypeDir: - if _, err := os.Stat(target); err != nil { - if err := os.MkdirAll(target, 0750); err != nil { - return err - } - } - - // if it's a file create it - case tar.TypeReg: - f, err := os.OpenFile(filepath.Clean(target), os.O_CREATE|os.O_RDWR, os.FileMode(header.Mode)) - if err != nil { - return err - } - - for { - _, err = io.CopyN(f, tr, 1024) - if err != nil { - if err != io.EOF { - return err - } - break - } - } - - // manually close here after each file operation; deferring would cause each file close - // to wait until all operations have completed. - err = f.Close() - if err != nil { - return err - } - } - } -} - -func sanitizeArchivePath(d, t string) (v string, err error) { - v = filepath.Join(d, t) - if strings.HasPrefix(v, filepath.Clean(d)) { - return v, nil - } - - return "", fmt.Errorf("%s: %s", "content filepath is tainted", t) -} diff --git a/server/api.go b/server/api.go index 6bf1e4b..669ed4b 100644 --- a/server/api.go +++ b/server/api.go @@ -13,11 +13,10 @@ import ( "sync" "time" - "github.com/launchrctl/launchr/pkg/log" + "gopkg.in/yaml.v3" "github.com/launchrctl/launchr" "github.com/launchrctl/launchr/pkg/action" - "gopkg.in/yaml.v3" ) type launchrServer struct { @@ -60,26 +59,22 @@ func (l *launchrServer) GetCustomisationConfig(w http.ResponseWriter, _ *http.Re } customisation := make(CustomisationConfig) - if launchrConfig == nil { - log.Debug("Launchr config doesn't exist") - } else { - if launchrConfig.VarsFile != "" { - vars := make(map[string]bool) - for _, item := range launchrConfig.Variables { - vars[item] = true - } + if launchrConfig != nil && launchrConfig.VarsFile != "" { + vars := make(map[string]bool) + for _, item := range launchrConfig.Variables { + vars[item] = true + } - gvFile, err := parseVarsFile(launchrConfig.VarsFile) - if err != nil { - sendError(w, http.StatusInternalServerError, "error getting group vars file") - return - } + gvFile, err := parseVarsFile(launchrConfig.VarsFile) + if err != nil { + sendError(w, http.StatusInternalServerError, "error getting group vars file") + return + } - if len(launchrConfig.Variables) > 0 { - for key, value := range gvFile { - if _, ok := vars[key]; ok { - customisation[key] = value - } + if len(launchrConfig.Variables) > 0 { + for key, value := range gvFile { + if _, ok := vars[key]; ok { + customisation[key] = value } } } @@ -269,13 +264,12 @@ func apiActionFull(baseURL string, a *action.Action) (ActionFull, error) { var uiSchema map[string]interface{} - yamlData, err := os.ReadFile(fmt.Sprintf("%s/ui-schema.yaml", a.Dir())) + yamlData, err := os.ReadFile(filepath.Join(a.Dir(), "ui-schema.yaml")) if err != nil { if !os.IsNotExist(err) { return ActionFull{}, err } - - fmt.Println("Info: ui-schema.yaml not found, using empty UISchema") + launchr.Log().Debug("ui-schema.yaml not found, using empty ui-schema", "action_id", a.ID) uiSchema = map[string]interface{}{} } else { err = yaml.Unmarshal(yamlData, &uiSchema) @@ -305,7 +299,7 @@ func apiActionShort(a *action.Action) (ActionShort, error) { func sendError(w http.ResponseWriter, code int, message string) { petErr := Error{ - Code: int32(code), + Code: code, Message: message, } w.WriteHeader(code) diff --git a/server/openapi.gen.go b/server/openapi.gen.go index bbb9ac3..8eca65c 100644 --- a/server/openapi.gen.go +++ b/server/openapi.gen.go @@ -83,7 +83,7 @@ type CustomisationConfig = map[string]interface{} // Error defines model for Error. type Error struct { - Code int32 `json:"code"` + Code int `json:"code"` Message string `json:"message"` } @@ -565,28 +565,28 @@ func HandlerWithOptions(si ServerInterface, options ChiServerOptions) http.Handl // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "H4sIAAAAAAAC/8xYS2/jNhD+KwLbo2w52Ztu3k1auFisF3F7SnNgpJHEXfERPpoYhv97QVJPS45lxEl7", - "siWOON98M/NpqB1KOBWcAdMKxTsksMQUNEh3tUw04WyV2v8pqEQSYW+gGK1uAp4F2K0HmgcZ6KRAISJ2", - "UWBt/zNMAcWIpChEEp4MkZCiWEsDIVJJARTbffVWWCulJWE52u/DyuudYSuW8ePOdQGBNIwRlldAxv1L", - "YyM4D8JXQokeOmaGPoK0zqEEajmzsUvQRjbOnwzIbeu9dDt1vVH8QqihKL5aLEJECauuwhoHYRpykA7I", - "OssUTEaifhJxBAf3G42E3brbW5KU4EyBS/8NZNiU+lZKLu11wpkG5tBgIUqSYIsm+qEspF1n518lZChG", - "v0RtcUV+VUV+N+esH5Jh8CIg0ZAGUNnUaDvF+JspSwegLNcZiu9fd+af2RRcarQPd0hILkBq4uOzwAd0", - "8McfkFiiXmY5n1Xs/bFZf9t4y2qhsm63mA/XZ4QK69n1lS5QjHKiC/M4TziNSmxYUshEl/XfSPzMow4m", - "G78h0wD+tRqFR7G492X9YNMsM5zAbt/DaGtmxl0ScDkT3Nn5HvEFUXfNfZevh/AAzv7hsHP7WeozT9Jh", - "3/UDWt0gm36NtVGnSqpxu/Hmh7idAlVbnQD+3eqfeg06lrmhtVyOpqS66TVp/udWwFLm6q2FUUmcjS0p", - "MMvBUUg0UDWiYU2UWEq8tdc+xeegXgt9OdQHKWlZbJGdSM2mqQVgVjDvUSIBa3Da7t8DKEQZYUQV7qZX", - "kXbXlpvOnhIwvcEav5byju4Ntkq46a00ahrWkju65u+0oSidro2TZ52umP+9lWPoD4h0q2EDMWyF3iN7", - "jVSvi68E3hPokeCntrEmuoTxN+2gT71t/+UwHsQXozSnROEDfKMC2TP+wllG8stqZYia9+Rh9aQu9oxL", - "irUvg0/XKBypCgpK4XwCU27P1n7IT9h9ZQ2oce1IKo3uv4WXLFh+X9nB4qtv42q2so1akgSYcvAqVpcC", - "JwUE1/MFCpGRJYpRobVQcRQ9Pz/PsVuec5lH1bMq+rr6cvttczu7ni/mhaZlpz5Q5RKF6B+QygO6mi/m", - "Cy9gwLAgKEaf5lfOoVUhR3FUY4x3KB8bl+7ciKYCXJZB2Y/rb+YkCCSuZ130O+hlE3RvIrpeLM4ahBp5", - "nj6kHAj3cE6qcAc1MOQs3KR2zFMTQ9Qb6dyAZSjFcmvJJ0p7hmo27XpNbbQj6f4ov7Lm1x8IHreB6+Uj", - "tH7erm5c+tqzxpE5rjWJmrPI/uGNSTmdCzdkHqX+4szfjbG3D5HgaoRreIHEaFDtoadP851hy3rlTRQ/", - "GVD6M0+3F2a3HbNGKPYmQTMfBJilQT0hHB7i9oNKuLo8VjfLflwxSNNUwrABo3rQOdWI/YOxGuvFO29S", - "Kd1/3JNnCGWTkqlSackISPXIJfv1kOSj6Yp27iPEZP1sAI+kbc2gl7k3Ji6caNt+jvkA/Z3Qde+U0sH2", - "p1IaKXeEUGd2ZFA/dqoxN43dB2b49CPVd6kJlv5T2gcLROdcd75MNLl5V7lovQxKrPqgVJMwRTGsbaDq", - "T0BH5q7eV6z/4/TVAXg8Td1Q36f7ex5sdpLDk2aVkgHP46fM9y/8/lF4QsknDtvFh5cejsA7se73/wYA", - "AP//vuYFImgYAAA=", + "H4sIAAAAAAAC/8xYy3LjthL9FRbuXVKiPNlxp7GdlFKu0ZSVrBwvYLJJYoZ4GI/YKpX+PQWAT5GyqLLs", + "ZCWRaKJPn+4+bHCHEk4FZ8C0QvEOCSwxBQ3SXS0TTThbpfZ/CiqRRNgbKEarm4BnAXbrgeZBBjopUIiI", + "XRRY2/8MU0AxIikKkYRnQySkKNbSQIhUUgDFdl+9FdZKaUlYjvb7sPJ6b9iKZfy4c11AIA1jhOUVkHH/", + "0tgIzoNwRyjRQ8fM0CeQ1jmUQC1nNnYJ2sjG+bMBuW29l26nrjeKXwk1FMVXi0WIKGHVVVjjIExDDtIB", + "WWeZgslI1E8ijuDgfqORsFt3e0uSEpwpcOm/gQybUt9KyaW9TjjTwBwaLERJEmzRRD+UhbTr7Px/CRmK", + "0f+itrgiv6oiv5tz1g/JMHgVkGhIA6hsarSdYvzVlKUDUJbrDMUPbzvzz2wKLjXahzskJBcgNfHxWeAD", + "OvjTD0gsUa+znM8q9n7frL9tvGW1UFm3W8yH6zNChfXs+koXKEY50YV5miecRiU2LClkosv6byR+5lEH", + "k43fkGkA/1yNwqNYPPiyfrRplhlOYLfvYbQ1M+MuCbicCe7sfI/4gqi75qHL12N4AGf/eNi5/Sz1mSfp", + "sO/6Aa1ukE2/xtqoUyXVuN1480PcToGqrU4A/271T70FHcvc0FouR1NS3fSaNP9jK2Apc/XewqgkzsaW", + "FJjl4CgkGqga0bAmSiwl3tprn+JzUK+Fvhzqg5S0LLbITqRm09QCMCuYDyiRgDU4bffvARSijDCiCnfT", + "q0i7a8tNZ08JmN5gjd9KeUf3Blsl3PRWGjUNa8kdXfN32lCUTtfGybNOV8z/3sox9AdEutWwgRi2Qu+R", + "vUWq18U3Au8J9EjwU9tYE13C+Jt20Kfetv9yGA/i2ijNKVH4AN+oQPaMrznLSH5ZrQxR8548rJ7UxZ5x", + "SbH2ZYDCkZqgoBTOJ/Dkdmzth+yE3RfWgBjXjKRS6P47eMmC5feVHSvufBNXk5Vt05IkwJSDV3G6FDgp", + "IPgyX6AQGVmiGBVaCxVH0cvLyxy75TmXeVQ9q6K71fXtt83t7Mt8MS80LTvVgSqXKER/g1Qe0NV8MV94", + "+QKGBUEx+mV+5RxaDXIERzXGeIfysWHp3g1oKsBlGZT9uP5iToBA4nrSRb+BXjZB9+ahL4vFWWNQI87T", + "R5QD2R5OSRXuoAaGnIWb0455amKIegOdG68MpVhuLflEac9QzaZdr6mNdiTdH+VX1vz648DTNnCdfITW", + "r9vVjUtfe9I4MsW1JlFzEtk/vjMpp3PhRsyj1F+c+fsx9vYhElyNcA2vkBgNqj3y9Gm+N2xZr7yL4mcD", + "Sn/l6fbC7LZD1gjF3iRopoMAszSo54PDI9x+UAlXl8fqJtnPKwZpmkoYNmBUjzmnGrF/LFZjvXjvTSql", + "+5d78gyhbFIyVSotGQGpHrlkvx6SfDRd0c59gpisnw3gkbStGfQy987EhRNt248xn6C/E7rug1I62P5U", + "SiPlDhDqzI4M6sdONeamsfvEDJ9+pPoqNcHSf0j7ZIHonOrOl4kmNx8qF62XQYlVn5NqEqYohrUNVP0B", + "6Mjc1fuG9V+cvjoAj6epG+rHdH/Pg81OcnjOrFIy4Hn8jPnxhd8/CE8o+cRhu/jw0sMReCfW/f6fAAAA", + "///k7iMNZhgAAA==", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/server/openapi.yaml b/server/openapi.yaml index bd0d993..abcbffb 100644 --- a/server/openapi.yaml +++ b/server/openapi.yaml @@ -296,6 +296,6 @@ components: properties: code: type: integer - format: int32 + format: int message: type: string \ No newline at end of file diff --git a/server/server.go b/server/server.go index aa77ad0..a6917db 100644 --- a/server/server.go +++ b/server/server.go @@ -13,11 +13,8 @@ import ( "net/http/httputil" "net/url" "os" - "os/exec" "os/signal" "path" - "path/filepath" - "runtime" "sort" "strings" "syscall" @@ -29,10 +26,7 @@ import ( "github.com/go-chi/render" "github.com/gorilla/websocket" "github.com/launchrctl/launchr" - "github.com/launchrctl/launchr/pkg/cli" - "github.com/launchrctl/launchr/pkg/log" middleware "github.com/oapi-codegen/nethttp-middleware" - "gopkg.in/yaml.v3" ) // RunOptions is a set of options for running openapi http server. @@ -48,13 +42,10 @@ type RunOptions struct { // Client server. ClientFS fs.FS ProxyClient string - RunInfoDir string } -// RunInfo is structure that stores current running server metadata. -type RunInfo struct { - // BaseURL stores server accessible URL. - BaseURL string `yaml:"BaseURL"` +func (o RunOptions) BaseURL() string { + return "http://localhost:" + strings.Split(o.Addr, ":")[1] } const ( @@ -64,8 +55,6 @@ const ( swaggerJSONPath = "/swagger.json" statusRunning string = "running" - - runInfoName = "server-info.yaml" ) // Run starts http server. @@ -92,6 +81,7 @@ func Run(ctx context.Context, app launchr.App, opts *RunOptions) error { })) store := &launchrServer{ ctx: ctx, + baseURL: opts.BaseURL(), apiPrefix: opts.APIPrefix, } app.GetService(&store.actionMngr) @@ -114,12 +104,9 @@ func Run(ctx context.Context, app launchr.App, opts *RunOptions) error { ) }) - r.Post("/api/shutdown", func(w http.ResponseWriter, r *http.Request) { + r.Post("/api/shutdown", func(w http.ResponseWriter, _ *http.Request) { cancel() - _, err = w.Write([]byte("Server is shutting down...")) - if err != nil { - log.Warn(err.Error()) - } + _, _ = w.Write([]byte("Server is shutting down...")) }) // Register router in openapi and start the server. @@ -132,142 +119,46 @@ func Run(ctx context.Context, app launchr.App, opts *RunOptions) error { // @todo remove all stopped containers when stopped // @todo add special prefix for web run containers. - baseURL := "http://localhost:" + strings.Split(s.Addr, ":")[1] - store.baseURL = baseURL + store.baseURL = opts.BaseURL() if opts.SwaggerJSON { - fmt.Println("Swagger UI: " + baseURL + opts.APIPrefix + swaggerUIPath) - fmt.Println("swagger.json: " + baseURL + opts.APIPrefix + swaggerJSONPath) + launchr.Term().Info(). + Printfln("Swagger UI: %s\nswagger.json: %s", store.basePath()+swaggerUIPath, store.basePath()+swaggerJSONPath) } - runInfo := RunInfo{BaseURL: baseURL} - - defer onShutdown(opts.RunInfoDir) - signals := make(chan os.Signal, 1) signal.Notify(signals, syscall.SIGINT, syscall.SIGTERM) go func() { - popInBrowser(baseURL) - }() - - go func() { - <-signals - log.Info("terminating...\n") + sig := <-signals + launchr.Log().Debug("shutting down on signal", "signal", sig) cancel() }() + var errShutdown error go func() { <-ctx.Done() - fmt.Println("Shutting down the server...") - if err = s.Shutdown(context.Background()); err != nil { - fmt.Printf("Error shutting down the server: %v\n", err) + launchr.Term().Info().Println("Shutting down...") + ctxShut, cancelShut := context.WithTimeout(context.Background(), time.Second*10) + defer cancelShut() + errShutdown = s.Shutdown(ctxShut) + // Force shutdown. + if errShutdown != nil { + errShutdown = s.Close() } }() - err = storeRunInfo(runInfo, opts.RunInfoDir) - if err != nil { - return err - } - if err = s.ListenAndServe(); !errors.Is(err, http.ErrServerClosed) { return err } - return nil -} - -func onShutdown(dir string) { - err := os.RemoveAll(dir) - if err != nil { - log.Warn(err.Error()) - } -} - -// GetRunInfo lookups server run info metadata and tries to get it from storage. -func GetRunInfo(storePath string) (*RunInfo, error) { - riPath := fmt.Sprintf("%s/%s", storePath, runInfoName) - - _, err := os.Stat(riPath) - if os.IsNotExist(err) { - return nil, nil - } - - data, err := os.ReadFile(filepath.Clean(riPath)) - if err != nil { - return nil, fmt.Errorf("error reading plugin storage path: %w", err) - } - - var ri RunInfo - err = yaml.Unmarshal(data, &ri) - if err != nil { - return nil, fmt.Errorf("error unmarshalling yaml: %w", err) - } - - return &ri, nil -} - -func storeRunInfo(runInfo RunInfo, storePath string) error { - ri, err := yaml.Marshal(&runInfo) - if err != nil { - return err - } - - err = os.MkdirAll(storePath, 0750) - if err != nil { - return err - } - err = os.WriteFile(fmt.Sprintf("%s/%s", storePath, runInfoName), ri, os.FileMode(0640)) - if err != nil { - return err + if errShutdown != nil { + launchr.Log().Error("error on shutting down", "error", err) + return errShutdown } return nil } -// CheckHealth helper to check if server is available by request. -func CheckHealth(url string) (bool, error) { - client := &http.Client{} - req, err := http.NewRequest(http.MethodHead, url, nil) - if err != nil { - return false, err - } - resp, err := client.Do(req) - if err != nil { - return false, err - } - _ = resp.Body.Close() - - return resp.StatusCode == http.StatusOK, nil -} - -func popInBrowser(url string) bool { - client := &http.Client{} - for { - req, err := http.NewRequest(http.MethodHead, url, nil) - if err != nil { - log.Warn(err.Error()) - return false - } - resp, err := client.Do(req) - if err != nil { - log.Info("The server isn't ready yet, please standby...") - time.Sleep(time.Second) - continue - } - _ = resp.Body.Close() - - if resp.StatusCode == http.StatusOK { - cli.Println("The web server can be reached through the following URL: %s.", url) - if err = openBrowser(url); err != nil { - log.Debug("Failed to open browser: %v\n", err) - } - } - break - } - - return true -} - func spaHandler(opts *RunOptions) http.HandlerFunc { if opts.ProxyClient != "" { target, _ := url.Parse(opts.ProxyClient) @@ -313,7 +204,7 @@ func serveSwaggerUI(swagger *openapi3.T, r chi.Router, opts *RunOptions) { var upgrader = websocket.Upgrader{ ReadBufferSize: 1024, WriteBufferSize: 1024, - CheckOrigin: func(r *http.Request) bool { + CheckOrigin: func(_ *http.Request) bool { return true }, } @@ -327,7 +218,8 @@ func wsHandler(l *launchrServer) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { ws, err := upgrader.Upgrade(w, r, nil) if err != nil { - log.Fatal(err.Error()) + launchr.Log().Error("failed to upgrade to websocket", "error", err) + return } defer ws.Close() @@ -335,18 +227,17 @@ func wsHandler(l *launchrServer) http.HandlerFunc { for { _, message, err = ws.ReadMessage() if err != nil { - log.Info(err.Error()) - break + launchr.Log().Error("failed to read ws message", "error", err) + return } var msg messageType if err = json.Unmarshal(message, &msg); err != nil { - log.Debug("Error unmarshalling message: %v", err) + launchr.Log().Info("error unmarshalling ws command", "error", err) continue } - log.Info("Received command: %s", msg.Message) - log.Info("Received params: %v", msg.Action) + launchr.Log().Debug("received ws command", "command", msg.Message, "params", msg.Action) switch msg.Message { case "get-processes": @@ -354,7 +245,7 @@ func wsHandler(l *launchrServer) http.HandlerFunc { case "get-process": go getStreams(msg, ws, l) default: - log.Info("Unknown command: %s", msg.Message) + launchr.Log().Info("unknown command", "command", msg.Message) } } } @@ -381,21 +272,21 @@ func getProcesses(msg messageType, ws *websocket.Conn, l *launchrServer) { return runningActions[i].Status < runningActions[j].Status }) - responseMessage := map[string]interface{}{ + msgAllProcesses := map[string]interface{}{ "message": "send-processes", "action": msg.Action, "processes": runningActions, } - finalResponse, err := json.Marshal(responseMessage) + resp, err := json.Marshal(msgAllProcesses) if err != nil { - log.Debug("Error marshaling final response: %v", err) + launchr.Log().Error("error on marshaling the response", "error", err) return } l.wsMutex.Lock() - if writeErr := ws.WriteMessage(websocket.TextMessage, finalResponse); writeErr != nil { - log.Debug(writeErr.Error()) + if writeErr := ws.WriteMessage(websocket.TextMessage, resp); writeErr != nil { + launchr.Log().Error("error on writing ws all processes", "error", writeErr) } l.wsMutex.Unlock() @@ -405,23 +296,23 @@ func getProcesses(msg messageType, ws *websocket.Conn, l *launchrServer) { } } - completeMessage := map[string]interface{}{ + msgFinished := map[string]interface{}{ "channel": "processes", "message": "send-processes-finished", "action": msg.Action, "processes": runningActions, } - finalCompleteResponse, err := json.Marshal(completeMessage) + resp, err = json.Marshal(msgFinished) if err != nil { - log.Debug("Error marshaling final response: %v", err) + launchr.Log().Error("error on marshaling the finished processes response", "error", err) return } if !anyProccessRunning { l.wsMutex.Lock() - if err = ws.WriteMessage(websocket.TextMessage, finalCompleteResponse); err != nil { - log.Debug(err.Error()) + if err = ws.WriteMessage(websocket.TextMessage, resp); err != nil { + launchr.Log().Error("error on writing ws finished processes", "error", err) } l.wsMutex.Unlock() break @@ -456,56 +347,43 @@ func getStreams(msg messageType, ws *websocket.Conn, l *launchrServer) { } // Send the process data - responseMessage := map[string]interface{}{ + msgAllProcesses := map[string]interface{}{ "channel": "process", "message": "send-process", "action": msg.Action, "data": sd, } - finalResponse, err := json.Marshal(responseMessage) + resp, err := json.Marshal(msgAllProcesses) if err != nil { - log.Debug("Error marshaling response: %v", err) + launchr.Log().Error("error on marshaling the response", "error", err) return } l.wsMutex.Lock() - if err = ws.WriteMessage(websocket.TextMessage, finalResponse); err != nil { - log.Debug(err.Error()) + if err = ws.WriteMessage(websocket.TextMessage, resp); err != nil { + launchr.Log().Error("error on writing ws all streams", "error", err) } l.wsMutex.Unlock() } // Send the final message indicating streams have finished with the last stream data - finalMessage := map[string]interface{}{ + msgFinished := map[string]interface{}{ "channel": "process", "message": "send-process-finished", "action": msg.Action, "data": lastStreamData, } - finalResponse, err := json.Marshal(finalMessage) + finalResponse, err := json.Marshal(msgFinished) if err != nil { - log.Debug("Error marshaling final message: %v", err) + launchr.Log().Error("error on marshaling the finished streams response", "error", err) return } l.wsMutex.Lock() if err = ws.WriteMessage(websocket.TextMessage, finalResponse); err != nil { - log.Debug(err.Error()) + launchr.Log().Error("error on writing ws finished streams", "error", err) } l.wsMutex.Unlock() } - -func openBrowser(url string) error { - switch runtime.GOOS { - case "linux": - return exec.Command("xdg-open", url).Start() - case "windows": - return exec.Command("rundll32", "url.dll,FileProtocolHandler", url).Start() - case "darwin": - return exec.Command("open", url).Start() - default: - return fmt.Errorf("unsupported platform") - } -} diff --git a/server/streams.go b/server/streams.go index 87a49f2..fb8c50c 100644 --- a/server/streams.go +++ b/server/streams.go @@ -7,7 +7,7 @@ import ( "os" "strings" - "github.com/launchrctl/launchr/pkg/cli" + "github.com/launchrctl/launchr" ) type fileStreams interface { @@ -17,19 +17,19 @@ type fileStreams interface { // webCli implements Streams interface. // @todo Maybe refactor original streams. type webCli struct { - in *cli.In - out *cli.Out + in *launchr.In + out *launchr.Out err io.Writer files []*os.File } // In returns the reader used for stdin -func (cli *webCli) In() *cli.In { +func (cli *webCli) In() *launchr.In { return cli.in } // Out returns the writer used for stdout -func (cli *webCli) Out() *cli.Out { +func (cli *webCli) Out() *launchr.Out { return cli.out } @@ -93,12 +93,12 @@ func (w *wrappedWriter) Write(p []byte) (int, error) { } func createFileStreams(runId string) (*webCli, error) { - outfile, err := os.Create(fmt.Sprintf("%s-out.txt", runId)) + outfile, err := os.Create(runId + "-out.txt") if err != nil { return nil, fmt.Errorf("error creating output file: %w", err) } - errfile, err := os.Create(fmt.Sprintf("%s-err.txt", runId)) + errfile, err := os.Create(runId + "-err.txt") if err != nil { return nil, fmt.Errorf("error creating error file: %w", err) } @@ -116,8 +116,8 @@ func createFileStreams(runId string) (*webCli, error) { // Build and return webCli return &webCli{ files: []*os.File{outfile, errfile}, - in: cli.NewIn(io.NopCloser(strings.NewReader(""))), - out: cli.NewOut(out), + in: launchr.NewIn(io.NopCloser(strings.NewReader(""))), + out: launchr.NewOut(out), err: errWriter, }, nil } diff --git a/web-runner.go b/web-runner.go deleted file mode 100644 index 28192b5..0000000 --- a/web-runner.go +++ /dev/null @@ -1,272 +0,0 @@ -package web - -import ( - "context" - "errors" - "fmt" - "net" - "os" - "os/exec" - "path/filepath" - "time" - - "github.com/launchrctl/launchr/pkg/cli" - "github.com/launchrctl/launchr/pkg/log" - "github.com/spf13/cobra" - - "github.com/launchrctl/web/server" -) - -const ( - launchrBackgroundEnvVar = "LAUNCHR_BACKGROUND" -) - -func isBackGroundEnv() bool { - return len(os.Getenv(launchrBackgroundEnvVar)) == 1 -} - -func runWeb(ctx context.Context, p *Plugin, webOpts *webFlags) error { - var err error - - port := webOpts.Port - if !isAvailablePort(port) { - log.Info("The port %d you are trying to use for the web server is not available.", port) - port, err = getAvailablePort(port) - if err != nil { - return err - } - } - - serverOpts := &server.RunOptions{ - Addr: fmt.Sprintf(":%d", port), // @todo use proper addr - APIPrefix: APIPrefix, - SwaggerJSON: webOpts.UseSwaggerUI, - ProxyClient: webOpts.ProxyClient, - RunInfoDir: webOpts.RunInfoDir, - // @todo use embed fs for client or provide path ? - } - - // @todo to consider renaming and removing access to plugin and overall global assets. - prepareRunOption(p, serverOpts) - - return server.Run(ctx, p.app, serverOpts) -} - -func runBackgroundWeb(cmd *cobra.Command, p *Plugin, flags *webFlags, pidFile string) error { - if isBackGroundEnv() { - // @TODO rework logs, to replace with global launchr logging. - err := redirectOutputs(flags.RunInfoDir) - if err != nil { - return err - } - - return runWeb(cmd.Context(), p, flags) - } - - pid, err := runBackgroundCmd(cmd, pidFile) - if err != nil { - return err - } - - // Wait until background server is up. - // Check if run info created and server is reachable. - // Print server URL in CLI. - // Kill process in case of timeout - timeout := time.After(10 * time.Second) - ticker := time.NewTicker(1 * time.Second) - - defer ticker.Stop() - - for { - select { - case <-timeout: - // Kill existing process - _ = killProcess(pid) - - // Cleanup temp dir - err = os.RemoveAll(flags.RunInfoDir) - if err != nil { - log.Debug(err.Error()) - } - - return errors.New("couldn't start background process") - case <-ticker.C: - runInfo, _ := server.GetRunInfo(flags.RunInfoDir) - if runInfo == nil { - continue - } - - cli.Println("Web running in background with PID:%d", pid) - cli.Println(runInfo.BaseURL) - - return nil - } - } -} - -func runBackgroundCmd(cmd *cobra.Command, pidFile string) (int, error) { - err := os.MkdirAll(filepath.Dir(pidFile), 0750) - if err != nil { - return 0, fmt.Errorf("not possible to create tmp directory for %s", pidFile) - } - - // Prepare the command to restart itself in the background - args := append([]string{cmd.Name()}, os.Args[2:]...) - - command := exec.Command(os.Args[0], args...) //nolint G204 - command.Env = append(os.Environ(), fmt.Sprintf("%s=1", launchrBackgroundEnvVar)) - - // Set platform-specific process ID - setSysProcAttr(command) - - err = command.Start() - if err != nil { - cmd.Println("Failed to start in background:", err) - return 0, err - } - - err = os.WriteFile(pidFile, []byte(fmt.Sprintf("%d", command.Process.Pid)), os.FileMode(0644)) - if err != nil { - return 0, fmt.Errorf("failed to write PID file: %w", err) - } - - return command.Process.Pid, nil -} - -func redirectOutputs(dir string) error { - err := os.MkdirAll(dir, 0750) - if err != nil { - return fmt.Errorf("can't create plugin temporary directory") - } - - outLog, err := os.Create(fmt.Sprintf("%s/out.log", dir)) - if err != nil { - return err - } - - errLog, err := os.Create(fmt.Sprintf("%s/error.log", dir)) - if err != nil { - return err - } - - os.Stdout = outLog - os.Stderr = errLog - - return nil -} - -func isWebRunning(pidFile string, runInfoDir string) (bool, string) { - url := "" - if isBackGroundEnv() { - return false, url - } - - serverRunInfo, err := server.GetRunInfo(runInfoDir) - if err != nil { - log.Debug(err.Error()) - return false, url - } - - if serverRunInfo != nil { - url = serverRunInfo.BaseURL - } - - if pidFileExists(pidFile) { - pid, errPid := readPidFile(pidFile) - if errPid == nil { - if isProcessRunning(pid) { - return true, url - } - } - } - - if url == "" { - return false, url - } - - isHealthy, err := server.CheckHealth(serverRunInfo.BaseURL) - if err != nil { - log.Debug(err.Error()) - } - - return isHealthy, url -} - -func stopWeb(pidFile, runInfoDir string) error { - onSuccess := "The web server has been successfully shut down." - - if pidFileExists(pidFile) { - pid, err := readPidFile(pidFile) - if err != nil { - return err - } - - if isProcessRunning(pid) { - err = interruptProcess(pid) - if err != nil { - return err - } - - cli.Println(onSuccess) - return nil - } - } - - serverRunInfo, err := server.GetRunInfo(runInfoDir) - if err != nil { - return err - } - - if serverRunInfo == nil { - cli.Println("At present, there is no active server that can be stopped.") - return nil - } - - if serverRunInfo.BaseURL == "" { - panic("An instance of 'run-info' with an empty URL has been detected. Please remove it.") - } - - isHealthy, err := server.CheckHealth(serverRunInfo.BaseURL) - if err != nil { - return err - } - - if isHealthy { - return fmt.Errorf("A foreground server is currently running at the address '%s'. Please stop it via the user interface or terminate the process", serverRunInfo.BaseURL) - } - - cli.Println(onSuccess) - return nil -} - -func getAvailablePort(port int) (int, error) { - // Quick check if port available and return if yes. - if isAvailablePort(port) { - return port, nil - } - - maxPort := 65535 - newPort := 49152 - - // Check available port from pool. - for !isAvailablePort(newPort) && newPort < maxPort { - log.Debug("port %d is not available", newPort) - newPort++ - } - - if newPort >= maxPort && !isAvailablePort(newPort) { - panic("port limit exceeded") - } - - return newPort, nil -} - -func isAvailablePort(port int) bool { - listener, err := net.Listen("tcp", fmt.Sprintf(":%d", port)) - if err != nil { - return false - } - - _ = listener.Close() - return true -} diff --git a/web_runner.go b/web_runner.go new file mode 100644 index 0000000..8937ebf --- /dev/null +++ b/web_runner.go @@ -0,0 +1,356 @@ +package web + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "io" + "net" + "net/http" + "os" + "os/exec" + "path/filepath" + "runtime" + "strconv" + "time" + + "github.com/launchrctl/launchr" + + "github.com/launchrctl/web/server" +) + +const ( + backgroundEnvVar = "LAUNCHR_BACKGROUND" + serverInfoFilename = "server-info.json" +) + +func isBackGroundEnv() bool { + return len(os.Getenv(backgroundEnvVar)) == 1 +} + +func (p *Plugin) runWeb(ctx context.Context, webOpts webFlags) error { + var err error + + port := webOpts.Port + if !isAvailablePort(port) { + launchr.Term().Warning().Printfln("The port %d you are trying to use for the web server is not available.", port) + port, err = getAvailablePort(port) + if err != nil { + return err + } + } + + swaggerFS, _ := GetSwaggerUIAssetsFS() + serverOpts := &server.RunOptions{ + Addr: fmt.Sprintf(":%d", port), // @todo use proper addr + APIPrefix: APIPrefix, + SwaggerJSON: webOpts.UseSwaggerUI, + ProxyClient: webOpts.ProxyClient, + ClientFS: GetClientAssetsFS(), + SwaggerUIFS: swaggerFS, + } + go func() { + time.Sleep(time.Second) + err := openInBrowserWhenReady(serverOpts.BaseURL()) + if err != nil { + launchr.Term().Error().Println(err) + } + }() + + err = storeServerInfo(serverInfo{URL: serverOpts.BaseURL()}, webOpts.PluginDir) + if err != nil { + return err + } + defer cleanupPluginTemp(webOpts.PluginDir) + return server.Run(ctx, p.app, serverOpts) +} + +func (p *Plugin) runBackgroundWeb(cmd *launchr.Command, flags webFlags, pidFile string) error { + if isBackGroundEnv() { + err := redirectOutputs(flags.PluginDir) + if err != nil { + return err + } + + return p.runWeb(cmd.Context(), flags) + } + + pid, err := runBackgroundCmd(cmd, pidFile) + if err != nil { + return err + } + + // Wait until background server is up. + // Check if run info created and server is reachable. + // Print server URL in CLI. + // Kill process in case of timeout + timeout := time.After(10 * time.Second) + ticker := time.NewTicker(1 * time.Second) + + defer ticker.Stop() + + for { + select { + case <-timeout: + // Kill existing process + _ = killProcess(pid) + + // Cleanup temp dir + cleanupPluginTemp(flags.PluginDir) + return errors.New("couldn't start background process") + case <-ticker.C: + info, _ := getServerInfo(flags.PluginDir) + if info == nil { + continue + } + + launchr.Term().Info().Printfln("Web is running in the background (pid: %d)\nURL: %s", pid, info.URL) + return nil + } + } +} + +func stopWeb(pidFile, pluginDir string) (err error) { + onSuccess := "The web UI has been successfully shut down." + + // Try to finish the background process. + pid, ok := pidFileInfo(pidFile) + if pid != 0 && ok { + err = interruptProcess(pid) + if err != nil { + return err + } + + launchr.Term().Success().Println(onSuccess) + return nil + } + + // If we don't have pid, probably there is a server running in foreground. + // We may also not have access to the pid file, prompt user the same. + serverRunInfo, err := getServerInfo(pluginDir) + if err != nil { + return err + } + + if serverRunInfo == nil || serverRunInfo.URL == "" { + launchr.Term().Warning().Println("There is no active Web UI that can be stopped.") + return nil + } + + if checkHealth(serverRunInfo.URL) { + return fmt.Errorf("The web UI is currently running at %s\nPlease stop it through the user interface or terminate the process.", serverRunInfo.URL) + } + + launchr.Term().Success().Println(onSuccess) + return nil +} + +func runBackgroundCmd(cmd *launchr.Command, pidFile string) (int, error) { + err := launchr.EnsurePath(filepath.Dir(pidFile)) + if err != nil { + return 0, fmt.Errorf("cannot create tmp directory for %q", pidFile) + } + + // Prepare the command to restart itself in the background + args := append([]string{cmd.Name()}, os.Args[2:]...) + + command := exec.Command(os.Args[0], args...) //nolint G204 + command.Env = append(os.Environ(), backgroundEnvVar+"=1") + + // Set platform-specific process ID + setSysProcAttr(command) + + err = command.Start() + if err != nil { + return 0, fmt.Errorf("failed to start the process in background: %w", err) + } + + err = os.WriteFile(pidFile, []byte(strconv.Itoa(command.Process.Pid)), os.FileMode(0644)) + if err != nil { + return 0, fmt.Errorf("failed to write PID file: %w", err) + } + + return command.Process.Pid, nil +} + +func redirectOutputs(dir string) error { + err := launchr.EnsurePath(dir) + if err != nil { + return fmt.Errorf("can't create plugin temporary directory") + } + + outLog, err := os.Create(filepath.Join(dir, "out.log")) //nolint G304 // Path is clean. + if err != nil { + return err + } + + // Redirect log messages to a file. + launchr.Log().SetOutput(outLog) + // Discard console output because it's intended for user interaction. + launchr.Term().SetOutput(io.Discard) + + return nil +} + +// serverInfo is structure that stores current running server metadata. +type serverInfo struct { + // URL holds the server's publicly accessible URL. + URL string `json:"url"` +} + +func storeServerInfo(ri serverInfo, storePath string) error { + out, err := json.Marshal(&ri) + if err != nil { + return err + } + + err = os.MkdirAll(storePath, 0750) + if err != nil { + return err + } + err = os.WriteFile(filepath.Join(storePath, serverInfoFilename), out, os.FileMode(0640)) + if err != nil { + return err + } + + return nil +} + +func cleanupPluginTemp(dir string) { + err := os.RemoveAll(dir) + if err != nil { + launchr.Log().Warn("error on server info cleanup", "error", err) + } +} + +// checkHealth helper to check if server is available by request. +func checkHealth(url string) bool { + resp, err := http.Head(url) + if err != nil { + // Error is thrown on an incorrect url. + panic(err) + } + _ = resp.Body.Close() + return resp.StatusCode == http.StatusOK +} + +// getServerInfo lookups server run info metadata and tries to get it from storage. +func getServerInfo(dir string) (*serverInfo, error) { + path := filepath.Clean(filepath.Join(dir, serverInfoFilename)) + + _, err := os.Stat(path) + if os.IsNotExist(err) { + return nil, nil + } else if err != nil { + return nil, err + } + + data, err := os.ReadFile(path) + if err != nil { + return nil, fmt.Errorf("error reading plugin storage path: %w", err) + } + + var info serverInfo + err = json.Unmarshal(data, &info) + if err != nil { + return nil, fmt.Errorf("error unmarshalling json: %w", err) + } + + return &info, nil +} + +func getExistingWeb(pidFile string, pluginDir string) (string, error) { + if isBackGroundEnv() { + // The case was checked on the init step. + return "", nil + } + + serverRunInfo, err := getServerInfo(pluginDir) + if err != nil { + launchr.Log().Warn("error on getting server run info", "error", err) + return "", err + } + if serverRunInfo == nil || serverRunInfo.URL == "" { + // No server. + return "", nil + } + + if _, ok := pidFileInfo(pidFile); ok { + return serverRunInfo.URL, nil + } + + if !checkHealth(serverRunInfo.URL) { + return serverRunInfo.URL, errors.New("web: unhealthy response") + } + + return serverRunInfo.URL, nil +} + +func getAvailablePort(port int) (int, error) { + // Quick check if port available and return if yes. + if isAvailablePort(port) { + return port, nil + } + + maxPort := 65535 + newPort := 49152 + + // Check available port from pool. + for !isAvailablePort(newPort) && newPort < maxPort { + launchr.Log().Debug("port is not available", "port", newPort) + newPort++ + } + + if newPort >= maxPort && !isAvailablePort(newPort) { + panic("port limit exceeded") + } + + return newPort, nil +} + +func isAvailablePort(port int) bool { + listener, err := net.Listen("tcp", fmt.Sprintf(":%d", port)) + if err != nil { + return false + } + + _ = listener.Close() + return true +} + +func openInBrowserWhenReady(url string) error { + // Wait until the service is healthy. + retries := 0 + for !checkHealth(url) { + time.Sleep(time.Second) + if retries == 10 { + return errors.New("the service is unhealthy") + } + retries++ + if retries == 3 { + launchr.Term().Info().Println("The server isn't ready yet, please standby...") + } + launchr.Log().Debug("waiting for server to start", "retries", retries) + } + // Open the browser + launchr.Term().Info().Printfln("You can reach the web server at this URL: %s", url) + if err := openBrowser(url); err != nil { + launchr.Log().Error("failed to open browser", "error", err) + return fmt.Errorf("Failed to open browser: %w", err) + } + return nil +} + +func openBrowser(url string) error { + switch runtime.GOOS { + case "linux": + return exec.Command("xdg-open", url).Start() + case "windows": + return exec.Command("rundll32", "url.dll,FileProtocolHandler", url).Start() + case "darwin": + return exec.Command("open", url).Start() + default: + return fmt.Errorf("unsupported platform") + } +}