diff --git a/cmd/kubevpn/cmds/dev.go b/cmd/kubevpn/cmds/dev.go index 2a98f027..2f6435ad 100644 --- a/cmd/kubevpn/cmds/dev.go +++ b/cmd/kubevpn/cmds/dev.go @@ -32,9 +32,9 @@ func CmdDev(f cmdutil.Factory) *cobra.Command { Env: opts.NewListOpts(nil), Volumes: opts.NewListOpts(nil), ExtraHosts: opts.NewListOpts(nil), - //Aliases: opts.NewListOpts(nil), - NoProxy: false, - ExtraCIDR: []string{}, + Aliases: opts.NewListOpts(nil), + NoProxy: false, + ExtraCIDR: []string{}, } var sshConf = &util.SshConfig{} cmd := &cobra.Command{ @@ -164,9 +164,9 @@ Startup your kubernetes workloads in local Docker container with same volume态e cmd.Flags().Var(&devOptions.NetMode, "network", "Connect a container to a network") cmd.Flags().MarkHidden("net") // We allow for both "--net-alias" and "--network-alias", although the latter is the recommended way. - //cmd.Flags().Var(&devOptions.Aliases, "net-alias", "Add network-scoped alias for the container") - //cmd.Flags().Var(&devOptions.Aliases, "network-alias", "Add network-scoped alias for the container") - //cmd.Flags().MarkHidden("net-alias") + cmd.Flags().Var(&devOptions.Aliases, "net-alias", "Add network-scoped alias for the container") + cmd.Flags().Var(&devOptions.Aliases, "network-alias", "Add network-scoped alias for the container") + cmd.Flags().MarkHidden("net-alias") cmd.Flags().VarP(&devOptions.Volumes, "volume", "v", "Bind mount a volume") cmd.Flags().Var(&devOptions.Mounts, "mount", "Attach a filesystem mount to the container") cmd.Flags().Var(&devOptions.Expose, "expose", "Expose a port or a range of ports") diff --git a/pkg/dev/main.go b/pkg/dev/main.go index 4f48f013..124ef49c 100644 --- a/pkg/dev/main.go +++ b/pkg/dev/main.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "math/rand" + "net" "os" "path/filepath" "sort" @@ -16,7 +17,9 @@ import ( "github.com/docker/docker/api/types/container" containertypes "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/mount" + "github.com/docker/docker/api/types/network" "github.com/docker/docker/client" + "github.com/docker/docker/errdefs" "github.com/docker/docker/pkg/archive" log "github.com/sirupsen/logrus" v1 "k8s.io/api/core/v1" @@ -27,8 +30,10 @@ import ( "k8s.io/kubectl/pkg/polymorphichelpers" "k8s.io/kubectl/pkg/util/podutils" + "github.com/wencaiwulue/kubevpn/pkg/config" "github.com/wencaiwulue/kubevpn/pkg/handler" "github.com/wencaiwulue/kubevpn/pkg/mesh" + "github.com/wencaiwulue/kubevpn/pkg/tun" "github.com/wencaiwulue/kubevpn/pkg/util" ) @@ -45,14 +50,14 @@ type Options struct { // docker options Platform string //Pull string // always, missing, never - PublishAll bool - Entrypoint string - DockerImage string - Publish opts.ListOpts - Expose opts.ListOpts - ExtraHosts opts.ListOpts - NetMode opts.NetworkOpt - //Aliases opts.ListOpts + PublishAll bool + Entrypoint string + DockerImage string + Publish opts.ListOpts + Expose opts.ListOpts + ExtraHosts opts.ListOpts + NetMode opts.NetworkOpt + Aliases opts.ListOpts Env opts.ListOpts Mounts opts.MountOpt Volumes opts.ListOpts @@ -146,6 +151,43 @@ func (d Options) Main(ctx context.Context) error { config.config.Hostname = "" } } else { + getInterface, err := tun.GetInterface() + if err != nil { + return err + } + addrs, err := getInterface.Addrs() + if err != nil { + return err + } + cidr, _, err := net.ParseCIDR(addrs[0].String()) + if err != nil { + return err + } + create, err := cli.NetworkCreate(ctx, list[0].containerName, types.NetworkCreate{ + Driver: "bridge", + Scope: "local", + IPAM: &network.IPAM{ + Driver: "", + Options: nil, + Config: []network.IPAMConfig{ + { + Subnet: config.CIDR.String(), + Gateway: cidr.String(), + }, + }, + }, + Internal: true, + }) + if err != nil { + if errdefs.IsForbidden(err) { + _, _ = cli.NetworkList(ctx, types.NetworkListOptions{}) + } + return err + } + + list[0].networkingConfig.EndpointsConfig[list[0].containerName] = &network.EndpointSettings{ + NetworkID: create.ID, + } // skip first for _, config := range list[1:] { // remove expose port @@ -182,24 +224,24 @@ func (r Run) Remove(ctx context.Context) error { } for _, config := range r { - //err = cli.NetworkDisconnect(ctx, config.networkName, config.containerName, true) - //if err != nil { - // log.Errorln(err) - //} + err = cli.NetworkDisconnect(ctx, config.containerName, config.containerName, true) + if err != nil { + log.Errorln(err) + } err = cli.ContainerRemove(ctx, config.containerName, types.ContainerRemoveOptions{Force: true}) if err != nil { log.Errorln(err) } } - //for _, config := range r { - // _, err = cli.NetworkInspect(ctx, config.networkName, types.NetworkInspectOptions{}) - // if err == nil { - // err = cli.NetworkRemove(ctx, config.networkName) - // if err != nil { - // log.Errorln(err) - // } - // } - //} + for _, config := range r { + _, err = cli.NetworkInspect(ctx, config.containerName, types.NetworkInspectOptions{}) + if err == nil { + err = cli.NetworkRemove(ctx, config.containerName) + if err != nil { + log.Errorln(err) + } + } + } return nil } diff --git a/pkg/dev/option.go b/pkg/dev/option.go index 30ffcc3b..18eca2eb 100644 --- a/pkg/dev/option.go +++ b/pkg/dev/option.go @@ -3,13 +3,17 @@ package dev import ( "fmt" "path/filepath" + "reflect" "strconv" "strings" "github.com/docker/cli/cli/compose/loader" "github.com/docker/cli/opts" + "github.com/docker/docker/api/types/container" mounttypes "github.com/docker/docker/api/types/mount" + "github.com/docker/docker/api/types/network" "github.com/docker/docker/api/types/strslice" + "github.com/docker/docker/errdefs" "github.com/docker/go-connections/nat" v12 "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors" @@ -147,6 +151,11 @@ func fillOptions(r Run, copts Options) error { } config.hostConfig.Binds = binds + networkOpts, err := parseNetworkOpts(copts) + if err != nil { + return err + } + config.networkingConfig = &network.NetworkingConfig{EndpointsConfig: networkOpts} return nil } @@ -171,3 +180,87 @@ func convertToStandardNotation(ports []string) ([]string, error) { } return optsList, nil } + +// parseNetworkOpts converts --network advanced options to endpoint-specs, and combines +// them with the old --network-alias and --links. If returns an error if conflicting options +// are found. +// +// this function may return _multiple_ endpoints, which is not currently supported +// by the daemon, but may be in future; it's up to the daemon to produce an error +// in case that is not supported. +func parseNetworkOpts(copts Options) (map[string]*network.EndpointSettings, error) { + var ( + endpoints = make(map[string]*network.EndpointSettings, len(copts.NetMode.Value())) + hasUserDefined, hasNonUserDefined bool + ) + + for i, n := range copts.NetMode.Value() { + n := n + if container.NetworkMode(n.Target).IsUserDefined() { + hasUserDefined = true + } else { + hasNonUserDefined = true + } + if i == 0 { + // The first network corresponds with what was previously the "only" + // network, and what would be used when using the non-advanced syntax + // `--network-alias`, `--link`, `--ip`, `--ip6`, and `--link-local-ip` + // are set on this network, to preserve backward compatibility with + // the non-advanced notation + n.Aliases = copts.Aliases.GetAll() + } + ep, err := parseNetworkAttachmentOpt(n) + if err != nil { + return nil, err + } + if _, ok := endpoints[n.Target]; ok { + return nil, errdefs.InvalidParameter(errors.Errorf("network %q is specified multiple times", n.Target)) + } + + // For backward compatibility: if no custom options are provided for the network, + // and only a single network is specified, omit the endpoint-configuration + // on the client (the daemon will still create it when creating the container) + if i == 0 && len(copts.NetMode.Value()) == 1 { + if ep == nil || reflect.DeepEqual(*ep, network.EndpointSettings{}) { + continue + } + } + endpoints[n.Target] = ep + } + if hasUserDefined && hasNonUserDefined { + return nil, errdefs.InvalidParameter(errors.New("conflicting options: cannot attach both user-defined and non-user-defined network-modes")) + } + return endpoints, nil +} + +func parseNetworkAttachmentOpt(ep opts.NetworkAttachmentOpts) (*network.EndpointSettings, error) { + if strings.TrimSpace(ep.Target) == "" { + return nil, errors.New("no name set for network") + } + if !container.NetworkMode(ep.Target).IsUserDefined() { + if len(ep.Aliases) > 0 { + return nil, errors.New("network-scoped aliases are only supported for user-defined networks") + } + if len(ep.Links) > 0 { + return nil, errors.New("links are only supported for user-defined networks") + } + } + + epConfig := &network.EndpointSettings{} + epConfig.Aliases = append(epConfig.Aliases, ep.Aliases...) + if len(ep.DriverOpts) > 0 { + epConfig.DriverOpts = make(map[string]string) + epConfig.DriverOpts = ep.DriverOpts + } + if len(ep.Links) > 0 { + epConfig.Links = ep.Links + } + if ep.IPv4Address != "" || ep.IPv6Address != "" || len(ep.LinkLocalIPs) > 0 { + epConfig.IPAMConfig = &network.EndpointIPAMConfig{ + IPv4Address: ep.IPv4Address, + IPv6Address: ep.IPv6Address, + LinkLocalIPs: ep.LinkLocalIPs, + } + } + return epConfig, nil +}