├── .gitignore ├── CHANGELOG.md ├── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── cmd └── deployx │ └── main.go ├── commands ├── common.go ├── completion.go ├── deployx.go └── options │ └── opts.go ├── convert ├── compose.go ├── service.go └── volume.go ├── deploy ├── common.go ├── deploy.go ├── deploy_composefile.go └── remove.go ├── docker-bake.hcl ├── go.mod ├── go.sum └── version └── version.go /.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | .vscode 3 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [Unreleased] 9 | 10 | ## [0.0.2] - 2023-06-26 11 | 12 | ### Added 13 | 14 | - Support for cluster volume types (#4). See docker 15 | [cluster volumes](https://github.com/moby/moby/blob/master/docs/cluster_volumes.md) 16 | documentation for additional information. Thanks @TheSilkky for suggesting 17 | this addition! 18 | 19 | ## [0.0.1] - 2023-05-16 20 | 21 | ### Added 22 | 23 | - Initial release of `docker deployx`, a 24 | [Compose-Spec](https://compose-spec.io/) Compliant `docker stack deploy` 25 | Replacement 26 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1 2 | 3 | ARG GO_VERSION=1.20 4 | 5 | FROM golang:${GO_VERSION}-alpine as build-deployx 6 | RUN mkdir /build 7 | WORKDIR /build 8 | 9 | COPY go.mod go.sum /build/ 10 | RUN go mod download && go mod verify 11 | 12 | COPY version /build/version 13 | COPY cmd /build/cmd 14 | COPY commands /build/commands 15 | COPY convert /build/convert 16 | COPY deploy /build/deploy 17 | 18 | RUN go build -o docker-deployx cmd/deployx/* 19 | 20 | FROM alpine AS shell 21 | COPY --from=build-deployx /build/docker-deployx /usr/local/bin 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | https://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | Copyright 2013-2017 Docker, Inc. 180 | 181 | Licensed under the Apache License, Version 2.0 (the "License"); 182 | you may not use this file except in compliance with the License. 183 | You may obtain a copy of the License at 184 | 185 | https://www.apache.org/licenses/LICENSE-2.0 186 | 187 | Unless required by applicable law or agreed to in writing, software 188 | distributed under the License is distributed on an "AS IS" BASIS, 189 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 190 | See the License for the specific language governing permissions and 191 | limitations under the License. 192 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | build_name = docker-deployx 2 | build_path = build 3 | build_target = $(build_path)/$(build_name) 4 | 5 | source_target = cmd/deployx/* 6 | 7 | install_path = $(HOME)/.docker/cli-plugins 8 | install_target = $(install_path)/$(build_name) 9 | 10 | .PHONY: build 11 | build: 12 | go mod download 13 | go build -o $(build_target) $(source_target) 14 | 15 | .PHONY: install 16 | install: build 17 | mkdir -p $(HOME)/.docker/cli-plugins 18 | cp $(build_target) $(install_path) 19 | 20 | .PHONY: uninstall 21 | uninstall: 22 | rm $(install_target) 23 | 24 | .PHONY: clean 25 | clean: 26 | rm -rf $(build_path) 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # deployx 2 | 3 | [![Go Reference](https://pkg.go.dev/badge/github.com/aaraney/deployx.svg)](https://pkg.go.dev/github.com/aaraney/deployx) 4 | 5 | `deployx` is a Docker CLI plugin rewrite of `docker stack deploy` that is compliant 6 | with [compose-spec](https://github.com/compose-spec/compose-spec). 7 | 8 | ## Features 9 | 10 | - Compose file variable expansion (done by default) 11 | - Support for one or more alternate `.env` files 12 | - Support for container storage interface (CSI) [cluster volume](https://github.com/moby/moby/blob/master/docs/cluster_volumes.md) types 13 | 14 | ## Installing 15 | 16 | ### Building from source 17 | 18 | Building from source requires go and make be installed. Install go using either your package manager 19 | of choice (i.e. `brew`) or by following [these instructions](https://go.dev/doc/install). 20 | 21 | ```shell 22 | # clone repo 23 | git clone https://github.com/aaraney/deployx.git && cd deployx 24 | 25 | make build && make install 26 | 27 | # uninstall 28 | # make uninstall 29 | ``` 30 | 31 | Note, the instructions above install `deployx` for a single user. To install globally run `make build` 32 | and copy the `docker-deployx` binary from the `./build` directory to one of the following locations: 33 | 34 | - `/usr/local/lib/docker/cli-plugins` OR `/usr/local/libexec/docker/cli-plugins` 35 | - `/usr/lib/docker/cli-plugins` OR `/usr/libexec/docker/cli-plugins` 36 | 37 | ### Brew 38 | 39 | Install using `homebrew`: 40 | 41 | ```shell 42 | brew install aaraney/tap/deployx 43 | 44 | # install as docker cli plugin. invoke using 'docker deployx' 45 | ln -s $(which deployx) $HOME/.docker/cli-plugin/docker-deployx 46 | ``` 47 | 48 | ### Dockerfile 49 | 50 | The easiest way to get started it using a pre-built docker image and `deployx` in standalone mode. 51 | The following snippet shows pulling and running `deployx`. Adjust the volume mount accordingly to 52 | mount your compose and env files. 53 | 54 | ```shell 55 | docker run -it --rm --volume $(pwd):/home --volume /var/run/docker.sock:/var/run/docker.sock aaraney/docker-deployx 56 | docker-deployx --compose-file /home/ mystack 57 | ``` 58 | 59 | ## Usage 60 | 61 | Use `deployx` just as you would `docker stack deploy` by instead calling `docker deployx`. Unlike 62 | `docker stack deploy`, environment and `.env` variables are interpolated into compose files. So, 63 | there is no longer a need to: `docker stack deploy -c <(docker-compose config) stack-name`. 64 | 65 | ```shell 66 | $ docker deployx 67 | Usage: docker deployx [OPTIONS] STACK 68 | 69 | Deploy a new stack or update an existing stack 70 | 71 | Options: 72 | -c, --compose-file strings Path to a Compose file, or "-" to read from stdin 73 | --env-file strings Path to an alternative env file, or "-" to read from stdin 74 | --no-interpolate Don't perform environment variable interpolation 75 | --prune Prune services that are no longer referenced 76 | --resolve-image string Query the registry to resolve image digest and supported platforms ("always", "changed", "never") (default "always") 77 | --with-registry-auth Send registry authentication details to Swarm agents 78 | ``` 79 | -------------------------------------------------------------------------------- /cmd/deployx/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | "github.com/aaraney/deployx/commands" 8 | "github.com/aaraney/deployx/version" 9 | 10 | "github.com/docker/cli/cli" 11 | "github.com/docker/cli/cli-plugins/manager" 12 | "github.com/docker/cli/cli-plugins/plugin" 13 | "github.com/docker/cli/cli/command" 14 | cliflags "github.com/docker/cli/cli/flags" 15 | ) 16 | 17 | func runStandalone(cmd *command.DockerCli) error { 18 | if err := cmd.Initialize(cliflags.NewClientOptions()); err != nil { 19 | return err 20 | } 21 | rootCmd := commands.NewDeployxCommand(cmd, false) 22 | return rootCmd.Execute() 23 | } 24 | 25 | func runPlugin(cmd *command.DockerCli) error { 26 | rootCmd := commands.NewDeployxCommand(cmd, true) 27 | return plugin.RunPlugin(cmd, rootCmd, manager.Metadata{ 28 | SchemaVersion: "0.1.0", 29 | Vendor: "aaraney", // TODO: not sure who to put here 30 | Version: version.Version, 31 | URL: "github.com/aaraney/deployx", 32 | }) 33 | } 34 | 35 | func main() { 36 | cmd, err := command.NewDockerCli() 37 | if err != nil { 38 | fmt.Fprintln(cmd.Err(), err) 39 | os.Exit(1) 40 | } 41 | 42 | if plugin.RunningStandalone() { 43 | err = runStandalone(cmd) 44 | } else { 45 | err = runPlugin(cmd) 46 | } 47 | if err == nil { 48 | return 49 | } 50 | 51 | if sterr, ok := err.(cli.StatusError); ok { 52 | if sterr.Status != "" { 53 | fmt.Fprintln(cmd.Err(), sterr.Status) 54 | } 55 | // StatusError should only be used for errors, and all errors should 56 | // have a non-zero exit status, so never exit with 0 57 | if sterr.StatusCode == 0 { 58 | os.Exit(1) 59 | } 60 | os.Exit(sterr.StatusCode) 61 | } 62 | 63 | fmt.Fprintln(cmd.Err(), err) 64 | os.Exit(1) 65 | } 66 | -------------------------------------------------------------------------------- /commands/common.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strings" 7 | "unicode" 8 | ) 9 | 10 | // validateStackName checks if the provided string is a valid stack name (namespace). 11 | // It currently only does a rudimentary check if the string is empty, or consists 12 | // of only whitespace and quoting characters. 13 | func validateStackName(namespace string) error { 14 | v := strings.TrimFunc(namespace, quotesOrWhitespace) 15 | if v == "" { 16 | return fmt.Errorf("invalid stack name: %q", namespace) 17 | } 18 | return nil 19 | } 20 | 21 | func quotesOrWhitespace(r rune) bool { 22 | return unicode.IsSpace(r) || r == '"' || r == '\'' 23 | } 24 | 25 | // merge the right map into the left map. intersecting keys are returned but not replaced in the 26 | // left map. 27 | func leftMerge(left, right map[string]string) []string { 28 | right_in_left := []string{} 29 | 30 | for key, value := range right { 31 | if _, ok := left[key]; ok { 32 | right_in_left = append(right_in_left, value) 33 | continue 34 | } 35 | left[key] = value 36 | } 37 | 38 | return right_in_left 39 | } 40 | 41 | // retrieves environment variables, split them on '=', and returns them as map[string]string. 42 | func environMap() map[string]string { 43 | env := os.Environ() 44 | env_map := make(map[string]string) 45 | for _, variable := range env { 46 | split_loc := strings.IndexRune(variable, '=') 47 | variable_name := variable[:split_loc] 48 | variable_value := variable[split_loc+1:] 49 | env_map[variable_name] = variable_value 50 | } 51 | return env_map 52 | } 53 | -------------------------------------------------------------------------------- /commands/completion.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | import ( 4 | "github.com/docker/cli/cli/command" 5 | "github.com/docker/cli/cli/command/completion" 6 | "github.com/docker/docker/api/types" 7 | "github.com/spf13/cobra" 8 | ) 9 | 10 | // completeNames offers completion for swarm configs 11 | func completeNames(dockerCli command.Cli) completion.ValidArgsFn { 12 | return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { 13 | list, err := dockerCli.Client().ConfigList(cmd.Context(), types.ConfigListOptions{}) 14 | if err != nil { 15 | return nil, cobra.ShellCompDirectiveError 16 | } 17 | var names []string 18 | for _, config := range list { 19 | names = append(names, config.ID) 20 | } 21 | return names, cobra.ShellCompDirectiveNoFileComp 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /commands/deployx.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | import ( 4 | "errors" 5 | "os" 6 | 7 | "github.com/aaraney/deployx/commands/options" 8 | "github.com/aaraney/deployx/deploy" 9 | 10 | composego_cli "github.com/compose-spec/compose-go/cli" 11 | "github.com/compose-spec/compose-go/loader" 12 | composego "github.com/compose-spec/compose-go/types" 13 | "github.com/docker/cli/cli" 14 | "github.com/docker/cli/cli-plugins/plugin" 15 | "github.com/docker/cli/cli/command" 16 | "github.com/docker/cli/cli/command/stack/swarm" 17 | "github.com/spf13/cobra" 18 | ) 19 | 20 | func NewDeployxCommand(dockerCli command.Cli, isPlugin bool) *cobra.Command { 21 | var opts options.Deploy 22 | 23 | cmd := &cobra.Command{ 24 | Use: "deployx [OPTIONS] STACK", 25 | Short: "Deploy a new stack or update an existing stack", 26 | Args: cli.RequiresMaxArgs(1), 27 | RunE: func(cmd *cobra.Command, args []string) error { 28 | if len(args) < 1 { 29 | cmd.Usage() 30 | return nil 31 | } 32 | opts.Namespace = args[0] 33 | return runDeployxCommand(dockerCli, &opts) 34 | }, 35 | ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { 36 | return completeNames(dockerCli)(cmd, args, toComplete) 37 | }, 38 | } 39 | if isPlugin { 40 | cmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) error { 41 | return plugin.PersistentPreRunE(cmd, args) 42 | } 43 | } else { 44 | // match plugin behavior for standalone mode 45 | // https://github.com/docker/cli/blob/6c9eb708fa6d17765d71965f90e1c59cea686ee9/cli-plugins/plugin/plugin.go#L117-L127 46 | cmd.SilenceUsage = true 47 | cmd.SilenceErrors = true 48 | cmd.TraverseChildren = true 49 | cmd.DisableFlagsInUseLine = true 50 | cli.DisableFlagsInUseLine(cmd) 51 | } 52 | 53 | flags := cmd.Flags() 54 | flags.StringSliceVarP(&opts.Composefiles, "compose-file", "c", []string{}, `Path to a Compose file, or "-" to read from stdin`) 55 | flags.SetAnnotation("compose-file", "version", []string{"2"}) 56 | flags.StringSliceVar(&opts.Envfiles, "env-file", []string{}, `Path to an alternative env file, or "-" to read from stdin`) 57 | flags.BoolVar(&opts.NoInterpolate, "no-interpolate", false, "Don't perform environment variable interpolation") 58 | flags.BoolVar(&opts.SendRegistryAuth, "with-registry-auth", false, "Send registry authentication details to Swarm agents") 59 | flags.BoolVar(&opts.Prune, "prune", false, "Prune services that are no longer referenced") 60 | flags.SetAnnotation("prune", "version", []string{"1.27"}) 61 | flags.StringVar(&opts.ResolveImage, "resolve-image", swarm.ResolveImageAlways, 62 | `Query the registry to resolve image digest and supported platforms ("`+swarm.ResolveImageAlways+`", "`+swarm.ResolveImageChanged+`", "`+swarm.ResolveImageNever+`")`) 63 | flags.SetAnnotation("resolve-image", "version", []string{"1.30"}) 64 | return cmd 65 | } 66 | 67 | func getEnv(workingDir string, envFiles []string) (map[string]string, error) { 68 | env := environMap() 69 | new_env, err := composego_cli.GetEnvFromFile(env, workingDir, envFiles) 70 | if err != nil { 71 | return env, err 72 | } 73 | 74 | // TODO: right now intersection does not overwrite. consider if it should 75 | leftMerge(env, new_env) 76 | 77 | return env, nil 78 | } 79 | 80 | func runDeployxCommand(dockerCli command.Cli, opts *options.Deploy) error { 81 | if opts == nil { 82 | return errors.New("options.Deploy is nil") 83 | } 84 | 85 | if err := validateStackName(opts.Namespace); err != nil { 86 | return err 87 | } 88 | 89 | workingDir, err := os.Getwd() 90 | if err != nil { 91 | return err 92 | } 93 | 94 | env := make(map[string]string) 95 | if !opts.NoInterpolate { 96 | // NOTE: if no env files are provided, .env file is default 97 | env, err = getEnv(workingDir, opts.Envfiles) 98 | if err != nil { 99 | return err 100 | } 101 | } 102 | 103 | if len(opts.Composefiles) < 1 { 104 | return errors.New("Please specify a Compose file (with --compose-file)") 105 | } 106 | 107 | var compose_files []composego.ConfigFile 108 | for _, compose_file := range opts.Composefiles { 109 | compose_files = append(compose_files, composego.ConfigFile{Filename: compose_file}) 110 | } 111 | 112 | config, err := loader.Load(composego.ConfigDetails{ 113 | WorkingDir: workingDir, 114 | ConfigFiles: compose_files, 115 | Environment: env, 116 | }, func(options *loader.Options) { 117 | options.SetProjectName(opts.Namespace, true) 118 | }) 119 | if err != nil { 120 | return err 121 | } 122 | 123 | cfg := composego.Config{ 124 | // Filename: filename, // note sure if this is right since this could be merged compose files 125 | Name: config.Name, 126 | Services: config.Services, 127 | Networks: config.Networks, 128 | Volumes: config.Volumes, 129 | Secrets: config.Secrets, 130 | Configs: config.Configs, 131 | Extensions: config.Extensions, 132 | } 133 | return deploy.RunDeploy(dockerCli, *opts, &cfg) 134 | } 135 | -------------------------------------------------------------------------------- /commands/options/opts.go: -------------------------------------------------------------------------------- 1 | package options 2 | 3 | // Deploy holds docker stack deploy options 4 | type Deploy struct { 5 | Composefiles []string 6 | Namespace string 7 | ResolveImage string 8 | SendRegistryAuth bool 9 | Prune bool 10 | Envfiles []string 11 | NoInterpolate bool 12 | } 13 | -------------------------------------------------------------------------------- /convert/compose.go: -------------------------------------------------------------------------------- 1 | package convert 2 | 3 | import ( 4 | "os" 5 | "strings" 6 | 7 | composego "github.com/compose-spec/compose-go/types" 8 | "github.com/docker/docker/api/types" 9 | networktypes "github.com/docker/docker/api/types/network" 10 | "github.com/docker/docker/api/types/swarm" 11 | ) 12 | 13 | const ( 14 | // LabelNamespace is the label used to track stack resources 15 | LabelNamespace = "com.docker.stack.namespace" 16 | ) 17 | 18 | // Namespace mangles names by prepending the name 19 | type Namespace struct { 20 | name string 21 | } 22 | 23 | // Scope prepends the namespace to a name 24 | func (n Namespace) Scope(name string) string { 25 | return n.name + "_" + name 26 | } 27 | 28 | // Descope returns the name without the namespace prefix 29 | func (n Namespace) Descope(name string) string { 30 | return strings.TrimPrefix(name, n.name+"_") 31 | } 32 | 33 | // Name returns the name of the namespace 34 | func (n Namespace) Name() string { 35 | return n.name 36 | } 37 | 38 | // NewNamespace returns a new Namespace for scoping of names 39 | func NewNamespace(name string) Namespace { 40 | return Namespace{name: name} 41 | } 42 | 43 | // AddStackLabel returns labels with the namespace label added 44 | func AddStackLabel(namespace Namespace, labels map[string]string) map[string]string { 45 | if labels == nil { 46 | labels = make(map[string]string) 47 | } 48 | labels[LabelNamespace] = namespace.name 49 | return labels 50 | } 51 | 52 | // Networks from the compose-file type to the engine API type 53 | func Networks(namespace Namespace, networks composego.Networks, servicesNetworks map[string]struct{}) (map[string]types.NetworkCreate, []string) { 54 | if networks == nil { 55 | networks = make(composego.Networks) 56 | } 57 | 58 | externalNetworks := []string{} 59 | result := make(map[string]types.NetworkCreate) 60 | for internalName := range servicesNetworks { 61 | network := networks[internalName] 62 | if network.External.External { 63 | externalNetworks = append(externalNetworks, network.Name) 64 | continue 65 | } 66 | 67 | createOpts := types.NetworkCreate{ 68 | Labels: AddStackLabel(namespace, network.Labels), 69 | Driver: network.Driver, 70 | Options: network.DriverOpts, 71 | Internal: network.Internal, 72 | Attachable: network.Attachable, 73 | } 74 | 75 | if network.Ipam.Driver != "" || len(network.Ipam.Config) > 0 { 76 | createOpts.IPAM = &networktypes.IPAM{} 77 | } 78 | 79 | if network.Ipam.Driver != "" { 80 | createOpts.IPAM.Driver = network.Ipam.Driver 81 | } 82 | for _, ipamConfig := range network.Ipam.Config { 83 | config := networktypes.IPAMConfig{ 84 | Subnet: ipamConfig.Subnet, 85 | } 86 | createOpts.IPAM.Config = append(createOpts.IPAM.Config, config) 87 | } 88 | 89 | networkName := namespace.Scope(internalName) 90 | if network.Name != "" { 91 | networkName = network.Name 92 | } 93 | result[networkName] = createOpts 94 | } 95 | 96 | return result, externalNetworks 97 | } 98 | 99 | // Secrets converts secrets from the Compose type to the engine API type 100 | func Secrets(namespace Namespace, secrets composego.Secrets) ([]swarm.SecretSpec, error) { 101 | result := []swarm.SecretSpec{} 102 | for name, secret := range secrets { 103 | if secret.External.External { 104 | continue 105 | } 106 | 107 | var obj SwarmFileObject 108 | var err error 109 | if secret.Driver != "" { 110 | obj = driverObjectConfig(namespace, name, composego.FileObjectConfig(secret)) 111 | } else { 112 | obj, err = fileObjectConfig(namespace, name, composego.FileObjectConfig(secret)) 113 | } 114 | if err != nil { 115 | return nil, err 116 | } 117 | spec := swarm.SecretSpec{Annotations: obj.Annotations, Data: obj.Data} 118 | if secret.Driver != "" { 119 | spec.Driver = &swarm.Driver{ 120 | Name: secret.Driver, 121 | Options: secret.DriverOpts, 122 | } 123 | } 124 | if secret.TemplateDriver != "" { 125 | spec.Templating = &swarm.Driver{ 126 | Name: secret.TemplateDriver, 127 | } 128 | } 129 | result = append(result, spec) 130 | } 131 | return result, nil 132 | } 133 | 134 | func Configs(namespace Namespace, configs map[string]composego.ConfigObjConfig) ([]swarm.ConfigSpec, error) { 135 | result := []swarm.ConfigSpec{} 136 | for name, config := range configs { 137 | if config.External.External { 138 | continue 139 | } 140 | 141 | obj, err := fileObjectConfig(namespace, name, composego.FileObjectConfig(config)) 142 | if err != nil { 143 | return nil, err 144 | } 145 | spec := swarm.ConfigSpec{Annotations: obj.Annotations, Data: obj.Data} 146 | if config.TemplateDriver != "" { 147 | spec.Templating = &swarm.Driver{ 148 | Name: config.TemplateDriver, 149 | } 150 | } 151 | result = append(result, spec) 152 | } 153 | return result, nil 154 | } 155 | 156 | type SwarmFileObject struct { 157 | Annotations swarm.Annotations 158 | Data []byte 159 | } 160 | 161 | func driverObjectConfig(namespace Namespace, name string, obj composego.FileObjectConfig) SwarmFileObject { 162 | if obj.Name != "" { 163 | name = obj.Name 164 | } else { 165 | name = namespace.Scope(name) 166 | } 167 | 168 | return SwarmFileObject{ 169 | Annotations: swarm.Annotations{ 170 | Name: name, 171 | Labels: AddStackLabel(namespace, obj.Labels), 172 | }, 173 | Data: []byte{}, 174 | } 175 | } 176 | 177 | func fileObjectConfig(namespace Namespace, name string, obj composego.FileObjectConfig) (SwarmFileObject, error) { 178 | data, err := os.ReadFile(obj.File) 179 | if err != nil { 180 | return SwarmFileObject{}, err 181 | } 182 | 183 | if obj.Name != "" { 184 | name = obj.Name 185 | } else { 186 | name = namespace.Scope(name) 187 | } 188 | 189 | return SwarmFileObject{ 190 | Annotations: swarm.Annotations{ 191 | Name: name, 192 | Labels: AddStackLabel(namespace, obj.Labels), 193 | }, 194 | Data: data, 195 | }, nil 196 | } 197 | -------------------------------------------------------------------------------- /convert/service.go: -------------------------------------------------------------------------------- 1 | package convert 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "sort" 7 | "strconv" 8 | "strings" 9 | "time" 10 | 11 | composego "github.com/compose-spec/compose-go/types" 12 | servicecli "github.com/docker/cli/cli/command/service" 13 | "github.com/docker/cli/opts" 14 | "github.com/docker/docker/api/types/container" 15 | "github.com/docker/docker/api/types/swarm" 16 | "github.com/docker/docker/api/types/versions" 17 | "github.com/docker/docker/client" 18 | apiclient "github.com/docker/docker/client" 19 | "github.com/docker/go-units" 20 | "github.com/pkg/errors" 21 | ) 22 | 23 | const ( 24 | defaultNetwork = "default" 25 | // LabelImage is the label used to store image name provided in the compose file 26 | LabelImage = "com.docker.stack.image" 27 | ) 28 | 29 | // Services from compose-file types to engine API types 30 | func Services( 31 | namespace Namespace, 32 | config *composego.Config, 33 | client client.CommonAPIClient, 34 | ) (map[string]swarm.ServiceSpec, error) { 35 | result := make(map[string]swarm.ServiceSpec) 36 | 37 | services := config.Services 38 | volumes := config.Volumes 39 | networks := config.Networks 40 | 41 | for _, service := range services { 42 | secrets, err := convertServiceSecrets(client, namespace, service.Secrets, config.Secrets) 43 | if err != nil { 44 | return nil, errors.Wrapf(err, "service %s", service.Name) 45 | } 46 | configs, err := convertServiceConfigObjs(client, namespace, service, config.Configs) 47 | if err != nil { 48 | return nil, errors.Wrapf(err, "service %s", service.Name) 49 | } 50 | 51 | serviceSpec, err := Service(client.ClientVersion(), namespace, service, networks, volumes, secrets, configs) 52 | if err != nil { 53 | return nil, errors.Wrapf(err, "service %s", service.Name) 54 | } 55 | result[service.Name] = serviceSpec 56 | } 57 | 58 | return result, nil 59 | } 60 | 61 | // Service converts a ServiceConfig into a swarm ServiceSpec 62 | func Service( 63 | apiVersion string, 64 | namespace Namespace, 65 | service composego.ServiceConfig, 66 | networkConfigs map[string]composego.NetworkConfig, 67 | volumes map[string]composego.VolumeConfig, 68 | secrets []*swarm.SecretReference, 69 | configs []*swarm.ConfigReference, 70 | ) (swarm.ServiceSpec, error) { 71 | name := namespace.Scope(service.Name) 72 | 73 | deploy := service.Deploy 74 | if deploy == nil { 75 | // something 76 | deploy = &composego.DeployConfig{} 77 | } 78 | endpoint, err := convertEndpointSpec(deploy.EndpointMode, service.Ports) 79 | if err != nil { 80 | return swarm.ServiceSpec{}, err 81 | } 82 | 83 | mode, err := convertDeployMode(deploy.Mode, deploy.Replicas) 84 | if err != nil { 85 | return swarm.ServiceSpec{}, err 86 | } 87 | 88 | mounts, err := Volumes(service.Volumes, volumes, namespace) 89 | if err != nil { 90 | return swarm.ServiceSpec{}, err 91 | } 92 | 93 | resources, err := convertResources(deploy.Resources) 94 | if err != nil { 95 | return swarm.ServiceSpec{}, err 96 | } 97 | 98 | restartPolicy, err := convertRestartPolicy( 99 | service.Restart, deploy.RestartPolicy) 100 | if err != nil { 101 | return swarm.ServiceSpec{}, err 102 | } 103 | 104 | healthCheck := service.HealthCheck 105 | if healthCheck == nil { 106 | healthCheck = &composego.HealthCheckConfig{} 107 | } 108 | healthcheck, err := convertHealthcheck(healthCheck) 109 | if err != nil { 110 | return swarm.ServiceSpec{}, err 111 | } 112 | 113 | networks, err := convertServiceNetworks(service.Networks, networkConfigs, namespace, service.Name) 114 | if err != nil { 115 | return swarm.ServiceSpec{}, err 116 | } 117 | 118 | dnsConfig := convertDNSConfig(service.DNS, service.DNSSearch) 119 | 120 | var privileges swarm.Privileges 121 | credentialSpec := service.CredentialSpec 122 | if credentialSpec == nil { 123 | credentialSpec = &composego.CredentialSpecConfig{} 124 | } 125 | privileges.CredentialSpec, err = convertCredentialSpec( 126 | namespace, *credentialSpec, configs, 127 | ) 128 | if err != nil { 129 | return swarm.ServiceSpec{}, err 130 | } 131 | 132 | var logDriver *swarm.Driver 133 | if service.Logging != nil { 134 | logDriver = &swarm.Driver{ 135 | Name: service.Logging.Driver, 136 | Options: service.Logging.Options, 137 | } 138 | } 139 | 140 | capAdd, capDrop := opts.EffectiveCapAddCapDrop(service.CapAdd, service.CapDrop) 141 | 142 | serviceSpec := swarm.ServiceSpec{ 143 | Annotations: swarm.Annotations{ 144 | Name: name, 145 | Labels: AddStackLabel(namespace, deploy.Labels), 146 | }, 147 | TaskTemplate: swarm.TaskSpec{ 148 | ContainerSpec: &swarm.ContainerSpec{ 149 | Image: service.Image, 150 | Command: service.Entrypoint, 151 | Args: service.Command, 152 | Hostname: service.Hostname, 153 | Hosts: convertExtraHosts(service.ExtraHosts), 154 | DNSConfig: dnsConfig, 155 | Healthcheck: healthcheck, 156 | Env: sortStrings(convertEnvironment(service.Environment)), 157 | Labels: AddStackLabel(namespace, service.Labels), 158 | Dir: service.WorkingDir, 159 | User: service.User, 160 | Mounts: mounts, 161 | StopGracePeriod: composego.ConvertDurationPtr(service.StopGracePeriod), 162 | StopSignal: service.StopSignal, 163 | TTY: service.Tty, 164 | OpenStdin: service.StdinOpen, 165 | Secrets: secrets, 166 | Configs: configs, 167 | ReadOnly: service.ReadOnly, 168 | Privileges: &privileges, 169 | Isolation: container.Isolation(service.Isolation), 170 | Init: service.Init, 171 | Sysctls: service.Sysctls, 172 | CapabilityAdd: capAdd, 173 | CapabilityDrop: capDrop, 174 | Ulimits: convertUlimits(service.Ulimits), 175 | }, 176 | LogDriver: logDriver, 177 | Resources: resources, 178 | RestartPolicy: restartPolicy, 179 | Placement: &swarm.Placement{ 180 | Constraints: deploy.Placement.Constraints, 181 | Preferences: getPlacementPreference(deploy.Placement.Preferences), 182 | MaxReplicas: deploy.Placement.MaxReplicas, 183 | }, 184 | }, 185 | EndpointSpec: endpoint, 186 | Mode: mode, 187 | UpdateConfig: convertUpdateConfig(deploy.UpdateConfig), 188 | RollbackConfig: convertUpdateConfig(deploy.RollbackConfig), 189 | } 190 | 191 | // add an image label to serviceSpec 192 | serviceSpec.Labels[LabelImage] = service.Image 193 | 194 | // ServiceSpec.Networks is deprecated and should not have been used by 195 | // this package. It is possible to update TaskTemplate.Networks, but it 196 | // is not possible to update ServiceSpec.Networks. Unfortunately, we 197 | // can't unconditionally start using TaskTemplate.Networks, because that 198 | // will break with older daemons that don't support migrating from 199 | // ServiceSpec.Networks to TaskTemplate.Networks. So which field to use 200 | // is conditional on daemon version. 201 | if versions.LessThan(apiVersion, "1.29") { 202 | serviceSpec.Networks = networks 203 | } else { 204 | serviceSpec.TaskTemplate.Networks = networks 205 | } 206 | return serviceSpec, nil 207 | } 208 | 209 | func getPlacementPreference(preferences []composego.PlacementPreferences) []swarm.PlacementPreference { 210 | result := []swarm.PlacementPreference{} 211 | for _, preference := range preferences { 212 | spreadDescriptor := preference.Spread 213 | result = append(result, swarm.PlacementPreference{ 214 | Spread: &swarm.SpreadOver{ 215 | SpreadDescriptor: spreadDescriptor, 216 | }, 217 | }) 218 | } 219 | return result 220 | } 221 | 222 | func sortStrings(strs []string) []string { 223 | sort.Strings(strs) 224 | return strs 225 | } 226 | 227 | func convertServiceNetworks( 228 | networks map[string]*composego.ServiceNetworkConfig, 229 | networkConfigs composego.Networks, 230 | namespace Namespace, 231 | name string, 232 | ) ([]swarm.NetworkAttachmentConfig, error) { 233 | if len(networks) == 0 { 234 | networks = map[string]*composego.ServiceNetworkConfig{ 235 | defaultNetwork: {}, 236 | } 237 | } 238 | 239 | nets := []swarm.NetworkAttachmentConfig{} 240 | for networkName, network := range networks { 241 | networkConfig, ok := networkConfigs[networkName] 242 | if !ok && networkName != defaultNetwork { 243 | return nil, errors.Errorf("undefined network %q", networkName) 244 | } 245 | var aliases []string 246 | if network != nil { 247 | aliases = network.Aliases 248 | } 249 | target := namespace.Scope(networkName) 250 | if networkConfig.Name != "" { 251 | target = networkConfig.Name 252 | } 253 | netAttachConfig := swarm.NetworkAttachmentConfig{ 254 | Target: target, 255 | Aliases: aliases, 256 | } 257 | // Only add default aliases to user defined networks. Other networks do 258 | // not support aliases. 259 | if container.NetworkMode(target).IsUserDefined() { 260 | netAttachConfig.Aliases = append(netAttachConfig.Aliases, name) 261 | } 262 | nets = append(nets, netAttachConfig) 263 | } 264 | 265 | sort.Slice(nets, func(i, j int) bool { 266 | return nets[i].Target < nets[j].Target 267 | }) 268 | return nets, nil 269 | } 270 | 271 | // TODO: fix secrets API so that SecretAPIClient is not required here 272 | func convertServiceSecrets( 273 | client apiclient.SecretAPIClient, 274 | namespace Namespace, 275 | secrets []composego.ServiceSecretConfig, 276 | secretSpecs composego.Secrets, 277 | ) ([]*swarm.SecretReference, error) { 278 | refs := []*swarm.SecretReference{} 279 | 280 | lookup := func(key string) (composego.FileObjectConfig, error) { 281 | secretSpec, exists := secretSpecs[key] 282 | if !exists { 283 | return composego.FileObjectConfig{}, errors.Errorf("undefined secret %q", key) 284 | } 285 | return composego.FileObjectConfig(secretSpec), nil 286 | } 287 | for _, secret := range secrets { 288 | obj, err := convertFileObject(namespace, composego.FileReferenceConfig(secret), lookup) 289 | if err != nil { 290 | return nil, err 291 | } 292 | 293 | file := swarm.SecretReferenceFileTarget(obj.File) 294 | refs = append(refs, &swarm.SecretReference{ 295 | File: &file, 296 | SecretName: obj.Name, 297 | }) 298 | } 299 | 300 | secrs, err := servicecli.ParseSecrets(client, refs) 301 | if err != nil { 302 | return nil, err 303 | } 304 | // sort to ensure idempotence (don't restart services just because the entries are in different order) 305 | sort.SliceStable(secrs, func(i, j int) bool { return secrs[i].SecretName < secrs[j].SecretName }) 306 | return secrs, err 307 | } 308 | 309 | func convertServiceConfigObjs( 310 | client apiclient.ConfigAPIClient, 311 | namespace Namespace, 312 | service composego.ServiceConfig, 313 | configSpecs composego.Configs, 314 | ) ([]*swarm.ConfigReference, error) { 315 | refs := []*swarm.ConfigReference{} 316 | 317 | lookup := func(key string) (composego.FileObjectConfig, error) { 318 | configSpec, exists := configSpecs[key] 319 | if !exists { 320 | return composego.FileObjectConfig{}, errors.Errorf("undefined config %q", key) 321 | } 322 | return composego.FileObjectConfig(configSpec), nil 323 | } 324 | for _, config := range service.Configs { 325 | obj, err := convertFileObject(namespace, composego.FileReferenceConfig(config), lookup) 326 | if err != nil { 327 | return nil, err 328 | } 329 | 330 | file := swarm.ConfigReferenceFileTarget(obj.File) 331 | refs = append(refs, &swarm.ConfigReference{ 332 | File: &file, 333 | ConfigName: obj.Name, 334 | }) 335 | } 336 | 337 | // finally, after converting all of the file objects, create any 338 | // Runtime-type configs that are needed. these are configs that are not 339 | // mounted into the container, but are used in some other way by the 340 | // container runtime. Currently, this only means CredentialSpecs, but in 341 | // the future it may be used for other fields 342 | 343 | if service.CredentialSpec != nil { 344 | // grab the CredentialSpec out of the Service 345 | credSpec := service.CredentialSpec 346 | // if the credSpec uses a config, then we should grab the config name, and 347 | // create a config reference for it. A File or Registry-type CredentialSpec 348 | // does not need this operation. 349 | if credSpec.Config != "" { 350 | // look up the config in the configSpecs. 351 | obj, err := lookup(credSpec.Config) 352 | if err != nil { 353 | return nil, err 354 | } 355 | 356 | // get the actual correct name. 357 | name := namespace.Scope(credSpec.Config) 358 | if obj.Name != "" { 359 | name = obj.Name 360 | } 361 | 362 | // now append a Runtime-type config. 363 | refs = append(refs, &swarm.ConfigReference{ 364 | ConfigName: name, 365 | Runtime: &swarm.ConfigReferenceRuntimeTarget{}, 366 | }) 367 | } 368 | } 369 | 370 | confs, err := servicecli.ParseConfigs(client, refs) 371 | if err != nil { 372 | return nil, err 373 | } 374 | // sort to ensure idempotence (don't restart services just because the entries are in different order) 375 | sort.SliceStable(confs, func(i, j int) bool { return confs[i].ConfigName < confs[j].ConfigName }) 376 | return confs, err 377 | } 378 | 379 | type SwarmReferenceTarget struct { 380 | Name string 381 | UID string 382 | GID string 383 | Mode os.FileMode 384 | } 385 | 386 | type SwarmReferenceObject struct { 387 | File SwarmReferenceTarget 388 | ID string 389 | Name string 390 | } 391 | 392 | func convertFileObject( 393 | namespace Namespace, 394 | config composego.FileReferenceConfig, 395 | lookup func(key string) (composego.FileObjectConfig, error), 396 | ) (SwarmReferenceObject, error) { 397 | obj, err := lookup(config.Source) 398 | if err != nil { 399 | return SwarmReferenceObject{}, err 400 | } 401 | 402 | source := namespace.Scope(config.Source) 403 | if obj.Name != "" { 404 | source = obj.Name 405 | } 406 | 407 | target := config.Target 408 | if target == "" { 409 | target = config.Source 410 | } 411 | 412 | uid := config.UID 413 | gid := config.GID 414 | if uid == "" { 415 | uid = "0" 416 | } 417 | if gid == "" { 418 | gid = "0" 419 | } 420 | mode := config.Mode 421 | if mode == nil { 422 | mode = uint32Ptr(0o444) 423 | } 424 | 425 | return SwarmReferenceObject{ 426 | File: SwarmReferenceTarget{ 427 | Name: target, 428 | UID: uid, 429 | GID: gid, 430 | Mode: os.FileMode(*mode), 431 | }, 432 | Name: source, 433 | }, nil 434 | } 435 | 436 | func uint32Ptr(value uint32) *uint32 { 437 | return &value 438 | } 439 | 440 | // convertExtraHosts converts : mappings to SwarmKit notation: 441 | // "IP-address hostname(s)". The original order of mappings is preserved. 442 | func convertExtraHosts(extraHosts composego.HostsList) []string { 443 | hosts := make([]string, 0, len(extraHosts)) 444 | for _, hostIP := range extraHosts { 445 | if hostName, ipAddr, ok := strings.Cut(hostIP, ":"); ok { 446 | // Convert to SwarmKit notation: IP-address hostname(s) 447 | hosts = append(hosts, ipAddr+" "+hostName) 448 | } 449 | } 450 | return hosts 451 | } 452 | 453 | func convertHealthcheck(healthcheck *composego.HealthCheckConfig) (*container.HealthConfig, error) { 454 | if healthcheck == nil { 455 | return nil, nil 456 | } 457 | var ( 458 | timeout, interval, startPeriod time.Duration 459 | retries int 460 | ) 461 | if healthcheck.Disable { 462 | if len(healthcheck.Test) != 0 { 463 | return nil, errors.Errorf("test and disable can't be set at the same time") 464 | } 465 | return &container.HealthConfig{ 466 | Test: []string{"NONE"}, 467 | }, nil 468 | 469 | } 470 | if healthcheck.Timeout != nil { 471 | timeout = time.Duration(*healthcheck.Timeout) 472 | } 473 | if healthcheck.Interval != nil { 474 | interval = time.Duration(*healthcheck.Interval) 475 | } 476 | if healthcheck.StartPeriod != nil { 477 | startPeriod = time.Duration(*healthcheck.StartPeriod) 478 | } 479 | if healthcheck.Retries != nil { 480 | retries = int(*healthcheck.Retries) 481 | } 482 | return &container.HealthConfig{ 483 | Test: healthcheck.Test, 484 | Timeout: timeout, 485 | Interval: interval, 486 | Retries: retries, 487 | StartPeriod: startPeriod, 488 | }, nil 489 | } 490 | 491 | func convertRestartPolicy(restart string, source *composego.RestartPolicy) (*swarm.RestartPolicy, error) { 492 | // TODO: log if restart is being ignored 493 | if source == nil { 494 | policy, err := opts.ParseRestartPolicy(restart) 495 | if err != nil { 496 | return nil, err 497 | } 498 | switch { 499 | case policy.IsNone(): 500 | return nil, nil 501 | case policy.IsAlways(), policy.IsUnlessStopped(): 502 | return &swarm.RestartPolicy{ 503 | Condition: swarm.RestartPolicyConditionAny, 504 | }, nil 505 | case policy.IsOnFailure(): 506 | attempts := uint64(policy.MaximumRetryCount) 507 | return &swarm.RestartPolicy{ 508 | Condition: swarm.RestartPolicyConditionOnFailure, 509 | MaxAttempts: &attempts, 510 | }, nil 511 | default: 512 | return nil, errors.Errorf("unknown restart policy: %s", restart) 513 | } 514 | } 515 | 516 | return &swarm.RestartPolicy{ 517 | Condition: swarm.RestartPolicyCondition(source.Condition), 518 | Delay: composego.ConvertDurationPtr(source.Delay), 519 | MaxAttempts: source.MaxAttempts, 520 | Window: composego.ConvertDurationPtr(source.Window), 521 | }, nil 522 | } 523 | 524 | func convertUpdateConfig(source *composego.UpdateConfig) *swarm.UpdateConfig { 525 | if source == nil { 526 | return nil 527 | } 528 | parallel := uint64(1) 529 | if source.Parallelism != nil { 530 | parallel = *source.Parallelism 531 | } 532 | return &swarm.UpdateConfig{ 533 | Parallelism: parallel, 534 | Delay: time.Duration(source.Delay), 535 | FailureAction: source.FailureAction, 536 | Monitor: time.Duration(source.Monitor), 537 | MaxFailureRatio: source.MaxFailureRatio, 538 | Order: source.Order, 539 | } 540 | } 541 | 542 | func convertResources(source composego.Resources) (*swarm.ResourceRequirements, error) { 543 | resources := &swarm.ResourceRequirements{} 544 | var err error 545 | if source.Limits != nil { 546 | var cpus int64 547 | if source.Limits.NanoCPUs != "" { 548 | cpus, err = opts.ParseCPUs(source.Limits.NanoCPUs) 549 | if err != nil { 550 | return nil, err 551 | } 552 | } 553 | resources.Limits = &swarm.Limit{ 554 | NanoCPUs: cpus, 555 | MemoryBytes: int64(source.Limits.MemoryBytes), 556 | Pids: source.Limits.PIds, 557 | } 558 | } 559 | if source.Reservations != nil { 560 | var cpus int64 561 | if source.Reservations.NanoCPUs != "" { 562 | cpus, err = opts.ParseCPUs(source.Reservations.NanoCPUs) 563 | if err != nil { 564 | return nil, err 565 | } 566 | } 567 | 568 | var generic []swarm.GenericResource 569 | for _, res := range source.Reservations.GenericResources { 570 | var r swarm.GenericResource 571 | 572 | if res.DiscreteResourceSpec != nil { 573 | r.DiscreteResourceSpec = &swarm.DiscreteGenericResource{ 574 | Kind: res.DiscreteResourceSpec.Kind, 575 | Value: res.DiscreteResourceSpec.Value, 576 | } 577 | } 578 | 579 | generic = append(generic, r) 580 | } 581 | 582 | resources.Reservations = &swarm.Resources{ 583 | NanoCPUs: cpus, 584 | MemoryBytes: int64(source.Reservations.MemoryBytes), 585 | GenericResources: generic, 586 | } 587 | } 588 | return resources, nil 589 | } 590 | 591 | func convertEndpointSpec(endpointMode string, source []composego.ServicePortConfig) (*swarm.EndpointSpec, error) { 592 | portConfigs := []swarm.PortConfig{} 593 | for _, port := range source { 594 | publishedPort, err := strconv.Atoi(port.Published) 595 | if err != nil { 596 | return nil, errors.Errorf("published port: '%s' must be coercible to integer.", port.Published) 597 | } 598 | 599 | portConfig := swarm.PortConfig{ 600 | Protocol: swarm.PortConfigProtocol(port.Protocol), 601 | TargetPort: port.Target, 602 | PublishedPort: uint32(publishedPort), 603 | PublishMode: swarm.PortConfigPublishMode(port.Mode), 604 | } 605 | portConfigs = append(portConfigs, portConfig) 606 | } 607 | 608 | sort.Slice(portConfigs, func(i, j int) bool { 609 | return portConfigs[i].PublishedPort < portConfigs[j].PublishedPort 610 | }) 611 | 612 | return &swarm.EndpointSpec{ 613 | Mode: swarm.ResolutionMode(strings.ToLower(endpointMode)), 614 | Ports: portConfigs, 615 | }, nil 616 | } 617 | 618 | func convertEnvironment(source map[string]*string) []string { 619 | var output []string 620 | 621 | for name, value := range source { 622 | switch value { 623 | case nil: 624 | output = append(output, name) 625 | default: 626 | output = append(output, fmt.Sprintf("%s=%s", name, *value)) 627 | } 628 | } 629 | 630 | return output 631 | } 632 | 633 | func convertDeployMode(mode string, replicas *uint64) (swarm.ServiceMode, error) { 634 | serviceMode := swarm.ServiceMode{} 635 | 636 | switch mode { 637 | case "global-job": 638 | if replicas != nil { 639 | return serviceMode, errors.Errorf("replicas can only be used with replicated or replicated-job mode") 640 | } 641 | serviceMode.GlobalJob = &swarm.GlobalJob{} 642 | case "global": 643 | if replicas != nil { 644 | return serviceMode, errors.Errorf("replicas can only be used with replicated or replicated-job mode") 645 | } 646 | serviceMode.Global = &swarm.GlobalService{} 647 | case "replicated-job": 648 | serviceMode.ReplicatedJob = &swarm.ReplicatedJob{ 649 | MaxConcurrent: replicas, 650 | TotalCompletions: replicas, 651 | } 652 | case "replicated", "": 653 | serviceMode.Replicated = &swarm.ReplicatedService{Replicas: replicas} 654 | default: 655 | return serviceMode, errors.Errorf("Unknown mode: %s", mode) 656 | } 657 | return serviceMode, nil 658 | } 659 | 660 | func convertDNSConfig(DNS []string, DNSSearch []string) *swarm.DNSConfig { 661 | if DNS != nil || DNSSearch != nil { 662 | return &swarm.DNSConfig{ 663 | Nameservers: DNS, 664 | Search: DNSSearch, 665 | } 666 | } 667 | return nil 668 | } 669 | 670 | func convertCredentialSpec(namespace Namespace, spec composego.CredentialSpecConfig, refs []*swarm.ConfigReference) (*swarm.CredentialSpec, error) { 671 | var o []string 672 | 673 | // Config was added in API v1.40 674 | if spec.Config != "" { 675 | o = append(o, `"Config"`) 676 | } 677 | if spec.File != "" { 678 | o = append(o, `"File"`) 679 | } 680 | if spec.Registry != "" { 681 | o = append(o, `"Registry"`) 682 | } 683 | l := len(o) 684 | switch { 685 | case l == 0: 686 | return nil, nil 687 | case l == 2: 688 | return nil, errors.Errorf("invalid credential spec: cannot specify both %s and %s", o[0], o[1]) 689 | case l > 2: 690 | return nil, errors.Errorf("invalid credential spec: cannot specify both %s, and %s", strings.Join(o[:l-1], ", "), o[l-1]) 691 | } 692 | swarmCredSpec := swarm.CredentialSpec{ 693 | Config: spec.Config, 694 | File: spec.File, 695 | Registry: spec.Registry, 696 | } 697 | // if we're using a swarm Config for the credential spec, over-write it 698 | // here with the config ID 699 | if swarmCredSpec.Config != "" { 700 | for _, config := range refs { 701 | if swarmCredSpec.Config == config.ConfigName { 702 | swarmCredSpec.Config = config.ConfigID 703 | return &swarmCredSpec, nil 704 | } 705 | } 706 | // if none of the configs match, try namespacing 707 | for _, config := range refs { 708 | if namespace.Scope(swarmCredSpec.Config) == config.ConfigName { 709 | swarmCredSpec.Config = config.ConfigID 710 | return &swarmCredSpec, nil 711 | } 712 | } 713 | return nil, errors.Errorf("invalid credential spec: spec specifies config %v, but no such config can be found", swarmCredSpec.Config) 714 | } 715 | return &swarmCredSpec, nil 716 | } 717 | 718 | func convertUlimits(origUlimits map[string]*composego.UlimitsConfig) []*units.Ulimit { 719 | newUlimits := make(map[string]*units.Ulimit) 720 | for name, u := range origUlimits { 721 | if u.Single != 0 { 722 | newUlimits[name] = &units.Ulimit{ 723 | Name: name, 724 | Soft: int64(u.Single), 725 | Hard: int64(u.Single), 726 | } 727 | } else { 728 | newUlimits[name] = &units.Ulimit{ 729 | Name: name, 730 | Soft: int64(u.Soft), 731 | Hard: int64(u.Hard), 732 | } 733 | } 734 | } 735 | var ulimits []*units.Ulimit 736 | for _, ulimit := range newUlimits { 737 | ulimits = append(ulimits, ulimit) 738 | } 739 | sort.SliceStable(ulimits, func(i, j int) bool { 740 | return ulimits[i].Name < ulimits[j].Name 741 | }) 742 | return ulimits 743 | } 744 | -------------------------------------------------------------------------------- /convert/volume.go: -------------------------------------------------------------------------------- 1 | package convert 2 | 3 | import ( 4 | composego "github.com/compose-spec/compose-go/types" 5 | "github.com/docker/docker/api/types/mount" 6 | "github.com/pkg/errors" 7 | ) 8 | 9 | // Volumes from compose-file types to engine api types 10 | func Volumes(serviceVolumes []composego.ServiceVolumeConfig, stackVolumes composego.Volumes, namespace Namespace) ([]mount.Mount, error) { 11 | var mounts []mount.Mount 12 | 13 | for _, volumeConfig := range serviceVolumes { 14 | mount, err := convertVolumeToMount(volumeConfig, stackVolumes, namespace) 15 | if err != nil { 16 | return nil, err 17 | } 18 | mounts = append(mounts, mount) 19 | } 20 | return mounts, nil 21 | } 22 | 23 | func convertVolumeToMount( 24 | volume composego.ServiceVolumeConfig, 25 | stackVolumes composego.Volumes, 26 | namespace Namespace, 27 | ) (mount.Mount, error) { 28 | switch volume.Type { 29 | case "volume", "": 30 | return handleVolumeToMount(volume, stackVolumes, namespace) 31 | case "bind": 32 | return handleBindToMount(volume) 33 | case "tmpfs": 34 | return handleTmpfsToMount(volume) 35 | case "npipe": 36 | return handleNpipeToMount(volume) 37 | case "cluster": 38 | return handleClusterToMount(volume) 39 | } 40 | 41 | return mount.Mount{}, errors.New("volume type must be volume, bind, tmpfs, npipe, or cluster") 42 | } 43 | 44 | func handleVolumeToMount( 45 | volume composego.ServiceVolumeConfig, 46 | stackVolumes composego.Volumes, 47 | namespace Namespace, 48 | ) (mount.Mount, error) { 49 | result := createMountFromVolume(volume) 50 | 51 | if volume.Tmpfs != nil { 52 | return mount.Mount{}, errors.New("tmpfs options are incompatible with type volume") 53 | } 54 | if volume.Bind != nil { 55 | return mount.Mount{}, errors.New("bind options are incompatible with type volume") 56 | } 57 | // Anonymous volumes 58 | if volume.Source == "" { 59 | return result, nil 60 | } 61 | 62 | stackVolume, exists := stackVolumes[volume.Source] 63 | if !exists { 64 | return mount.Mount{}, errors.Errorf("undefined volume %q", volume.Source) 65 | } 66 | 67 | result.Source = namespace.Scope(volume.Source) 68 | result.VolumeOptions = &mount.VolumeOptions{} 69 | 70 | if volume.Volume != nil { 71 | result.VolumeOptions.NoCopy = volume.Volume.NoCopy 72 | } 73 | 74 | if stackVolume.Name != "" { 75 | result.Source = stackVolume.Name 76 | } 77 | 78 | // External named volumes 79 | if stackVolume.External.External { 80 | return result, nil 81 | } 82 | 83 | result.VolumeOptions.Labels = AddStackLabel(namespace, stackVolume.Labels) 84 | if stackVolume.Driver != "" || stackVolume.DriverOpts != nil { 85 | result.VolumeOptions.DriverConfig = &mount.Driver{ 86 | Name: stackVolume.Driver, 87 | Options: stackVolume.DriverOpts, 88 | } 89 | } 90 | 91 | return result, nil 92 | } 93 | 94 | func handleBindToMount(volume composego.ServiceVolumeConfig) (mount.Mount, error) { 95 | result := createMountFromVolume(volume) 96 | 97 | if volume.Source == "" { 98 | return mount.Mount{}, errors.New("invalid bind source, source cannot be empty") 99 | } 100 | if volume.Volume != nil { 101 | return mount.Mount{}, errors.New("volume options are incompatible with type bind") 102 | } 103 | if volume.Tmpfs != nil { 104 | return mount.Mount{}, errors.New("tmpfs options are incompatible with type bind") 105 | } 106 | if volume.Bind != nil { 107 | result.BindOptions = &mount.BindOptions{ 108 | Propagation: mount.Propagation(volume.Bind.Propagation), 109 | } 110 | } 111 | return result, nil 112 | } 113 | 114 | func handleTmpfsToMount(volume composego.ServiceVolumeConfig) (mount.Mount, error) { 115 | result := createMountFromVolume(volume) 116 | 117 | if volume.Source != "" { 118 | return mount.Mount{}, errors.New("invalid tmpfs source, source must be empty") 119 | } 120 | if volume.Bind != nil { 121 | return mount.Mount{}, errors.New("bind options are incompatible with type tmpfs") 122 | } 123 | if volume.Volume != nil { 124 | return mount.Mount{}, errors.New("volume options are incompatible with type tmpfs") 125 | } 126 | if volume.Tmpfs != nil { 127 | result.TmpfsOptions = &mount.TmpfsOptions{ 128 | SizeBytes: int64(volume.Tmpfs.Size), 129 | } 130 | } 131 | return result, nil 132 | } 133 | 134 | func handleNpipeToMount(volume composego.ServiceVolumeConfig) (mount.Mount, error) { 135 | result := createMountFromVolume(volume) 136 | 137 | if volume.Source == "" { 138 | return mount.Mount{}, errors.New("invalid npipe source, source cannot be empty") 139 | } 140 | if volume.Volume != nil { 141 | return mount.Mount{}, errors.New("volume options are incompatible with type npipe") 142 | } 143 | if volume.Tmpfs != nil { 144 | return mount.Mount{}, errors.New("tmpfs options are incompatible with type npipe") 145 | } 146 | if volume.Bind != nil { 147 | result.BindOptions = &mount.BindOptions{ 148 | Propagation: mount.Propagation(volume.Bind.Propagation), 149 | } 150 | } 151 | return result, nil 152 | } 153 | 154 | func handleClusterToMount(volume composego.ServiceVolumeConfig) (mount.Mount, error) { 155 | result := createMountFromVolume(volume) 156 | if volume.Source == "" { 157 | return mount.Mount{}, errors.New("invalid cluster source, source cannot be empty") 158 | } 159 | if volume.Volume != nil { 160 | return mount.Mount{}, errors.New("volume options are incompatible with type cluster") 161 | } 162 | if volume.Tmpfs != nil { 163 | return mount.Mount{}, errors.New("tmpfs options are incompatible with type cluster") 164 | } 165 | if volume.Bind != nil { 166 | result.BindOptions = &mount.BindOptions{ 167 | Propagation: mount.Propagation(volume.Bind.Propagation), 168 | } 169 | } 170 | return result, nil 171 | } 172 | 173 | func createMountFromVolume(volume composego.ServiceVolumeConfig) mount.Mount { 174 | return mount.Mount{ 175 | Type: mount.Type(volume.Type), 176 | Target: volume.Target, 177 | ReadOnly: volume.ReadOnly, 178 | Source: volume.Source, 179 | Consistency: mount.Consistency(volume.Consistency), 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /deploy/common.go: -------------------------------------------------------------------------------- 1 | package deploy 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/aaraney/deployx/convert" 7 | 8 | "github.com/docker/docker/api/types" 9 | "github.com/docker/docker/api/types/filters" 10 | "github.com/docker/docker/api/types/swarm" 11 | apiclient "github.com/docker/docker/client" 12 | ) 13 | 14 | func getStackFilter(namespace string) filters.Args { 15 | filter := filters.NewArgs() 16 | filter.Add("label", convert.LabelNamespace+"="+namespace) 17 | return filter 18 | } 19 | 20 | func getStackServices(ctx context.Context, apiclient apiclient.APIClient, namespace string) ([]swarm.Service, error) { 21 | return apiclient.ServiceList(ctx, types.ServiceListOptions{Filters: getStackFilter(namespace)}) 22 | } 23 | 24 | func getStackNetworks(ctx context.Context, apiclient apiclient.APIClient, namespace string) ([]types.NetworkResource, error) { 25 | return apiclient.NetworkList(ctx, types.NetworkListOptions{Filters: getStackFilter(namespace)}) 26 | } 27 | 28 | func getStackSecrets(ctx context.Context, apiclient apiclient.APIClient, namespace string) ([]swarm.Secret, error) { 29 | return apiclient.SecretList(ctx, types.SecretListOptions{Filters: getStackFilter(namespace)}) 30 | } 31 | 32 | func getStackConfigs(ctx context.Context, apiclient apiclient.APIClient, namespace string) ([]swarm.Config, error) { 33 | return apiclient.ConfigList(ctx, types.ConfigListOptions{Filters: getStackFilter(namespace)}) 34 | } 35 | -------------------------------------------------------------------------------- /deploy/deploy.go: -------------------------------------------------------------------------------- 1 | package deploy 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | 7 | "github.com/aaraney/deployx/convert" 8 | 9 | "github.com/aaraney/deployx/commands/options" 10 | composego "github.com/compose-spec/compose-go/types" 11 | "github.com/docker/cli/cli/command" 12 | "github.com/docker/docker/api/types/swarm" 13 | "github.com/docker/docker/api/types/versions" 14 | "github.com/pkg/errors" 15 | ) 16 | 17 | // Resolve image constants 18 | const ( 19 | defaultNetworkDriver = "overlay" 20 | ResolveImageAlways = "always" 21 | ResolveImageChanged = "changed" 22 | ResolveImageNever = "never" 23 | ) 24 | 25 | // RunDeploy is the swarm implementation of docker stack deploy 26 | func RunDeploy(dockerCli command.Cli, opts options.Deploy, cfg *composego.Config) error { 27 | ctx := context.Background() 28 | 29 | if err := validateResolveImageFlag(&opts); err != nil { 30 | return err 31 | } 32 | // client side image resolution should not be done when the supported 33 | // server version is older than 1.30 34 | if versions.LessThan(dockerCli.Client().ClientVersion(), "1.30") { 35 | opts.ResolveImage = ResolveImageNever 36 | } 37 | 38 | return deployCompose(ctx, dockerCli, opts, cfg) 39 | } 40 | 41 | // validateResolveImageFlag validates the opts.resolveImage command line option 42 | func validateResolveImageFlag(opts *options.Deploy) error { 43 | switch opts.ResolveImage { 44 | case ResolveImageAlways, ResolveImageChanged, ResolveImageNever: 45 | return nil 46 | default: 47 | return errors.Errorf("Invalid option %s for flag --resolve-image", opts.ResolveImage) 48 | } 49 | } 50 | 51 | func checkDaemonIsSwarmManager(ctx context.Context, dockerCli command.Cli) error { 52 | info, err := dockerCli.Client().Info(ctx) 53 | if err != nil { 54 | return err 55 | } 56 | if !info.Swarm.ControlAvailable { 57 | return errors.New("this node is not a swarm manager. Use \"docker swarm init\" or \"docker swarm join\" to connect this node to swarm and try again") 58 | } 59 | return nil 60 | } 61 | 62 | // pruneServices removes services that are no longer referenced in the source 63 | func pruneServices(ctx context.Context, dockerCli command.Cli, namespace convert.Namespace, services map[string]struct{}) { 64 | client := dockerCli.Client() 65 | 66 | oldServices, err := getStackServices(ctx, client, namespace.Name()) 67 | if err != nil { 68 | fmt.Fprintf(dockerCli.Err(), "Failed to list services: %s\n", err) 69 | } 70 | 71 | pruneServices := []swarm.Service{} 72 | for _, service := range oldServices { 73 | if _, exists := services[namespace.Descope(service.Spec.Name)]; !exists { 74 | pruneServices = append(pruneServices, service) 75 | } 76 | } 77 | removeServices(ctx, dockerCli, pruneServices) 78 | } 79 | -------------------------------------------------------------------------------- /deploy/deploy_composefile.go: -------------------------------------------------------------------------------- 1 | package deploy 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | 7 | "github.com/aaraney/deployx/commands/options" 8 | "github.com/aaraney/deployx/convert" 9 | 10 | composego "github.com/compose-spec/compose-go/types" 11 | "github.com/docker/cli/cli/command" 12 | "github.com/docker/docker/api/types" 13 | "github.com/docker/docker/api/types/container" 14 | "github.com/docker/docker/api/types/swarm" 15 | apiclient "github.com/docker/docker/client" 16 | "github.com/pkg/errors" 17 | ) 18 | 19 | func deployCompose(ctx context.Context, dockerCli command.Cli, opts options.Deploy, config *composego.Config) error { 20 | if err := checkDaemonIsSwarmManager(ctx, dockerCli); err != nil { 21 | return err 22 | } 23 | 24 | namespace := convert.NewNamespace(opts.Namespace) 25 | 26 | if opts.Prune { 27 | services := map[string]struct{}{} 28 | for _, service := range config.Services { 29 | services[service.Name] = struct{}{} 30 | } 31 | pruneServices(ctx, dockerCli, namespace, services) 32 | } 33 | 34 | serviceNetworks := getServicesDeclaredNetworks(config.Services) 35 | networks, externalNetworks := convert.Networks(namespace, config.Networks, serviceNetworks) 36 | 37 | if err := validateExternalNetworks(ctx, dockerCli.Client(), externalNetworks); err != nil { 38 | return err 39 | } 40 | if err := createNetworks(ctx, dockerCli, namespace, networks); err != nil { 41 | return err 42 | } 43 | 44 | secrets, err := convert.Secrets(namespace, config.Secrets) 45 | if err != nil { 46 | return err 47 | } 48 | if err := createSecrets(ctx, dockerCli, secrets); err != nil { 49 | return err 50 | } 51 | 52 | configs, err := convert.Configs(namespace, config.Configs) 53 | if err != nil { 54 | return err 55 | } 56 | if err := createConfigs(ctx, dockerCli, configs); err != nil { 57 | return err 58 | } 59 | 60 | services, err := convert.Services(namespace, config, dockerCli.Client()) 61 | if err != nil { 62 | return err 63 | } 64 | return deployServices(ctx, dockerCli, services, namespace, opts.SendRegistryAuth, opts.ResolveImage) 65 | } 66 | 67 | func getServicesDeclaredNetworks(serviceConfigs composego.Services) map[string]struct{} { 68 | serviceNetworks := map[string]struct{}{} 69 | for _, serviceConfig := range serviceConfigs { 70 | // NOTE: this should now be unreachable 71 | if len(serviceConfig.Networks) == 0 { 72 | serviceNetworks["default"] = struct{}{} 73 | continue 74 | } 75 | for network := range serviceConfig.Networks { 76 | serviceNetworks[network] = struct{}{} 77 | } 78 | } 79 | return serviceNetworks 80 | } 81 | 82 | func validateExternalNetworks(ctx context.Context, client apiclient.NetworkAPIClient, externalNetworks []string) error { 83 | for _, networkName := range externalNetworks { 84 | if !container.NetworkMode(networkName).IsUserDefined() { 85 | // Networks that are not user defined always exist on all nodes as 86 | // local-scoped networks, so there's no need to inspect them. 87 | continue 88 | } 89 | network, err := client.NetworkInspect(ctx, networkName, types.NetworkInspectOptions{}) 90 | switch { 91 | case apiclient.IsErrNotFound(err): 92 | return errors.New(fmt.Sprintf("network %q is declared as external, but could not be found. You need to create a swarm-scoped network before the stack is deployed", networkName)) 93 | case err != nil: 94 | return err 95 | case network.Scope != "swarm": 96 | return errors.New(fmt.Sprintf("network %q is declared as external, but it is not in the right scope: %q instead of \"swarm\"", networkName, network.Scope)) 97 | } 98 | } 99 | return nil 100 | } 101 | 102 | func createSecrets(ctx context.Context, dockerCli command.Cli, secrets []swarm.SecretSpec) error { 103 | client := dockerCli.Client() 104 | 105 | for _, secretSpec := range secrets { 106 | secret, _, err := client.SecretInspectWithRaw(ctx, secretSpec.Name) 107 | switch { 108 | case err == nil: 109 | // secret already exists, then we update that 110 | if err := client.SecretUpdate(ctx, secret.ID, secret.Meta.Version, secretSpec); err != nil { 111 | return errors.Wrapf(err, "failed to update secret %s", secretSpec.Name) 112 | } 113 | case apiclient.IsErrNotFound(err): 114 | // secret does not exist, then we create a new one. 115 | fmt.Fprintf(dockerCli.Out(), "Creating secret %s\n", secretSpec.Name) 116 | if _, err := client.SecretCreate(ctx, secretSpec); err != nil { 117 | return errors.Wrapf(err, "failed to create secret %s", secretSpec.Name) 118 | } 119 | default: 120 | return err 121 | } 122 | } 123 | return nil 124 | } 125 | 126 | func createConfigs(ctx context.Context, dockerCli command.Cli, configs []swarm.ConfigSpec) error { 127 | client := dockerCli.Client() 128 | 129 | for _, configSpec := range configs { 130 | config, _, err := client.ConfigInspectWithRaw(ctx, configSpec.Name) 131 | switch { 132 | case err == nil: 133 | // config already exists, then we update that 134 | if err := client.ConfigUpdate(ctx, config.ID, config.Meta.Version, configSpec); err != nil { 135 | return errors.Wrapf(err, "failed to update config %s", configSpec.Name) 136 | } 137 | case apiclient.IsErrNotFound(err): 138 | // config does not exist, then we create a new one. 139 | fmt.Fprintf(dockerCli.Out(), "Creating config %s\n", configSpec.Name) 140 | if _, err := client.ConfigCreate(ctx, configSpec); err != nil { 141 | return errors.Wrapf(err, "failed to create config %s", configSpec.Name) 142 | } 143 | default: 144 | return err 145 | } 146 | } 147 | return nil 148 | } 149 | 150 | func createNetworks(ctx context.Context, dockerCli command.Cli, namespace convert.Namespace, networks map[string]types.NetworkCreate) error { 151 | client := dockerCli.Client() 152 | 153 | existingNetworks, err := getStackNetworks(ctx, client, namespace.Name()) 154 | if err != nil { 155 | return err 156 | } 157 | 158 | existingNetworkMap := make(map[string]types.NetworkResource) 159 | for _, network := range existingNetworks { 160 | existingNetworkMap[network.Name] = network 161 | } 162 | 163 | for name, createOpts := range networks { 164 | if _, exists := existingNetworkMap[name]; exists { 165 | continue 166 | } 167 | 168 | if createOpts.Driver == "" { 169 | createOpts.Driver = defaultNetworkDriver 170 | } 171 | 172 | fmt.Fprintf(dockerCli.Out(), "Creating network %s\n", name) 173 | if _, err := client.NetworkCreate(ctx, name, createOpts); err != nil { 174 | return errors.Wrapf(err, "failed to create network %s", name) 175 | } 176 | } 177 | return nil 178 | } 179 | 180 | func deployServices(ctx context.Context, dockerCli command.Cli, services map[string]swarm.ServiceSpec, namespace convert.Namespace, sendAuth bool, resolveImage string) error { 181 | apiClient := dockerCli.Client() 182 | out := dockerCli.Out() 183 | 184 | existingServices, err := getStackServices(ctx, apiClient, namespace.Name()) 185 | if err != nil { 186 | return err 187 | } 188 | 189 | existingServiceMap := make(map[string]swarm.Service) 190 | for _, service := range existingServices { 191 | existingServiceMap[service.Spec.Name] = service 192 | } 193 | 194 | for internalName, serviceSpec := range services { 195 | var ( 196 | name = namespace.Scope(internalName) 197 | image = serviceSpec.TaskTemplate.ContainerSpec.Image 198 | encodedAuth string 199 | ) 200 | 201 | if sendAuth { 202 | // Retrieve encoded auth token from the image reference 203 | encodedAuth, err = command.RetrieveAuthTokenFromImage(ctx, dockerCli, image) 204 | if err != nil { 205 | return err 206 | } 207 | } 208 | 209 | if service, exists := existingServiceMap[name]; exists { 210 | fmt.Fprintf(out, "Updating service %s (id: %s)\n", name, service.ID) 211 | 212 | updateOpts := types.ServiceUpdateOptions{EncodedRegistryAuth: encodedAuth} 213 | 214 | switch resolveImage { 215 | case ResolveImageAlways: 216 | // image should be updated by the server using QueryRegistry 217 | updateOpts.QueryRegistry = true 218 | case ResolveImageChanged: 219 | if image != service.Spec.Labels[convert.LabelImage] { 220 | // Query the registry to resolve digest for the updated image 221 | updateOpts.QueryRegistry = true 222 | } else { 223 | // image has not changed; update the serviceSpec with the 224 | // existing information that was set by QueryRegistry on the 225 | // previous deploy. Otherwise this will trigger an incorrect 226 | // service update. 227 | serviceSpec.TaskTemplate.ContainerSpec.Image = service.Spec.TaskTemplate.ContainerSpec.Image 228 | } 229 | default: 230 | if image == service.Spec.Labels[convert.LabelImage] { 231 | // image has not changed; update the serviceSpec with the 232 | // existing information that was set by QueryRegistry on the 233 | // previous deploy. Otherwise this will trigger an incorrect 234 | // service update. 235 | serviceSpec.TaskTemplate.ContainerSpec.Image = service.Spec.TaskTemplate.ContainerSpec.Image 236 | } 237 | } 238 | 239 | // Stack deploy does not have a `--force` option. Preserve existing 240 | // ForceUpdate value so that tasks are not re-deployed if not updated. 241 | // TODO move this to API client? 242 | serviceSpec.TaskTemplate.ForceUpdate = service.Spec.TaskTemplate.ForceUpdate 243 | 244 | response, err := apiClient.ServiceUpdate(ctx, service.ID, service.Version, serviceSpec, updateOpts) 245 | if err != nil { 246 | return errors.Wrapf(err, "failed to update service %s", name) 247 | } 248 | 249 | for _, warning := range response.Warnings { 250 | fmt.Fprintln(dockerCli.Err(), warning) 251 | } 252 | } else { 253 | fmt.Fprintf(out, "Creating service %s\n", name) 254 | 255 | createOpts := types.ServiceCreateOptions{EncodedRegistryAuth: encodedAuth} 256 | 257 | // query registry if flag disabling it was not set 258 | if resolveImage == ResolveImageAlways || resolveImage == ResolveImageChanged { 259 | createOpts.QueryRegistry = true 260 | } 261 | 262 | if _, err := apiClient.ServiceCreate(ctx, serviceSpec, createOpts); err != nil { 263 | return errors.Wrapf(err, "failed to create service %s", name) 264 | } 265 | } 266 | } 267 | return nil 268 | } 269 | -------------------------------------------------------------------------------- /deploy/remove.go: -------------------------------------------------------------------------------- 1 | package deploy 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "sort" 7 | 8 | "github.com/docker/cli/cli/command" 9 | "github.com/docker/docker/api/types/swarm" 10 | ) 11 | 12 | func sortServiceByName(services []swarm.Service) func(i, j int) bool { 13 | return func(i, j int) bool { 14 | return services[i].Spec.Name < services[j].Spec.Name 15 | } 16 | } 17 | 18 | func removeServices( 19 | ctx context.Context, 20 | dockerCli command.Cli, 21 | services []swarm.Service, 22 | ) bool { 23 | var hasError bool 24 | sort.Slice(services, sortServiceByName(services)) 25 | for _, service := range services { 26 | fmt.Fprintf(dockerCli.Out(), "Removing service %s\n", service.Spec.Name) 27 | if err := dockerCli.Client().ServiceRemove(ctx, service.ID); err != nil { 28 | hasError = true 29 | fmt.Fprintf(dockerCli.Err(), "Failed to remove service %s: %s", service.ID, err) 30 | } 31 | } 32 | return hasError 33 | } 34 | -------------------------------------------------------------------------------- /docker-bake.hcl: -------------------------------------------------------------------------------- 1 | variable "GO_VERSION" { 2 | # default ARG value set in Dockerfile 3 | default = null 4 | } 5 | 6 | variable "VERSION_TAG" { 7 | default = "0.0.1" 8 | } 9 | 10 | variable "DOCKERFILE" { 11 | default = "./Dockerfile" 12 | } 13 | 14 | # Special target: https://github.com/docker/metadata-action#bake-definition 15 | target "meta-helper" {} 16 | 17 | target "default" { 18 | tags = ["aaraney/docker-deployx:latest", "aaraney/docker-deployx:${VERSION_TAG}"] 19 | args = { 20 | GO_VERSION = GO_VERSION 21 | } 22 | pull = true 23 | target = "shell" 24 | dockerfile = "${DOCKERFILE}" 25 | output = ["type=image"] 26 | 27 | platforms = [ 28 | // "linux/386", // TODO: determine why this is failing 29 | "linux/amd64", 30 | "linux/arm/v6", 31 | "linux/arm/v7", 32 | "linux/arm64", 33 | ] 34 | } 35 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/aaraney/deployx 2 | 3 | go 1.20 4 | 5 | require ( 6 | github.com/compose-spec/compose-go v1.13.4 7 | github.com/docker/cli v23.0.6+incompatible 8 | github.com/docker/docker v23.0.6+incompatible 9 | github.com/docker/go-units v0.5.0 10 | github.com/pkg/errors v0.9.1 11 | github.com/spf13/cobra v1.6.1 12 | ) 13 | 14 | require ( 15 | github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect 16 | github.com/Microsoft/go-winio v0.6.1 // indirect 17 | github.com/beorn7/perks v1.0.1 // indirect 18 | github.com/cespare/xxhash/v2 v2.1.2 // indirect 19 | github.com/distribution/distribution/v3 v3.0.0-20230214150026-36d8c594d7aa // indirect 20 | github.com/docker/distribution v2.8.1+incompatible // indirect 21 | github.com/docker/docker-credential-helpers v0.7.0 // indirect 22 | github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c // indirect 23 | github.com/docker/go-connections v0.4.0 // indirect 24 | github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c // indirect 25 | github.com/docker/go-metrics v0.0.1 // indirect 26 | github.com/fvbommel/sortorder v1.1.0 // indirect 27 | github.com/gogo/protobuf v1.3.2 // indirect 28 | github.com/golang/protobuf v1.5.2 // indirect 29 | github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect 30 | github.com/gorilla/mux v1.8.0 // indirect 31 | github.com/imdario/mergo v0.3.15 // indirect 32 | github.com/inconshreveable/mousetrap v1.0.1 // indirect 33 | github.com/mattn/go-runewidth v0.0.14 // indirect 34 | github.com/mattn/go-shellwords v1.0.12 // indirect 35 | github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect 36 | github.com/miekg/pkcs11 v1.0.2 // indirect 37 | github.com/mitchellh/mapstructure v1.5.0 // indirect 38 | github.com/moby/swarmkit/v2 v2.0.0-20230410170805-c6f9c0d5cd35 // indirect 39 | github.com/moby/sys/sequential v0.5.0 // indirect 40 | github.com/moby/term v0.5.0 // indirect 41 | github.com/morikuni/aec v1.0.0 // indirect 42 | github.com/opencontainers/go-digest v1.0.0 // indirect 43 | github.com/opencontainers/image-spec v1.0.2 // indirect 44 | github.com/prometheus/client_golang v1.14.0 // indirect 45 | github.com/prometheus/client_model v0.3.0 // indirect 46 | github.com/prometheus/common v0.37.0 // indirect 47 | github.com/prometheus/procfs v0.8.0 // indirect 48 | github.com/rivo/uniseg v0.2.0 // indirect 49 | github.com/sirupsen/logrus v1.9.0 // indirect 50 | github.com/spf13/pflag v1.0.5 // indirect 51 | github.com/theupdateframework/notary v0.7.0 // indirect 52 | github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect 53 | github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect 54 | github.com/xeipuuv/gojsonschema v1.2.0 // indirect 55 | go.etcd.io/etcd/raft/v3 v3.5.6 // indirect 56 | golang.org/x/crypto v0.2.0 // indirect 57 | golang.org/x/mod v0.8.0 // indirect 58 | golang.org/x/net v0.7.0 // indirect 59 | golang.org/x/sync v0.1.0 // indirect 60 | golang.org/x/sys v0.5.0 // indirect 61 | golang.org/x/term v0.5.0 // indirect 62 | golang.org/x/text v0.7.0 // indirect 63 | golang.org/x/time v0.1.0 // indirect 64 | golang.org/x/tools v0.6.0 // indirect 65 | google.golang.org/genproto v0.0.0-20220706185917-7780775163c4 // indirect 66 | google.golang.org/grpc v1.48.0 // indirect 67 | google.golang.org/protobuf v1.28.1 // indirect 68 | gopkg.in/yaml.v3 v3.0.1 // indirect 69 | ) 70 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 3 | cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= 4 | cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= 5 | cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= 6 | cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= 7 | cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= 8 | cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= 9 | cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= 10 | cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= 11 | cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= 12 | cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= 13 | cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= 14 | cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= 15 | cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= 16 | cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= 17 | cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= 18 | cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= 19 | cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= 20 | cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= 21 | cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= 22 | cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= 23 | cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= 24 | cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= 25 | cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= 26 | cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= 27 | cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= 28 | cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= 29 | cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= 30 | cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= 31 | cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= 32 | cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= 33 | dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= 34 | github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= 35 | github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= 36 | github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= 37 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 38 | github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= 39 | github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= 40 | github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= 41 | github.com/Shopify/logrus-bugsnag v0.0.0-20170309145241-6dbc35f2c30d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ= 42 | github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d h1:UrqY+r/OJnIp5u0s1SbQ8dVfLCZJsnvazdBP5hS4iRs= 43 | github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 44 | github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 45 | github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 46 | github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 47 | github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= 48 | github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= 49 | github.com/beorn7/perks v0.0.0-20150223135152-b965b613227f/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= 50 | github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= 51 | github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= 52 | github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= 53 | github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= 54 | github.com/bitly/go-hostpool v0.1.0/go.mod h1:4gOCgp6+NZnVqlKyZ/iBZFTAJKembaVENUpMkpg42fw= 55 | github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA= 56 | github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= 57 | github.com/bugsnag/bugsnag-go v1.0.5-0.20150529004307-13fd6b8acda0 h1:s7+5BfS4WFJoVF9pnB8kBk03S7pZXRdKamnV0FOl5Sc= 58 | github.com/bugsnag/bugsnag-go v1.0.5-0.20150529004307-13fd6b8acda0/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8= 59 | github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b h1:otBG+dV+YK+Soembjv71DPz3uX/V/6MMlSyD9JBQ6kQ= 60 | github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50= 61 | github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0 h1:nvj0OLI3YqYXer/kZD8Ri1aaunCxIEsOst1BVJswV0o= 62 | github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE= 63 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 64 | github.com/certifi/gocertifi v0.0.0-20191021191039-0944d244cd40/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= 65 | github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= 66 | github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 67 | github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= 68 | github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 69 | github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= 70 | github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= 71 | github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= 72 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 73 | github.com/cloudflare/cfssl v0.0.0-20180223231731-4e2dcbde5004/go.mod h1:yMWuSON2oQp+43nFtAV/uvKQIFpSPerB57DCt9t8sSA= 74 | github.com/cloudflare/cfssl v0.0.0-20180323000720-5d63dbd981b5 h1:PqZ3bA4yzwywivzk7PBQWngJp2/PAS0bWRZerKteicY= 75 | github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= 76 | github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= 77 | github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= 78 | github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= 79 | github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= 80 | github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= 81 | github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h6jFvWxBdQXxjopDMZyH2UVceIRfR84bdzbkoKrsWNo= 82 | github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA= 83 | github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI= 84 | github.com/compose-spec/compose-go v1.13.4 h1:O6xAsPqaY1s9KXteiO7wRCDTJLahv1XP/z/eUO9EfbI= 85 | github.com/compose-spec/compose-go v1.13.4/go.mod h1:rsiZ8uaOHJYJemDBzTe9UBpaq5ZFVEOO4TxM2G3SJxk= 86 | github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= 87 | github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= 88 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 89 | github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= 90 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 91 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 92 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 93 | github.com/denisenkom/go-mssqldb v0.0.0-20191128021309-1d7a30a10f73/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= 94 | github.com/distribution/distribution/v3 v3.0.0-20230214150026-36d8c594d7aa h1:L9Ay/slwQ4ERSPaurC+TVkZrM0K98GNrEEo1En3e8as= 95 | github.com/distribution/distribution/v3 v3.0.0-20230214150026-36d8c594d7aa/go.mod h1:WHNsWjnIn2V1LYOrME7e8KxSeKunYHsxEm4am0BUtcI= 96 | github.com/docker/cli v23.0.6+incompatible h1:CScadyCJ2ZKUDpAMZta6vK8I+6/m60VIjGIV7Wg/Eu4= 97 | github.com/docker/cli v23.0.6+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= 98 | github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= 99 | github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68= 100 | github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= 101 | github.com/docker/docker v23.0.6+incompatible h1:aBD4np894vatVX99UTx/GyOUOK4uEcROwA3+bQhEcoU= 102 | github.com/docker/docker v23.0.6+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= 103 | github.com/docker/docker-credential-helpers v0.7.0 h1:xtCHsjxogADNZcdv1pKUHXryefjlVRqWqIhk/uXJp0A= 104 | github.com/docker/docker-credential-helpers v0.7.0/go.mod h1:rETQfLdHNT3foU5kuNkFR1R1V12OJRRO5lzt2D1b5X0= 105 | github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c h1:lzqkGL9b3znc+ZUgi7FlLnqjQhcXxkNM/quxIjBVMD0= 106 | github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c/go.mod h1:CADgU4DSXK5QUlFslkQu2yW2TKzFZcXq/leZfM0UH5Q= 107 | github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= 108 | github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= 109 | github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c h1:+pKlWGMw7gf6bQ+oDZB4KHQFypsfjYlq/C4rfL7D3g8= 110 | github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= 111 | github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI= 112 | github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8= 113 | github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= 114 | github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= 115 | github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= 116 | github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 h1:UhxFibDNY/bfvqU5CAUmr9zpesgbU6SWc8/B4mflAE4= 117 | github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= 118 | github.com/dvsekhvalnov/jose2go v0.0.0-20170216131308-f21a8cedbbae/go.mod h1:7BvyPhdbLxMXIYTFPLsyJRFMsKmOZnQmzh6Gb+uquuM= 119 | github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 120 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 121 | github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= 122 | github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= 123 | github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= 124 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 125 | github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0= 126 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 127 | github.com/fvbommel/sortorder v1.1.0 h1:fUmoe+HLsBTctBDoaBwpQo5N+nrCp8g/BjKb/6ZQmYw= 128 | github.com/fvbommel/sortorder v1.1.0/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0= 129 | github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ= 130 | github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 131 | github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= 132 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 133 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 134 | github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 135 | github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 136 | github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= 137 | github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= 138 | github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= 139 | github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= 140 | github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= 141 | github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= 142 | github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= 143 | github.com/go-sql-driver/mysql v1.3.0 h1:pgwjLi/dvffoP9aabwkT3AKpXQM93QARkjFhDDqC1UE= 144 | github.com/go-sql-driver/mysql v1.3.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= 145 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 146 | github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= 147 | github.com/gogo/protobuf v1.0.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= 148 | github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= 149 | github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= 150 | github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= 151 | github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= 152 | github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= 153 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 154 | github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 155 | github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 156 | github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 157 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 158 | github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 159 | github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= 160 | github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 161 | github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 162 | github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 163 | github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= 164 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 165 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 166 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 167 | github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 168 | github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 169 | github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= 170 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 171 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 172 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 173 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 174 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 175 | github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= 176 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 177 | github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 178 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 179 | github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= 180 | github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 181 | github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 182 | github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 183 | github.com/google/certificate-transparency-go v1.0.10-0.20180222191210-5ab67e519c93/go.mod h1:QeJfpSbVSfYc7RgB3gJFj9cbuQMMchQxrWXz8Ruopmg= 184 | github.com/google/certificate-transparency-go v1.1.4 h1:hCyXHDbtqlr/lMXU0D4WgbalXL0Zk4dSWWMbPV8VrqY= 185 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 186 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 187 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 188 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 189 | github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 190 | github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 191 | github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 192 | github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 193 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 194 | github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 195 | github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= 196 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 197 | github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= 198 | github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= 199 | github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 200 | github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 201 | github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 202 | github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 203 | github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 204 | github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 205 | github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 206 | github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 207 | github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= 208 | github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= 209 | github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 210 | github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= 211 | github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= 212 | github.com/gorilla/mux v1.7.0/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= 213 | github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= 214 | github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= 215 | github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= 216 | github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed h1:5upAirOpQc1Q53c0bnx2ufif5kANL7bfZWcc6VJWJd8= 217 | github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4= 218 | github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 219 | github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 220 | github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= 221 | github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= 222 | github.com/imdario/mergo v0.3.15 h1:M8XP7IuFNsqUx6VPK2P9OSmsYsI/YFaGil0uD21V3dM= 223 | github.com/imdario/mergo v0.3.15/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= 224 | github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= 225 | github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc= 226 | github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= 227 | github.com/jinzhu/gorm v0.0.0-20170222002820-5409931a1bb8 h1:CZkYfurY6KGhVtlalI4QwQ6T0Cu6iuY3e0x5RLu96WE= 228 | github.com/jinzhu/gorm v0.0.0-20170222002820-5409931a1bb8/go.mod h1:Vla75njaFJ8clLU1W44h34PjIkijhjHIYnZxMqCdxqo= 229 | github.com/jinzhu/inflection v0.0.0-20170102125226-1c35d901db3d h1:jRQLvyVGL+iVtDElaEIDdKwpPqUIZJfzkNLV34htpEc= 230 | github.com/jinzhu/inflection v0.0.0-20170102125226-1c35d901db3d/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= 231 | github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= 232 | github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= 233 | github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= 234 | github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 235 | github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 236 | github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 237 | github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= 238 | github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= 239 | github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= 240 | github.com/juju/loggo v0.0.0-20190526231331-6e530bcce5d8/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U= 241 | github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= 242 | github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= 243 | github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= 244 | github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= 245 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 246 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 247 | github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 248 | github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 249 | github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= 250 | github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= 251 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 252 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 253 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 254 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 255 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 256 | github.com/lib/pq v0.0.0-20150723085316-0dad96c0b94f/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= 257 | github.com/magiconair/properties v1.5.3 h1:C8fxWnhYyME3n0klPOhVM7PtYUB3eV1W3DeFmN3j53Y= 258 | github.com/magiconair/properties v1.5.3/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= 259 | github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= 260 | github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= 261 | github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk= 262 | github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= 263 | github.com/mattn/go-sqlite3 v1.6.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= 264 | github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= 265 | github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= 266 | github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= 267 | github.com/miekg/pkcs11 v1.0.2 h1:CIBkOawOtzJNE0B+EpRiUBzuVW7JEQAwdwhSS6YhIeg= 268 | github.com/miekg/pkcs11 v1.0.2/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= 269 | github.com/mitchellh/mapstructure v0.0.0-20150613213606-2caf8efc9366/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 270 | github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= 271 | github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= 272 | github.com/moby/swarmkit/v2 v2.0.0-20230410170805-c6f9c0d5cd35 h1:1NAN0V8XkBTgkc9KNLQIdNov33ucOY+4Zu3NKeBmtIo= 273 | github.com/moby/swarmkit/v2 v2.0.0-20230410170805-c6f9c0d5cd35/go.mod h1:P/ha3F7UZMmuUvqrHw9cZK/BjktSngQIgRPiairNHTc= 274 | github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc= 275 | github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo= 276 | github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= 277 | github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= 278 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 279 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 280 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 281 | github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 282 | github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= 283 | github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= 284 | github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= 285 | github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= 286 | github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= 287 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= 288 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= 289 | github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 290 | github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg= 291 | github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= 292 | github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= 293 | github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= 294 | github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= 295 | github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= 296 | github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= 297 | github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM= 298 | github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= 299 | github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU= 300 | github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= 301 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 302 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 303 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 304 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 305 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 306 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 307 | github.com/prometheus/client_golang v0.9.0-pre1.0.20180209125602-c332b6f63c06/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= 308 | github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= 309 | github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= 310 | github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= 311 | github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= 312 | github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= 313 | github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= 314 | github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= 315 | github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= 316 | github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= 317 | github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= 318 | github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 319 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 320 | github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 321 | github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= 322 | github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= 323 | github.com/prometheus/common v0.0.0-20180110214958-89604d197083/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= 324 | github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= 325 | github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= 326 | github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= 327 | github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= 328 | github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= 329 | github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE= 330 | github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= 331 | github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= 332 | github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= 333 | github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= 334 | github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= 335 | github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= 336 | github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= 337 | github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= 338 | github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= 339 | github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= 340 | github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= 341 | github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= 342 | github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= 343 | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 344 | github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 345 | github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= 346 | github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= 347 | github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= 348 | github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= 349 | github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= 350 | github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= 351 | github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= 352 | github.com/spf13/cast v0.0.0-20150508191742-4d07383ffe94 h1:JmfC365KywYwHB946TTiQWEb8kqPY+pybPLoGE9GgVk= 353 | github.com/spf13/cast v0.0.0-20150508191742-4d07383ffe94/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg= 354 | github.com/spf13/cobra v0.0.1/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= 355 | github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= 356 | github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= 357 | github.com/spf13/jwalterweatherman v0.0.0-20141219030609-3d60171a6431 h1:XTHrT015sxHyJ5FnQ0AeemSspZWaDq7DoTRW0EVsDCE= 358 | github.com/spf13/jwalterweatherman v0.0.0-20141219030609-3d60171a6431/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= 359 | github.com/spf13/pflag v1.0.0/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 360 | github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= 361 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 362 | github.com/spf13/viper v0.0.0-20150530192845-be5ff3e4840c h1:2EejZtjFjKJGk71ANb+wtFK5EjUzUkEM3R0xnp559xg= 363 | github.com/spf13/viper v0.0.0-20150530192845-be5ff3e4840c/go.mod h1:A8kyI5cUJhb8N+3pkfONlcEcZbueH6nhAm0Fq7SrnBM= 364 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 365 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 366 | github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= 367 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 368 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 369 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 370 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 371 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 372 | github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= 373 | github.com/theupdateframework/notary v0.7.0 h1:QyagRZ7wlSpjT5N2qQAh/pN+DVqgekv4DzbAiAiEL3c= 374 | github.com/theupdateframework/notary v0.7.0/go.mod h1:c9DRxcmhHmVLDay4/2fUYdISnHqbFDGRSlXPO0AhYWw= 375 | github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c= 376 | github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= 377 | github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= 378 | github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= 379 | github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= 380 | github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= 381 | github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 382 | github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 383 | github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 384 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 385 | go.etcd.io/etcd/client/pkg/v3 v3.5.6/go.mod h1:ggrwbk069qxpKPq8/FKkQ3Xq9y39kbFR4LnKszpRXeQ= 386 | go.etcd.io/etcd/raft/v3 v3.5.6 h1:tOmx6Ym6rn2GpZOrvTGJZciJHek6RnC3U/zNInzIN50= 387 | go.etcd.io/etcd/raft/v3 v3.5.6/go.mod h1:wL8kkRGx1Hp8FmZUuHfL3K2/OaGIDaXGr1N7i2G07J0= 388 | go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= 389 | go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= 390 | go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 391 | go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 392 | go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 393 | go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= 394 | go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= 395 | go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= 396 | go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= 397 | golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 398 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 399 | golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 400 | golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 401 | golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 402 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 403 | golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 404 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 405 | golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= 406 | golang.org/x/crypto v0.2.0 h1:BRXPfhNivWL5Yq0BGQ39a2sW6t44aODpfxkWjYdzewE= 407 | golang.org/x/crypto v0.2.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= 408 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 409 | golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 410 | golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= 411 | golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= 412 | golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= 413 | golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 414 | golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 415 | golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 416 | golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= 417 | golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= 418 | golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= 419 | golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 420 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 421 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 422 | golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 423 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 424 | golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 425 | golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 426 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 427 | golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= 428 | golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 429 | golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 430 | golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= 431 | golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= 432 | golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= 433 | golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= 434 | golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 435 | golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 436 | golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 437 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 438 | golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8= 439 | golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 440 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 441 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 442 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 443 | golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 444 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 445 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 446 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 447 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 448 | golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 449 | golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 450 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 451 | golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 452 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 453 | golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 454 | golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 455 | golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 456 | golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 457 | golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 458 | golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 459 | golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 460 | golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 461 | golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 462 | golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 463 | golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 464 | golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 465 | golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 466 | golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 467 | golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 468 | golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 469 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 470 | golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= 471 | golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 472 | golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= 473 | golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= 474 | golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= 475 | golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= 476 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 477 | golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 478 | golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 479 | golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 480 | golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 481 | golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 482 | golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= 483 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 484 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 485 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 486 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 487 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 488 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 489 | golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 490 | golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 491 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 492 | golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 493 | golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= 494 | golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 495 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 496 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 497 | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 498 | golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 499 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 500 | golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 501 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 502 | golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 503 | golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 504 | golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 505 | golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 506 | golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 507 | golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 508 | golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 509 | golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 510 | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 511 | golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 512 | golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 513 | golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 514 | golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 515 | golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 516 | golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 517 | golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 518 | golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 519 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 520 | golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 521 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 522 | golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 523 | golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 524 | golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 525 | golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 526 | golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 527 | golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 528 | golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 529 | golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 530 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 531 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 532 | golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 533 | golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 534 | golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 535 | golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 536 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 537 | golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 538 | golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 539 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 540 | golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 541 | golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 542 | golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 543 | golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 544 | golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= 545 | golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 546 | golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= 547 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 548 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 549 | golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY= 550 | golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= 551 | golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 552 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 553 | golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 554 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 555 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 556 | golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 557 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 558 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 559 | golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= 560 | golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 561 | golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 562 | golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 563 | golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 564 | golang.org/x/time v0.1.0 h1:xYY+Bajn2a7VBmTM5GikTmnK8ZuX8YgnQCqZpbBNtmA= 565 | golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 566 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 567 | golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 568 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 569 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 570 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 571 | golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 572 | golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 573 | golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 574 | golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 575 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 576 | golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 577 | golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 578 | golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 579 | golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 580 | golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 581 | golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 582 | golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 583 | golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 584 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 585 | golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 586 | golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 587 | golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 588 | golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 589 | golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 590 | golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 591 | golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 592 | golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 593 | golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 594 | golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 595 | golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 596 | golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 597 | golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= 598 | golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= 599 | golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= 600 | golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 601 | golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 602 | golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 603 | golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 604 | golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 605 | golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 606 | golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 607 | golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 608 | golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 609 | golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM= 610 | golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= 611 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 612 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 613 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 614 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 615 | google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= 616 | google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= 617 | google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 618 | google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 619 | google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 620 | google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 621 | google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 622 | google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 623 | google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 624 | google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 625 | google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 626 | google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 627 | google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= 628 | google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= 629 | google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= 630 | google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= 631 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 632 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 633 | google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 634 | google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= 635 | google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 636 | google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 637 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 638 | google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 639 | google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 640 | google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 641 | google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 642 | google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 643 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 644 | google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= 645 | google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 646 | google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 647 | google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 648 | google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 649 | google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 650 | google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 651 | google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= 652 | google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 653 | google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 654 | google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 655 | google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 656 | google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 657 | google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 658 | google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 659 | google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 660 | google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 661 | google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= 662 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= 663 | google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= 664 | google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 665 | google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 666 | google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 667 | google.golang.org/genproto v0.0.0-20220706185917-7780775163c4 h1:7YDGQC/0sigNGzsEWyb9s72jTxlFdwVEYNJHbfQ+Dtg= 668 | google.golang.org/genproto v0.0.0-20220706185917-7780775163c4/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= 669 | google.golang.org/grpc v1.0.5/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= 670 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 671 | google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= 672 | google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= 673 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 674 | google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= 675 | google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 676 | google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 677 | google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 678 | google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= 679 | google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= 680 | google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 681 | google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 682 | google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= 683 | google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= 684 | google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= 685 | google.golang.org/grpc v1.48.0 h1:rQOsyJ/8+ufEDJd/Gdsz7HG220Mh9HAhFHRGnIjda0w= 686 | google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= 687 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 688 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 689 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 690 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 691 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 692 | google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 693 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 694 | google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 695 | google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= 696 | google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= 697 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 698 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 699 | google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 700 | google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 701 | google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= 702 | google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 703 | gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= 704 | gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 705 | gopkg.in/cenkalti/backoff.v2 v2.2.1 h1:eJ9UAg01/HIHG987TwxvnzK2MgxXq97YY6rYDpY9aII= 706 | gopkg.in/cenkalti/backoff.v2 v2.2.1/go.mod h1:S0QdOvT2AlerfSBkp0O+dk+bbIMaNbEmVk876gPCthU= 707 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 708 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 709 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 710 | gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= 711 | gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 712 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 713 | gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= 714 | gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= 715 | gopkg.in/rethinkdb/rethinkdb-go.v6 v6.2.1 h1:d4KQkxAaAiRY2h5Zqis161Pv91A37uZyJOx73duwUwM= 716 | gopkg.in/rethinkdb/rethinkdb-go.v6 v6.2.1/go.mod h1:WbjuEoo1oadwzQ4apSDU+JTvmllEHtsNHS6y7vFc7iw= 717 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= 718 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 719 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 720 | gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 721 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 722 | gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 723 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 724 | gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 725 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= 726 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 727 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 728 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 729 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 730 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 731 | gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o= 732 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 733 | honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 734 | honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 735 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 736 | honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= 737 | honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= 738 | honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= 739 | k8s.io/klog/v2 v2.80.1 h1:atnLQ121W371wYYFawwYx1aEY2eUfs4l3J72wtgAwV4= 740 | rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= 741 | rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= 742 | rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= 743 | -------------------------------------------------------------------------------- /version/version.go: -------------------------------------------------------------------------------- 1 | package version 2 | 3 | const Version = "0.0.1" 4 | --------------------------------------------------------------------------------