mirror of https://github.com/docker/cli.git
format (GoDoc) comments with Go 1.19 to prepare for go updates
Older versions of Go do not format these comments, so we can already reformat them ahead of time to prevent gofmt linting failing once we update to Go 1.19 or up. Result of: gofmt -s -w $(find . -type f -name '*.go' | grep -v "/vendor/") With some manual adjusting. Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
parent
3564b7d375
commit
82427d1a07
|
@ -34,7 +34,7 @@ type Plugin struct {
|
||||||
// returned with no error. An error is only returned due to a
|
// returned with no error. An error is only returned due to a
|
||||||
// non-recoverable error.
|
// non-recoverable error.
|
||||||
//
|
//
|
||||||
// nolint: gocyclo
|
//nolint:gocyclo
|
||||||
func newPlugin(c Candidate, rootcmd *cobra.Command) (Plugin, error) {
|
func newPlugin(c Candidate, rootcmd *cobra.Command) (Plugin, error) {
|
||||||
path := c.Path()
|
path := c.Path()
|
||||||
if path == "" {
|
if path == "" {
|
||||||
|
|
|
@ -368,10 +368,12 @@ func copyToContainer(ctx context.Context, dockerCli command.Cli, copyConfig cpCo
|
||||||
// in a valid LOCALPATH, like `file:name.txt`. We can resolve this ambiguity by
|
// in a valid LOCALPATH, like `file:name.txt`. We can resolve this ambiguity by
|
||||||
// requiring a LOCALPATH with a `:` to be made explicit with a relative or
|
// requiring a LOCALPATH with a `:` to be made explicit with a relative or
|
||||||
// absolute path:
|
// absolute path:
|
||||||
// `/path/to/file:name.txt` or `./file:name.txt`
|
//
|
||||||
|
// `/path/to/file:name.txt` or `./file:name.txt`
|
||||||
//
|
//
|
||||||
// This is apparently how `scp` handles this as well:
|
// This is apparently how `scp` handles this as well:
|
||||||
// http://www.cyberciti.biz/faq/rsync-scp-file-name-with-colon-punctuation-in-it/
|
//
|
||||||
|
// http://www.cyberciti.biz/faq/rsync-scp-file-name-with-colon-punctuation-in-it/
|
||||||
//
|
//
|
||||||
// We can't simply check for a filepath separator because container names may
|
// We can't simply check for a filepath separator because container names may
|
||||||
// have a separator, e.g., "host0/cname1" if container is in a Docker cluster,
|
// have a separator, e.g., "host0/cname1" if container is in a Docker cluster,
|
||||||
|
|
|
@ -198,7 +198,7 @@ func newCIDFile(path string) (*cidFile, error) {
|
||||||
return &cidFile{path: path, file: f}, nil
|
return &cidFile{path: path, file: f}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// nolint: gocyclo
|
//nolint:gocyclo
|
||||||
func createContainer(ctx context.Context, dockerCli command.Cli, containerConfig *containerConfig, opts *createOptions) (*container.CreateResponse, error) {
|
func createContainer(ctx context.Context, dockerCli command.Cli, containerConfig *containerConfig, opts *createOptions) (*container.CreateResponse, error) {
|
||||||
config := containerConfig.Config
|
config := containerConfig.Config
|
||||||
hostConfig := containerConfig.HostConfig
|
hostConfig := containerConfig.HostConfig
|
||||||
|
|
|
@ -311,7 +311,8 @@ type containerConfig struct {
|
||||||
// parse parses the args for the specified command and generates a Config,
|
// parse parses the args for the specified command and generates a Config,
|
||||||
// a HostConfig and returns them with the specified command.
|
// a HostConfig and returns them with the specified command.
|
||||||
// If the specified args are not valid, it will return an error.
|
// If the specified args are not valid, it will return an error.
|
||||||
// nolint: gocyclo
|
//
|
||||||
|
//nolint:gocyclo
|
||||||
func parse(flags *pflag.FlagSet, copts *containerOptions, serverOS string) (*containerConfig, error) {
|
func parse(flags *pflag.FlagSet, copts *containerOptions, serverOS string) (*containerConfig, error) {
|
||||||
var (
|
var (
|
||||||
attachStdin = copts.attach.Get("stdin")
|
attachStdin = copts.attach.Get("stdin")
|
||||||
|
@ -965,7 +966,8 @@ func parseWindowsDevice(device string) (container.DeviceMapping, error) {
|
||||||
|
|
||||||
// validateDeviceCgroupRule validates a device cgroup rule string format
|
// validateDeviceCgroupRule validates a device cgroup rule string format
|
||||||
// It will make sure 'val' is in the form:
|
// It will make sure 'val' is in the form:
|
||||||
// 'type major:minor mode'
|
//
|
||||||
|
// 'type major:minor mode'
|
||||||
func validateDeviceCgroupRule(val string) (string, error) {
|
func validateDeviceCgroupRule(val string) (string, error) {
|
||||||
if deviceCgroupRuleRegexp.MatchString(val) {
|
if deviceCgroupRuleRegexp.MatchString(val) {
|
||||||
return val, nil
|
return val, nil
|
||||||
|
@ -1009,7 +1011,9 @@ func validateDevice(val string, serverOS string) (string, error) {
|
||||||
// validateLinuxPath is the implementation of validateDevice knowing that the
|
// validateLinuxPath is the implementation of validateDevice knowing that the
|
||||||
// target server operating system is a Linux daemon.
|
// target server operating system is a Linux daemon.
|
||||||
// It will make sure 'val' is in the form:
|
// It will make sure 'val' is in the form:
|
||||||
// [host-dir:]container-path[:mode]
|
//
|
||||||
|
// [host-dir:]container-path[:mode]
|
||||||
|
//
|
||||||
// It also validates the device mode.
|
// It also validates the device mode.
|
||||||
func validateLinuxPath(val string, validator func(string) bool) (string, error) {
|
func validateLinuxPath(val string, validator func(string) bool) (string, error) {
|
||||||
var containerPath string
|
var containerPath string
|
||||||
|
|
|
@ -182,7 +182,7 @@ func TestParseRunWithInvalidArgs(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// nolint: gocyclo
|
//nolint:gocyclo
|
||||||
func TestParseWithVolumes(t *testing.T) {
|
func TestParseWithVolumes(t *testing.T) {
|
||||||
|
|
||||||
// A single volume
|
// A single volume
|
||||||
|
|
|
@ -118,7 +118,7 @@ func runRun(dockerCli command.Cli, flags *pflag.FlagSet, ropts *runOptions, copt
|
||||||
return runContainer(dockerCli, ropts, copts, containerConfig)
|
return runContainer(dockerCli, ropts, copts, containerConfig)
|
||||||
}
|
}
|
||||||
|
|
||||||
// nolint: gocyclo
|
//nolint:gocyclo
|
||||||
func runContainer(dockerCli command.Cli, opts *runOptions, copts *containerOptions, containerConfig *containerConfig) error {
|
func runContainer(dockerCli command.Cli, opts *runOptions, copts *containerOptions, containerConfig *containerConfig) error {
|
||||||
config := containerConfig.Config
|
config := containerConfig.Config
|
||||||
stdout, stderr := dockerCli.Out(), dockerCli.Err()
|
stdout, stderr := dockerCli.Out(), dockerCli.Err()
|
||||||
|
|
|
@ -67,7 +67,8 @@ func NewStartCommand(dockerCli command.Cli) *cobra.Command {
|
||||||
}
|
}
|
||||||
|
|
||||||
// RunStart executes a `start` command
|
// RunStart executes a `start` command
|
||||||
// nolint: gocyclo
|
//
|
||||||
|
//nolint:gocyclo
|
||||||
func RunStart(dockerCli command.Cli, opts *StartOptions) error {
|
func RunStart(dockerCli command.Cli, opts *StartOptions) error {
|
||||||
ctx, cancelFun := context.WithCancel(context.Background())
|
ctx, cancelFun := context.WithCancel(context.Background())
|
||||||
defer cancelFun()
|
defer cancelFun()
|
||||||
|
|
|
@ -56,7 +56,8 @@ func NewStatsCommand(dockerCli command.Cli) *cobra.Command {
|
||||||
|
|
||||||
// runStats displays a live stream of resource usage statistics for one or more containers.
|
// runStats displays a live stream of resource usage statistics for one or more containers.
|
||||||
// This shows real-time information on CPU usage, memory usage, and network I/O.
|
// This shows real-time information on CPU usage, memory usage, and network I/O.
|
||||||
// nolint: gocyclo
|
//
|
||||||
|
//nolint:gocyclo
|
||||||
func runStats(dockerCli command.Cli, opts *statsOptions) error {
|
func runStats(dockerCli command.Cli, opts *statsOptions) error {
|
||||||
showAll := len(opts.containers) == 0
|
showAll := len(opts.containers) == 0
|
||||||
closeChan := make(chan error)
|
closeChan := make(chan error)
|
||||||
|
|
|
@ -436,7 +436,7 @@ type ports struct {
|
||||||
expected string
|
expected string
|
||||||
}
|
}
|
||||||
|
|
||||||
// nolint: lll
|
//nolint:lll
|
||||||
func TestDisplayablePorts(t *testing.T) {
|
func TestDisplayablePorts(t *testing.T) {
|
||||||
cases := []ports{
|
cases := []ports{
|
||||||
{
|
{
|
||||||
|
|
|
@ -12,7 +12,7 @@ func (d *dummy) Func1() string {
|
||||||
return "Func1"
|
return "Func1"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *dummy) func2() string { // nolint: unused
|
func (d *dummy) func2() string { //nolint:unused
|
||||||
return "func2(should not be marshalled)"
|
return "func2(should not be marshalled)"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,8 @@
|
||||||
// The text/tabwriter package is frozen and is not accepting new features.
|
// The text/tabwriter package is frozen and is not accepting new features.
|
||||||
|
|
||||||
// based on https://github.com/golang/go/blob/master/src/text/tabwriter/tabwriter.go Last modified 690ac40 on 31 Jan
|
// based on https://github.com/golang/go/blob/master/src/text/tabwriter/tabwriter.go Last modified 690ac40 on 31 Jan
|
||||||
//nolint
|
|
||||||
|
//nolint:gocyclo,nakedret,revive,unused // ignore linting errors, so that we can stick close to upstream
|
||||||
package tabwriter
|
package tabwriter
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
|
@ -176,7 +176,7 @@ func (out *lastProgressOutput) WriteProgress(prog progress.Progress) error {
|
||||||
return out.output.WriteProgress(prog)
|
return out.output.WriteProgress(prog)
|
||||||
}
|
}
|
||||||
|
|
||||||
// nolint: gocyclo
|
//nolint:gocyclo
|
||||||
func runBuild(dockerCli command.Cli, options buildOptions) error {
|
func runBuild(dockerCli command.Cli, options buildOptions) error {
|
||||||
var (
|
var (
|
||||||
err error
|
err error
|
||||||
|
|
|
@ -84,8 +84,8 @@ func TestHistoryContext_CreatedSince(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHistoryContext_CreatedBy(t *testing.T) {
|
func TestHistoryContext_CreatedBy(t *testing.T) {
|
||||||
withTabs := `/bin/sh -c apt-key adv --keyserver hkp://pgp.mit.edu:80 --recv-keys 573BFD6B3D8FBC641079A6ABABF5BD827BD9BF62 && echo "deb http://nginx.org/packages/mainline/debian/ jessie nginx" >> /etc/apt/sources.list && apt-get update && apt-get install --no-install-recommends --no-install-suggests -y ca-certificates nginx=${NGINX_VERSION} nginx-module-xslt nginx-module-geoip nginx-module-image-filter nginx-module-perl nginx-module-njs gettext-base && rm -rf /var/lib/apt/lists/*` // nolint: lll
|
withTabs := `/bin/sh -c apt-key adv --keyserver hkp://pgp.mit.edu:80 --recv-keys 573BFD6B3D8FBC641079A6ABABF5BD827BD9BF62 && echo "deb http://nginx.org/packages/mainline/debian/ jessie nginx" >> /etc/apt/sources.list && apt-get update && apt-get install --no-install-recommends --no-install-suggests -y ca-certificates nginx=${NGINX_VERSION} nginx-module-xslt nginx-module-geoip nginx-module-image-filter nginx-module-perl nginx-module-njs gettext-base && rm -rf /var/lib/apt/lists/*` //nolint:lll
|
||||||
expected := `/bin/sh -c apt-key adv --keyserver hkp://pgp.mit.edu:80 --recv-keys 573BFD6B3D8FBC641079A6ABABF5BD827BD9BF62 && echo "deb http://nginx.org/packages/mainline/debian/ jessie nginx" >> /etc/apt/sources.list && apt-get update && apt-get install --no-install-recommends --no-install-suggests -y ca-certificates nginx=${NGINX_VERSION} nginx-module-xslt nginx-module-geoip nginx-module-image-filter nginx-module-perl nginx-module-njs gettext-base && rm -rf /var/lib/apt/lists/*` // nolint: lll
|
expected := `/bin/sh -c apt-key adv --keyserver hkp://pgp.mit.edu:80 --recv-keys 573BFD6B3D8FBC641079A6ABABF5BD827BD9BF62 && echo "deb http://nginx.org/packages/mainline/debian/ jessie nginx" >> /etc/apt/sources.list && apt-get update && apt-get install --no-install-recommends --no-install-suggests -y ca-certificates nginx=${NGINX_VERSION} nginx-module-xslt nginx-module-geoip nginx-module-image-filter nginx-module-perl nginx-module-njs gettext-base && rm -rf /var/lib/apt/lists/*` //nolint:lll
|
||||||
|
|
||||||
var ctx historyContext
|
var ctx historyContext
|
||||||
cases := []historyCase{
|
cases := []historyCase{
|
||||||
|
@ -186,7 +186,7 @@ func TestHistoryContext_Table(t *testing.T) {
|
||||||
{ID: "imageID3", Created: unixTime, CreatedBy: "/bin/bash ls", Size: int64(182964289), Comment: "Hi", Tags: []string{"image:tag2"}},
|
{ID: "imageID3", Created: unixTime, CreatedBy: "/bin/bash ls", Size: int64(182964289), Comment: "Hi", Tags: []string{"image:tag2"}},
|
||||||
{ID: "imageID4", Created: unixTime, CreatedBy: "/bin/bash grep", Size: int64(182964289), Comment: "Hi", Tags: []string{"image:tag2"}},
|
{ID: "imageID4", Created: unixTime, CreatedBy: "/bin/bash grep", Size: int64(182964289), Comment: "Hi", Tags: []string{"image:tag2"}},
|
||||||
}
|
}
|
||||||
// nolint: lll
|
//nolint:lll
|
||||||
expectedNoTrunc := `IMAGE CREATED CREATED BY SIZE COMMENT
|
expectedNoTrunc := `IMAGE CREATED CREATED BY SIZE COMMENT
|
||||||
imageID1 24 hours ago /bin/bash ls && npm i && npm run test && karma -c karma.conf.js start && npm start && more commands here && the list goes on 183MB Hi
|
imageID1 24 hours ago /bin/bash ls && npm i && npm run test && karma -c karma.conf.js start && npm start && more commands here && the list goes on 183MB Hi
|
||||||
imageID2 24 hours ago /bin/bash echo 183MB Hi
|
imageID2 24 hours ago /bin/bash echo 183MB Hi
|
||||||
|
|
|
@ -42,7 +42,8 @@ func TrustedPush(ctx context.Context, cli command.Cli, repoInfo *registry.Reposi
|
||||||
}
|
}
|
||||||
|
|
||||||
// PushTrustedReference pushes a canonical reference to the trust server.
|
// PushTrustedReference pushes a canonical reference to the trust server.
|
||||||
// nolint: gocyclo
|
//
|
||||||
|
//nolint:gocyclo
|
||||||
func PushTrustedReference(streams command.Streams, repoInfo *registry.RepositoryInfo, ref reference.Named, authConfig types.AuthConfig, in io.Reader) error {
|
func PushTrustedReference(streams command.Streams, repoInfo *registry.RepositoryInfo, ref reference.Named, authConfig types.AuthConfig, in io.Reader) error {
|
||||||
// If it is a trusted push we would like to find the target entry which match the
|
// If it is a trusted push we would like to find the target entry which match the
|
||||||
// tag provided in the function and then do an AddTarget later.
|
// tag provided in the function and then do an AddTarget later.
|
||||||
|
|
|
@ -68,7 +68,7 @@ func normalizeReference(ref string) (reference.Named, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// getManifest from the local store, and fallback to the remote registry if it
|
// getManifest from the local store, and fallback to the remote registry if it
|
||||||
// doesn't exist locally
|
// doesn't exist locally
|
||||||
func getManifest(ctx context.Context, dockerCli command.Cli, listRef, namedRef reference.Named, insecure bool) (types.ImageManifest, error) {
|
func getManifest(ctx context.Context, dockerCli command.Cli, listRef, namedRef reference.Named, insecure bool) (types.ImageManifest, error) {
|
||||||
data, err := dockerCli.ManifestStore().Get(listRef, namedRef)
|
data, err := dockerCli.ManifestStore().Get(listRef, namedRef)
|
||||||
switch {
|
switch {
|
||||||
|
|
|
@ -130,7 +130,8 @@ func runCreate(dockerCli command.Cli, options createOptions) error {
|
||||||
// possible to correlate the various related parameters and consolidate them.
|
// possible to correlate the various related parameters and consolidate them.
|
||||||
// consolidateIpam consolidates subnets, ip-ranges, gateways and auxiliary addresses into
|
// consolidateIpam consolidates subnets, ip-ranges, gateways and auxiliary addresses into
|
||||||
// structured ipam data.
|
// structured ipam data.
|
||||||
// nolint: gocyclo
|
//
|
||||||
|
//nolint:gocyclo
|
||||||
func consolidateIpam(subnets, ranges, gateways []string, auxaddrs map[string]string) ([]network.IPAMConfig, error) {
|
func consolidateIpam(subnets, ranges, gateways []string, auxaddrs map[string]string) ([]network.IPAMConfig, error) {
|
||||||
if len(subnets) < len(ranges) || len(subnets) < len(gateways) {
|
if len(subnets) < len(ranges) || len(subnets) < len(gateways) {
|
||||||
return nil, errors.Errorf("every ip-range or gateway must have a corresponding subnet")
|
return nil, errors.Errorf("every ip-range or gateway must have a corresponding subnet")
|
||||||
|
|
|
@ -101,7 +101,7 @@ func verifyloginOptions(dockerCli command.Cli, opts *loginOptions) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func runLogin(dockerCli command.Cli, opts loginOptions) error { //nolint: gocyclo
|
func runLogin(dockerCli command.Cli, opts loginOptions) error { //nolint:gocyclo
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
clnt := dockerCli.Client()
|
clnt := dockerCli.Client()
|
||||||
if err := verifyloginOptions(dockerCli, &opts); err != nil {
|
if err := verifyloginOptions(dockerCli, &opts); err != nil {
|
||||||
|
|
|
@ -109,7 +109,8 @@ func runList(dockerCli command.Cli, opts listOptions) error {
|
||||||
// there may be other situations where the client uses the "default" version.
|
// there may be other situations where the client uses the "default" version.
|
||||||
// To take these situations into account, we do a quick check for services
|
// To take these situations into account, we do a quick check for services
|
||||||
// that don't have ServiceStatus set, and perform a lookup for those.
|
// that don't have ServiceStatus set, and perform a lookup for those.
|
||||||
// nolint: gocyclo
|
//
|
||||||
|
//nolint:gocyclo
|
||||||
func AppendServiceStatus(ctx context.Context, c client.APIClient, services []swarm.Service) ([]swarm.Service, error) {
|
func AppendServiceStatus(ctx context.Context, c client.APIClient, services []swarm.Service) ([]swarm.Service, error) {
|
||||||
status := map[string]*swarm.ServiceStatus{}
|
status := map[string]*swarm.ServiceStatus{}
|
||||||
taskFilter := filters.NewArgs()
|
taskFilter := filters.NewArgs()
|
||||||
|
|
|
@ -466,9 +466,13 @@ func (opts *healthCheckOptions) toHealthConfig() (*container.HealthConfig, error
|
||||||
}
|
}
|
||||||
|
|
||||||
// convertExtraHostsToSwarmHosts converts an array of extra hosts in cli
|
// convertExtraHostsToSwarmHosts converts an array of extra hosts in cli
|
||||||
// <host>:<ip>
|
//
|
||||||
|
// <host>:<ip>
|
||||||
|
//
|
||||||
// into a swarmkit host format:
|
// into a swarmkit host format:
|
||||||
// IP_address canonical_hostname [aliases...]
|
//
|
||||||
|
// IP_address canonical_hostname [aliases...]
|
||||||
|
//
|
||||||
// This assumes input value (<host>:<ip>) has already been validated
|
// This assumes input value (<host>:<ip>) has already been validated
|
||||||
func convertExtraHostsToSwarmHosts(extraHosts []string) []string {
|
func convertExtraHostsToSwarmHosts(extraHosts []string) []string {
|
||||||
hosts := []string{}
|
hosts := []string{}
|
||||||
|
|
|
@ -68,7 +68,8 @@ func terminalState(state swarm.TaskState) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ServiceProgress outputs progress information for convergence of a service.
|
// ServiceProgress outputs progress information for convergence of a service.
|
||||||
// nolint: gocyclo
|
//
|
||||||
|
//nolint:gocyclo
|
||||||
func ServiceProgress(ctx context.Context, client client.APIClient, serviceID string, progressWriter io.WriteCloser) error {
|
func ServiceProgress(ctx context.Context, client client.APIClient, serviceID string, progressWriter io.WriteCloser) error {
|
||||||
defer progressWriter.Close()
|
defer progressWriter.Close()
|
||||||
|
|
||||||
|
|
|
@ -126,7 +126,7 @@ func newListOptsVarWithValidator(validator opts.ValidatorFctType) *opts.ListOpts
|
||||||
return opts.NewListOptsRef(&[]string{}, validator)
|
return opts.NewListOptsRef(&[]string{}, validator)
|
||||||
}
|
}
|
||||||
|
|
||||||
// nolint: gocyclo
|
//nolint:gocyclo
|
||||||
func runUpdate(dockerCli command.Cli, flags *pflag.FlagSet, options *serviceOptions, serviceID string) error {
|
func runUpdate(dockerCli command.Cli, flags *pflag.FlagSet, options *serviceOptions, serviceID string) error {
|
||||||
apiClient := dockerCli.Client()
|
apiClient := dockerCli.Client()
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
@ -252,7 +252,7 @@ func runUpdate(dockerCli command.Cli, flags *pflag.FlagSet, options *serviceOpti
|
||||||
return waitOnService(ctx, dockerCli, serviceID, options.quiet)
|
return waitOnService(ctx, dockerCli, serviceID, options.quiet)
|
||||||
}
|
}
|
||||||
|
|
||||||
// nolint: gocyclo
|
//nolint:gocyclo
|
||||||
func updateService(ctx context.Context, apiClient client.NetworkAPIClient, flags *pflag.FlagSet, spec *swarm.ServiceSpec) error {
|
func updateService(ctx context.Context, apiClient client.NetworkAPIClient, flags *pflag.FlagSet, spec *swarm.ServiceSpec) error {
|
||||||
updateBoolPtr := func(flag string, field **bool) {
|
updateBoolPtr := func(flag string, field **bool) {
|
||||||
if flags.Changed(flag) {
|
if flags.Changed(flag) {
|
||||||
|
@ -1139,37 +1139,37 @@ type hostMapping struct {
|
||||||
// Entries can be removed by either a specific `<host-name>:<ip-address>` mapping,
|
// Entries can be removed by either a specific `<host-name>:<ip-address>` mapping,
|
||||||
// or by `<host>` alone:
|
// or by `<host>` alone:
|
||||||
//
|
//
|
||||||
// - If both IP-address and host-name is provided, the hostname is removed only
|
// - If both IP-address and host-name is provided, the hostname is removed only
|
||||||
// from entries that match the given IP-address.
|
// from entries that match the given IP-address.
|
||||||
// - If only a host-name is provided, the hostname is removed from any entry it
|
// - If only a host-name is provided, the hostname is removed from any entry it
|
||||||
// is part of (either as canonical host-name, or as alias).
|
// is part of (either as canonical host-name, or as alias).
|
||||||
// - If, after removing the host-name from an entry, no host-names remain in
|
// - If, after removing the host-name from an entry, no host-names remain in
|
||||||
// the entry, the entry itself is removed.
|
// the entry, the entry itself is removed.
|
||||||
//
|
//
|
||||||
// For example, the list of host-entries before processing could look like this:
|
// For example, the list of host-entries before processing could look like this:
|
||||||
//
|
//
|
||||||
// hosts = &[]string{
|
// hosts = &[]string{
|
||||||
// "127.0.0.2 host3 host1 host2 host4",
|
// "127.0.0.2 host3 host1 host2 host4",
|
||||||
// "127.0.0.1 host1 host4",
|
// "127.0.0.1 host1 host4",
|
||||||
// "127.0.0.3 host1",
|
// "127.0.0.3 host1",
|
||||||
// "127.0.0.1 host1",
|
// "127.0.0.1 host1",
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// Removing `host1` removes every occurrence:
|
// Removing `host1` removes every occurrence:
|
||||||
//
|
//
|
||||||
// hosts = &[]string{
|
// hosts = &[]string{
|
||||||
// "127.0.0.2 host3 host2 host4",
|
// "127.0.0.2 host3 host2 host4",
|
||||||
// "127.0.0.1 host4",
|
// "127.0.0.1 host4",
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// Removing `host1:127.0.0.1` on the other hand, only remove the host if the
|
// Removing `host1:127.0.0.1` on the other hand, only remove the host if the
|
||||||
// IP-address matches:
|
// IP-address matches:
|
||||||
//
|
//
|
||||||
// hosts = &[]string{
|
// hosts = &[]string{
|
||||||
// "127.0.0.2 host3 host1 host2 host4",
|
// "127.0.0.2 host3 host1 host2 host4",
|
||||||
// "127.0.0.1 host4",
|
// "127.0.0.1 host4",
|
||||||
// "127.0.0.3 host1",
|
// "127.0.0.3 host1",
|
||||||
// }
|
// }
|
||||||
func updateHosts(flags *pflag.FlagSet, hosts *[]string) error {
|
func updateHosts(flags *pflag.FlagSet, hosts *[]string) error {
|
||||||
var toRemove []hostMapping
|
var toRemove []hostMapping
|
||||||
if flags.Changed(flagHostRemove) {
|
if flags.Changed(flagHostRemove) {
|
||||||
|
@ -1410,34 +1410,33 @@ func updateCredSpecConfig(flags *pflag.FlagSet, containerSpec *swarm.ContainerSp
|
||||||
//
|
//
|
||||||
// Adding/removing capabilities when updating a service is handled as a tri-state;
|
// Adding/removing capabilities when updating a service is handled as a tri-state;
|
||||||
//
|
//
|
||||||
// - if the capability was previously "dropped", then remove it from "CapabilityDrop",
|
// - if the capability was previously "dropped", then remove it from "CapabilityDrop",
|
||||||
// but NOT added to "CapabilityAdd". However, if the capability was not yet in
|
// but NOT added to "CapabilityAdd". However, if the capability was not yet in
|
||||||
// the service's "CapabilityDrop", then it's simply added to the service's "CapabilityAdd"
|
// the service's "CapabilityDrop", then it's simply added to the service's "CapabilityAdd"
|
||||||
// - likewise, if the capability was previously "added", then it's removed from
|
// - likewise, if the capability was previously "added", then it's removed from
|
||||||
// "CapabilityAdd", but NOT added to "CapabilityDrop". If the capability was
|
// "CapabilityAdd", but NOT added to "CapabilityDrop". If the capability was
|
||||||
// not yet in the service's "CapabilityAdd", then simply add it to the service's
|
// not yet in the service's "CapabilityAdd", then simply add it to the service's
|
||||||
// "CapabilityDrop".
|
// "CapabilityDrop".
|
||||||
//
|
//
|
||||||
// In other words, given a service with the following:
|
// In other words, given a service with the following:
|
||||||
//
|
//
|
||||||
// | CapDrop | CapAdd |
|
// | CapDrop | CapAdd |
|
||||||
// | -------------- | ------------- |
|
// |----------------|---------------|
|
||||||
// | CAP_SOME_CAP | |
|
// | CAP_SOME_CAP | |
|
||||||
//
|
//
|
||||||
// When updating the service, and applying `--cap-add CAP_SOME_CAP`, the previously
|
// When updating the service, and applying `--cap-add CAP_SOME_CAP`, the previously
|
||||||
// dropped capability is removed:
|
// dropped capability is removed:
|
||||||
//
|
//
|
||||||
// | CapDrop | CapAdd |
|
// | CapDrop | CapAdd |
|
||||||
// | -------------- | ------------- |
|
// |----------------|---------------|
|
||||||
// | | |
|
// | | |
|
||||||
//
|
//
|
||||||
// After updating the service a second time, applying `--cap-add CAP_SOME_CAP`,
|
// After updating the service a second time, applying `--cap-add CAP_SOME_CAP`,
|
||||||
// capability is now added:
|
// capability is now added:
|
||||||
//
|
//
|
||||||
// | CapDrop | CapAdd |
|
// | CapDrop | CapAdd |
|
||||||
// | -------------- | ------------- |
|
// |----------------|---------------|
|
||||||
// | | CAP_SOME_CAP |
|
// | | CAP_SOME_CAP |
|
||||||
//
|
|
||||||
func updateCapabilities(flags *pflag.FlagSet, containerSpec *swarm.ContainerSpec) {
|
func updateCapabilities(flags *pflag.FlagSet, containerSpec *swarm.ContainerSpec) {
|
||||||
var (
|
var (
|
||||||
toAdd, toDrop map[string]bool
|
toAdd, toDrop map[string]bool
|
||||||
|
|
|
@ -520,8 +520,9 @@ func (s secretAPIClientMock) SecretUpdate(ctx context.Context, id string, versio
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestUpdateSecretUpdateInPlace tests the ability to update the "target" of an secret with "docker service update"
|
// TestUpdateSecretUpdateInPlace tests the ability to update the "target" of a
|
||||||
// by combining "--secret-rm" and "--secret-add" for the same secret.
|
// secret with "docker service update" by combining "--secret-rm" and
|
||||||
|
// "--secret-add" for the same secret.
|
||||||
func TestUpdateSecretUpdateInPlace(t *testing.T) {
|
func TestUpdateSecretUpdateInPlace(t *testing.T) {
|
||||||
apiClient := secretAPIClientMock{
|
apiClient := secretAPIClientMock{
|
||||||
listResult: []swarm.Secret{
|
listResult: []swarm.Secret{
|
||||||
|
|
|
@ -175,7 +175,7 @@ func createNetworks(ctx context.Context, dockerCli command.Cli, namespace conver
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// nolint: gocyclo
|
//nolint:gocyclo
|
||||||
func deployServices(ctx context.Context, dockerCli command.Cli, services map[string]swarm.ServiceSpec, namespace convert.Namespace, sendAuth bool, resolveImage string) error {
|
func deployServices(ctx context.Context, dockerCli command.Cli, services map[string]swarm.ServiceSpec, namespace convert.Namespace, sendAuth bool, resolveImage string) error {
|
||||||
apiClient := dockerCli.Client()
|
apiClient := dockerCli.Client()
|
||||||
out := dockerCli.Out()
|
out := dockerCli.Out()
|
||||||
|
|
|
@ -195,7 +195,7 @@ func prettyPrintClientInfo(dockerCli command.Cli, info clientInfo) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// nolint: gocyclo
|
//nolint:gocyclo
|
||||||
func prettyPrintServerInfo(dockerCli command.Cli, info types.Info) []error {
|
func prettyPrintServerInfo(dockerCli command.Cli, info types.Info) []error {
|
||||||
var errs []error
|
var errs []error
|
||||||
|
|
||||||
|
@ -360,7 +360,7 @@ func prettyPrintServerInfo(dockerCli command.Cli, info types.Info) []error {
|
||||||
return errs
|
return errs
|
||||||
}
|
}
|
||||||
|
|
||||||
// nolint: gocyclo
|
//nolint:gocyclo
|
||||||
func printSwarmInfo(dockerCli command.Cli, info types.Info) {
|
func printSwarmInfo(dockerCli command.Cli, info types.Info) {
|
||||||
if info.Swarm.LocalNodeState == swarm.LocalNodeStateInactive || info.Swarm.LocalNodeState == swarm.LocalNodeStateLocked {
|
if info.Swarm.LocalNodeState == swarm.LocalNodeStateInactive || info.Swarm.LocalNodeState == swarm.LocalNodeStateLocked {
|
||||||
return
|
return
|
||||||
|
|
|
@ -227,7 +227,7 @@ func mergeLoggingConfig(dst, src reflect.Value) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
//nolint: unparam
|
//nolint:unparam
|
||||||
func mergeUlimitsConfig(dst, src reflect.Value) error {
|
func mergeUlimitsConfig(dst, src reflect.Value) error {
|
||||||
if src.Interface() != reflect.Zero(reflect.TypeOf(src.Interface())).Interface() {
|
if src.Interface() != reflect.Zero(reflect.TypeOf(src.Interface())).Interface() {
|
||||||
dst.Elem().Set(src.Elem())
|
dst.Elem().Set(src.Elem())
|
||||||
|
@ -235,7 +235,7 @@ func mergeUlimitsConfig(dst, src reflect.Value) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
//nolint: unparam
|
//nolint:unparam
|
||||||
func mergeShellCommand(dst, src reflect.Value) error {
|
func mergeShellCommand(dst, src reflect.Value) error {
|
||||||
if src.Len() != 0 {
|
if src.Len() != 0 {
|
||||||
dst.Set(src)
|
dst.Set(src)
|
||||||
|
@ -243,7 +243,7 @@ func mergeShellCommand(dst, src reflect.Value) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
//nolint: unparam
|
//nolint:unparam
|
||||||
func mergeServiceNetworkConfig(dst, src reflect.Value) error {
|
func mergeServiceNetworkConfig(dst, src reflect.Value) error {
|
||||||
if src.Interface() != reflect.Zero(reflect.TypeOf(src.Interface())).Interface() {
|
if src.Interface() != reflect.Zero(reflect.TypeOf(src.Interface())).Interface() {
|
||||||
dst.Elem().FieldByName("Aliases").Set(src.Elem().FieldByName("Aliases"))
|
dst.Elem().FieldByName("Aliases").Set(src.Elem().FieldByName("Aliases"))
|
||||||
|
|
|
@ -28,7 +28,8 @@ func isAbs(path string) (b bool) {
|
||||||
|
|
||||||
// volumeNameLen returns length of the leading volume name on Windows.
|
// volumeNameLen returns length of the leading volume name on Windows.
|
||||||
// It returns 0 elsewhere.
|
// It returns 0 elsewhere.
|
||||||
// nolint: gocyclo
|
//
|
||||||
|
//nolint:gocyclo
|
||||||
func volumeNameLen(path string) int {
|
func volumeNameLen(path string) int {
|
||||||
if len(path) < 2 {
|
if len(path) < 2 {
|
||||||
return 0
|
return 0
|
||||||
|
|
|
@ -4,13 +4,13 @@
|
||||||
// For example, to provide an http.Client that can connect to a Docker daemon
|
// For example, to provide an http.Client that can connect to a Docker daemon
|
||||||
// running in a Docker container ("DIND"):
|
// running in a Docker container ("DIND"):
|
||||||
//
|
//
|
||||||
// httpClient := &http.Client{
|
// httpClient := &http.Client{
|
||||||
// Transport: &http.Transport{
|
// Transport: &http.Transport{
|
||||||
// DialContext: func(ctx context.Context, _network, _addr string) (net.Conn, error) {
|
// DialContext: func(ctx context.Context, _network, _addr string) (net.Conn, error) {
|
||||||
// return commandconn.New(ctx, "docker", "exec", "-it", containerID, "docker", "system", "dial-stdio")
|
// return commandconn.New(ctx, "docker", "exec", "-it", containerID, "docker", "system", "dial-stdio")
|
||||||
// },
|
// },
|
||||||
// },
|
// },
|
||||||
// }
|
// }
|
||||||
package commandconn
|
package commandconn
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
|
@ -67,7 +67,7 @@ func (c *Endpoint) tlsConfig() (*tls.Config, error) {
|
||||||
if pemBlock == nil {
|
if pemBlock == nil {
|
||||||
return nil, errors.New("no valid private key found")
|
return nil, errors.New("no valid private key found")
|
||||||
}
|
}
|
||||||
if x509.IsEncryptedPEMBlock(pemBlock) { //nolint: staticcheck // SA1019: x509.IsEncryptedPEMBlock is deprecated, and insecure by design
|
if x509.IsEncryptedPEMBlock(pemBlock) { //nolint:staticcheck // SA1019: x509.IsEncryptedPEMBlock is deprecated, and insecure by design
|
||||||
return nil, errors.New("private key is encrypted - support for encrypted private keys has been removed, see https://docs.docker.com/go/deprecated/")
|
return nil, errors.New("private key is encrypted - support for encrypted private keys has been removed, see https://docs.docker.com/go/deprecated/")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,20 +1,32 @@
|
||||||
// Package store provides a generic way to store credentials to connect to virtually any kind of remote system.
|
// Package store provides a generic way to store credentials to connect to
|
||||||
// The term `context` comes from the similar feature in Kubernetes kubectl config files.
|
// virtually any kind of remote system.
|
||||||
|
// The term `context` comes from the similar feature in Kubernetes kubectl
|
||||||
|
// config files.
|
||||||
//
|
//
|
||||||
// Conceptually, a context is a set of metadata and TLS data, that can be used to connect to various endpoints
|
// Conceptually, a context is a set of metadata and TLS data, that can be used
|
||||||
// of a remote system. TLS data and metadata are stored separately, so that in the future, we will be able to store sensitive
|
// to connect to various endpoints of a remote system. TLS data and metadata
|
||||||
// information in a more secure way, depending on the os we are running on (e.g.: on Windows we could use the user Certificate Store, on Mac OS the user Keychain...).
|
// are stored separately, so that in the future, we will be able to store
|
||||||
|
// sensitive information in a more secure way, depending on the os we are running
|
||||||
|
// on (e.g.: on Windows we could use the user Certificate Store, on macOS the
|
||||||
|
// user Keychain...).
|
||||||
//
|
//
|
||||||
// Current implementation is purely file based with the following structure:
|
// Current implementation is purely file based with the following structure:
|
||||||
// ${CONTEXT_ROOT}
|
|
||||||
// - meta/
|
|
||||||
// - <context id>/meta.json: contains context medata (key/value pairs) as well as a list of endpoints (themselves containing key/value pair metadata)
|
|
||||||
// - tls/
|
|
||||||
// - <context id>/endpoint1/: directory containing TLS data for the endpoint1 in the corresponding context
|
|
||||||
//
|
//
|
||||||
// The context store itself has absolutely no knowledge about what a docker endpoint should contain in term of metadata or TLS config.
|
// ${CONTEXT_ROOT}
|
||||||
// Client code is responsible for generating and parsing endpoint metadata and TLS files.
|
// meta/
|
||||||
// The multi-endpoints approach of this package allows to combine many different endpoints in the same "context".
|
// <context id>/meta.json: contains context medata (key/value pairs) as
|
||||||
|
// well as a list of endpoints (themselves containing
|
||||||
|
// key/value pair metadata).
|
||||||
|
// tls/
|
||||||
|
// <context id>/endpoint1/: directory containing TLS data for the endpoint1
|
||||||
|
// in the corresponding context.
|
||||||
//
|
//
|
||||||
// Context IDs are actually SHA256 hashes of the context name, and are there only to avoid dealing with special characters in context names.
|
// The context store itself has absolutely no knowledge about what a docker
|
||||||
|
// endpoint should contain in term of metadata or TLS config. Client code is
|
||||||
|
// responsible for generating and parsing endpoint metadata and TLS files. The
|
||||||
|
// multi-endpoints approach of this package allows to combine many different
|
||||||
|
// endpoints in the same "context".
|
||||||
|
//
|
||||||
|
// Context IDs are actually SHA256 hashes of the context name, and are there
|
||||||
|
// only to avoid dealing with special characters in context names.
|
||||||
package store
|
package store
|
||||||
|
|
|
@ -43,7 +43,7 @@ func (s *tlsStore) getData(contextID contextdir, endpointName, filename string)
|
||||||
return data, nil
|
return data, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *tlsStore) remove(contextID contextdir, endpointName, filename string) error { // nolint:unused
|
func (s *tlsStore) remove(contextID contextdir, endpointName, filename string) error { //nolint:unused
|
||||||
err := os.Remove(s.filePath(contextID, endpointName, filename))
|
err := os.Remove(s.filePath(contextID, endpointName, filename))
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -99,7 +99,7 @@ func ExactArgs(number int) cobra.PositionalArgs {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//nolint: unparam
|
//nolint:unparam
|
||||||
func pluralize(word string, number int) string {
|
func pluralize(word string, number int) string {
|
||||||
if number == 1 {
|
if number == 1 {
|
||||||
return word
|
return word
|
||||||
|
|
|
@ -325,7 +325,7 @@ func createImage(t *testing.T, repo string, tags ...string) string {
|
||||||
return fmt.Sprintf("%s/%s:%s", registryPrefix, repo, tags[0])
|
return fmt.Sprintf("%s/%s:%s", registryPrefix, repo, tags[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
//nolint: unparam
|
//nolint:unparam
|
||||||
func withNotaryPassphrase(pwd string) func(*icmd.Cmd) {
|
func withNotaryPassphrase(pwd string) func(*icmd.Cmd) {
|
||||||
return func(c *icmd.Cmd) {
|
return func(c *icmd.Cmd) {
|
||||||
c.Env = append(c.Env, []string{
|
c.Env = append(c.Env, []string{
|
||||||
|
|
|
@ -1,3 +1,2 @@
|
||||||
// Package builders helps you create struct for your unit test while keeping them expressive.
|
// Package builders helps you create struct for your unit test while keeping them expressive.
|
||||||
//
|
|
||||||
package builders
|
package builders
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
// Package test is a test-only package that can be used by other cli package to write unit test.
|
// Package test is a test-only package that can be used by other cli package to write unit test.
|
||||||
//
|
//
|
||||||
// It as an internal package and cannot be used outside of github.com/docker/cli package.
|
// It as an internal package and cannot be used outside of github.com/docker/cli package.
|
||||||
//
|
|
||||||
package test
|
package test
|
||||||
|
|
|
@ -6,12 +6,12 @@ import (
|
||||||
|
|
||||||
// ParseEnvFile reads a file with environment variables enumerated by lines
|
// ParseEnvFile reads a file with environment variables enumerated by lines
|
||||||
//
|
//
|
||||||
// ``Environment variable names used by the utilities in the Shell and
|
// “Environment variable names used by the utilities in the Shell and
|
||||||
// Utilities volume of IEEE Std 1003.1-2001 consist solely of uppercase
|
// Utilities volume of IEEE Std 1003.1-2001 consist solely of uppercase
|
||||||
// letters, digits, and the '_' (underscore) from the characters defined in
|
// letters, digits, and the '_' (underscore) from the characters defined in
|
||||||
// Portable Character Set and do not begin with a digit. *But*, other
|
// Portable Character Set and do not begin with a digit. *But*, other
|
||||||
// characters may be permitted by an implementation; applications shall
|
// characters may be permitted by an implementation; applications shall
|
||||||
// tolerate the presence of such names.''
|
// tolerate the presence of such names.”
|
||||||
// -- http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap08.html
|
// -- http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap08.html
|
||||||
//
|
//
|
||||||
// As of #16585, it's up to application inside docker to validate or not
|
// As of #16585, it's up to application inside docker to validate or not
|
||||||
|
|
|
@ -24,7 +24,8 @@ func parseCount(s string) (int, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set a new mount value
|
// Set a new mount value
|
||||||
// nolint: gocyclo
|
//
|
||||||
|
//nolint:gocyclo
|
||||||
func (o *GpuOpts) Set(value string) error {
|
func (o *GpuOpts) Set(value string) error {
|
||||||
csvReader := csv.NewReader(strings.NewReader(value))
|
csvReader := csv.NewReader(strings.NewReader(value))
|
||||||
fields, err := csvReader.Read()
|
fields, err := csvReader.Read()
|
||||||
|
|
|
@ -18,7 +18,8 @@ type MountOpt struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set a new mount value
|
// Set a new mount value
|
||||||
// nolint: gocyclo
|
//
|
||||||
|
//nolint:gocyclo
|
||||||
func (m *MountOpt) Set(value string) error {
|
func (m *MountOpt) Set(value string) error {
|
||||||
csvReader := csv.NewReader(strings.NewReader(value))
|
csvReader := csv.NewReader(strings.NewReader(value))
|
||||||
fields, err := csvReader.Read()
|
fields, err := csvReader.Read()
|
||||||
|
|
|
@ -118,7 +118,7 @@ func TestListOptsWithValidator(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// nolint: lll
|
//nolint:lll
|
||||||
func TestValidateDNSSearch(t *testing.T) {
|
func TestValidateDNSSearch(t *testing.T) {
|
||||||
valid := []string{
|
valid := []string{
|
||||||
`.`,
|
`.`,
|
||||||
|
|
|
@ -55,7 +55,9 @@ func ConvertKVStringsToMap(values []string) map[string]string {
|
||||||
// ConvertKVStringsToMapWithNil converts ["key=value"] to {"key":"value"}
|
// ConvertKVStringsToMapWithNil converts ["key=value"] to {"key":"value"}
|
||||||
// but set unset keys to nil - meaning the ones with no "=" in them.
|
// but set unset keys to nil - meaning the ones with no "=" in them.
|
||||||
// We use this in cases where we need to distinguish between
|
// We use this in cases where we need to distinguish between
|
||||||
// FOO= and FOO
|
//
|
||||||
|
// FOO= and FOO
|
||||||
|
//
|
||||||
// where the latter case just means FOO was mentioned but not given a value
|
// where the latter case just means FOO was mentioned but not given a value
|
||||||
func ConvertKVStringsToMapWithNil(values []string) map[string]*string {
|
func ConvertKVStringsToMapWithNil(values []string) map[string]*string {
|
||||||
result := make(map[string]*string, len(values))
|
result := make(map[string]*string, len(values))
|
||||||
|
|
|
@ -26,7 +26,8 @@ type PortOpt struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set a new port value
|
// Set a new port value
|
||||||
// nolint: gocyclo
|
//
|
||||||
|
//nolint:gocyclo
|
||||||
func (p *PortOpt) Set(value string) error {
|
func (p *PortOpt) Set(value string) error {
|
||||||
longSyntax, err := regexp.MatchString(`\w+=\w+(,\w+=\w+)*`, value)
|
longSyntax, err := regexp.MatchString(`\w+=\w+(,\w+=\w+)*`, value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
Loading…
Reference in New Issue