Skip to content
This repository was archived by the owner on Feb 8, 2021. It is now read-only.

Commit 20ab5d6

Browse files
authored
Merge pull request #652 from gnawux/portmapping
pod level port-mapping support
2 parents f0c8bb2 + 5453b70 commit 20ab5d6

36 files changed

+2863
-476
lines changed

Makefile.am

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ if ON_DARWIN
2626
SUBDIRS=mac_installer
2727
endif
2828

29-
VERSION_PARAM=-ldflags "-X github.com/hyperhq/hyperd/utils.VERSION=$(VERSION) -X github.com/hyperhq/hyperd/utils.GITCOMMIT=`git describe`"
29+
VERSION_PARAM=-ldflags "-X github.com/hyperhq/hyperd/utils.VERSION=$(VERSION) -X github.com/hyperhq/hyperd/utils.GITCOMMIT=`git describe --dirty --always --tags 2> /dev/null || true`"
3030

3131
all-local: build-hyperd build-hyperctl build-vmlogd
3232
clean-local:

client/api/interface.go

+5
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@ type APIInterface interface {
3939
UnpausePod(podId string) error
4040
KillPod(pod string, sig int) error
4141

42+
// PortMapping APIs
43+
ListPortMappings(podId string) ([]*types.PortMapping, error)
44+
AddPortMappings(podId string, pms []*types.PortMapping) error
45+
DeletePortMappings(podId string, pms []*types.PortMapping) error
46+
4247
Build(name string, hasBody bool, body io.Reader) (io.ReadCloser, string, error)
4348
Commit(container, repo, author, message string, changes []string, pause bool) (string, error)
4449
Load(body io.Reader, name string, refs map[string]string) (io.ReadCloser, string, error)

client/api/portmapping.go

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package api
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"net/http"
7+
8+
"github.com/hyperhq/hyperd/types"
9+
)
10+
11+
type PortMappingList struct {
12+
PortMappings []*types.PortMapping `json:"portMappings"`
13+
}
14+
15+
func (c *Client) ListPortMappings(podId string) ([]*types.PortMapping, error) {
16+
path := fmt.Sprintf("/pod/%s/portmappings", podId)
17+
18+
body, code, err := readBody(c.call("GET", path, nil, nil))
19+
if code == http.StatusNotFound {
20+
return nil, fmt.Errorf("pod %s not found", podId)
21+
} else if err != nil {
22+
return nil, err
23+
}
24+
25+
var pms PortMappingList
26+
err = json.Unmarshal(body, &pms)
27+
if err != nil {
28+
return nil, err
29+
}
30+
31+
return pms.PortMappings, nil
32+
}
33+
34+
func (c *Client) AddPortMappings(podId string, pms []*types.PortMapping) error {
35+
path := fmt.Sprintf("/pod/%s/portmappings/add", podId)
36+
r, code, err := readBody(c.call("PUT", path, pms, nil))
37+
38+
if code == http.StatusNoContent || code == http.StatusOK {
39+
return nil
40+
} else if code == http.StatusNotFound {
41+
return fmt.Errorf("pod %s not found", podId)
42+
} else if err != nil {
43+
return err
44+
} else {
45+
return fmt.Errorf("unexpect response code %d: %s", code, string(r))
46+
}
47+
}
48+
49+
func (c *Client) DeletePortMappings(podId string, pms []*types.PortMapping) error {
50+
path := fmt.Sprintf("/pod/%s/portmappings/delete", podId)
51+
r, code, err := readBody(c.call("PUT", path, pms, nil))
52+
53+
if code == http.StatusNoContent || code == http.StatusOK {
54+
return nil
55+
} else if code == http.StatusNotFound {
56+
return fmt.Errorf("pod %s not found", podId)
57+
} else if err != nil {
58+
return err
59+
} else {
60+
return fmt.Errorf("unexpect response code %d: %s", code, string(r))
61+
}
62+
}

client/common_options.go

+18-28
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import (
77
"os"
88
"path/filepath"
99
"regexp"
10-
"strconv"
1110
"strings"
1211

1312
"github.com/docker/docker/pkg/namesgenerator"
@@ -98,7 +97,7 @@ func (cli *HyperClient) JsonFromCmdline(container bool, cmdArgs, cmdEnvs, cmdPor
9897
image = cmdArgs[0]
9998
command = []string{}
10099
env = []*apitype.EnvironmentVar{}
101-
ports = []*apitype.UserContainerPort{}
100+
ports = []*apitype.PortMapping{}
102101
logOpts = make(map[string]string)
103102
labels = make(map[string]string)
104103
volumesRef = []*apitype.UserVolumeReference{}
@@ -170,7 +169,6 @@ func (cli *HyperClient) JsonFromCmdline(container bool, cmdArgs, cmdEnvs, cmdPor
170169
Command: command,
171170
Workdir: cmdWorkdir,
172171
Entrypoint: entrypoints,
173-
Ports: ports,
174172
Envs: env,
175173
Volumes: volumesRef,
176174
Files: []*apitype.UserFileReference{},
@@ -190,6 +188,7 @@ func (cli *HyperClient) JsonFromCmdline(container bool, cmdArgs, cmdEnvs, cmdPor
190188
Type: cmdLogDriver,
191189
Config: logOpts,
192190
},
191+
Portmappings: ports,
193192
}
194193
body = userPod
195194
}
@@ -265,45 +264,36 @@ func parseVolume(volStr string) (*apitype.UserVolume, *apitype.UserVolumeReferen
265264
return &vol, &volRef, nil
266265
}
267266

268-
func parsePortMapping(portmap string) (*apitype.UserContainerPort, error) {
267+
func parsePortMapping(portmap string) (*apitype.PortMapping, error) {
269268

270269
var (
271-
port = apitype.UserContainerPort{}
272-
proto string
273-
hPort string
274-
cPort string
275-
err error
270+
tmp *apitype.PortMapping
271+
port *apitype.PortMapping
272+
err error
276273
)
277274

278275
fields := strings.Split(portmap, ":")
279276
if len(fields) < 2 {
280277
return nil, fmt.Errorf("flag needs host port and container port: --publish")
281278
} else if len(fields) == 2 {
282-
proto = "tcp"
283-
hPort = fields[0]
284-
cPort = fields[1]
279+
tmp = &apitype.PortMapping{
280+
Protocol: "tcp",
281+
ContainerPort: fields[1],
282+
HostPort: fields[0],
283+
}
285284
} else {
286-
proto = fields[0]
287-
if proto != "tcp" && proto != "udp" {
288-
return nil, fmt.Errorf("flag needs protocol(tcp or udp): --publish")
285+
tmp = &apitype.PortMapping{
286+
Protocol: fields[0],
287+
ContainerPort: fields[2],
288+
HostPort: fields[1],
289289
}
290-
hPort = fields[1]
291-
cPort = fields[2]
292290
}
293291

294-
port.Protocol = proto
295-
hp, err := strconv.Atoi(hPort)
296-
port.HostPort = int32(hp)
297-
if err != nil {
298-
return nil, fmt.Errorf("flag needs host port and container port: --publish: %v", err)
299-
}
300-
cp, err := strconv.Atoi(cPort)
301-
port.ContainerPort = int32(cp)
292+
port, err = tmp.Formalize()
302293
if err != nil {
303-
return nil, fmt.Errorf("flag needs host port and container port: --publish: %v", err)
294+
return nil, err
304295
}
305-
306-
return &port, nil
296+
return port, nil
307297
}
308298

309299
func imageToName(image string) string {

client/help_darwin.go

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ Command:
2222
login Register or log in to a Docker registry server
2323
logout Log out from a Docker registry server
2424
pause Pause a running pod
25+
ports Show or modify port mapping rules
2526
pull Pull an image from a Docker registry server
2627
push Push an image or a repository to a Docker registry server
2728
rm Remove one or more pods or containers

client/help_linux.go

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ Command:
2222
login Register or log in to a Docker registry server
2323
logout Log out from a Docker registry server
2424
pause Pause a running pod
25+
ports Show or modify port mapping rules
2526
pull Pull an image from a Docker registry server
2627
push Push an image or a repository to a Docker registry server
2728
rm Remove one or more pods or containers

client/portmapping.go

+83
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
package client
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"strings"
7+
"text/tabwriter"
8+
9+
"github.com/hyperhq/hyperd/types"
10+
gflag "github.com/jessevdk/go-flags"
11+
)
12+
13+
func (cli *HyperClient) HyperCmdPorts(args ...string) error {
14+
var opts struct {
15+
Portmap []string `short:"p" long:"publish" value-name:"[]" default-mask:"-" description:"Publish a container's port to the host, format: -p|--publish [tcp/udp:]hostPort:containerPort (only valid for add and delete)"`
16+
}
17+
var parser = gflag.NewParser(&opts, gflag.Default|gflag.IgnoreUnknown|gflag.PassAfterNonOption)
18+
parser.Usage = "ports ls|add|delete [OPTIONS] POD\n\nList or modify port mapping rules of a Pod\n"
19+
20+
if len(args) == 0 {
21+
parser.WriteHelp(cli.err)
22+
return nil
23+
}
24+
cmd := args[0]
25+
26+
args, err := parser.ParseArgs(args[1:])
27+
if err != nil {
28+
if !strings.Contains(err.Error(), "Usage") {
29+
return err
30+
} else {
31+
return nil
32+
}
33+
}
34+
35+
var modFunc func(string, []*types.PortMapping) error
36+
37+
switch cmd {
38+
case "ls":
39+
if len(args) != 1 {
40+
return errors.New("need a Pod Id as command parameter")
41+
}
42+
pms, err := cli.client.ListPortMappings(args[0])
43+
if err != nil {
44+
return err
45+
}
46+
w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)
47+
fmt.Fprintln(w, "Protocol\tHost Ports\tContainer Ports")
48+
for _, pm := range pms {
49+
fmt.Fprintf(w, "%s\t%s\t%s\n", pm.Protocol, pm.HostPort, pm.ContainerPort)
50+
}
51+
w.Flush()
52+
return nil
53+
case "add":
54+
modFunc = cli.client.AddPortMappings
55+
case "delete":
56+
modFunc = cli.client.DeletePortMappings
57+
default:
58+
parser.WriteHelp(cli.err)
59+
return nil
60+
}
61+
62+
if len(args) != 1 {
63+
return errors.New("need a Pod Id as command parameter")
64+
}
65+
if len(opts.Portmap) == 0 {
66+
return errors.New("no rules to be add or delete")
67+
}
68+
69+
pms := make([]*types.PortMapping, 0, len(opts.Portmap))
70+
for _, o := range opts.Portmap {
71+
pm, err := parsePortMapping(o)
72+
if err != nil {
73+
return fmt.Errorf("failed to parse rule %s: %v", o, err)
74+
}
75+
pms = append(pms, pm)
76+
}
77+
78+
err = modFunc(args[0], pms)
79+
if err != nil {
80+
return err
81+
}
82+
return nil
83+
}

daemon/daemon.go

+11-1
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,12 @@ import (
1818
"github.com/docker/docker/registry"
1919
dockerutils "github.com/docker/docker/utils"
2020
"github.com/golang/glog"
21+
"github.com/hyperhq/hyperd/networking/portmapping"
2122
"github.com/hyperhq/hyperd/utils"
2223
"github.com/hyperhq/runv/driverloader"
2324
"github.com/hyperhq/runv/factory"
2425
"github.com/hyperhq/runv/hypervisor"
26+
"github.com/hyperhq/runv/hypervisor/network"
2527
)
2628

2729
var (
@@ -224,10 +226,18 @@ func (daemon *Daemon) initRunV(c *apitypes.HyperConfig) error {
224226
}
225227

226228
func (daemon *Daemon) initNetworks(c *apitypes.HyperConfig) error {
227-
if err := hypervisor.InitNetwork(c.Bridge, c.BridgeIP, c.DisableIptables); err != nil {
229+
if err := hypervisor.InitNetwork(c.Bridge, c.BridgeIP, true); err != nil {
228230
glog.Errorf("InitNetwork failed, %s", err.Error())
229231
return err
230232
}
233+
addr, err := network.GetIfaceAddr(network.BridgeIface)
234+
if err != nil {
235+
glog.Errorf("failed to get address of the configured bridge: %v", err)
236+
return err
237+
}
238+
if err := portmapping.Setup(network.BridgeIface, addr, c.DisableIptables); err != nil {
239+
glog.Errorf("Setup portmapping failed: %v", err)
240+
}
231241
return nil
232242
}
233243

daemon/pod/decommission.go

+8
Original file line numberDiff line numberDiff line change
@@ -549,11 +549,13 @@ func (p *XPod) cleanup() {
549549
if err != nil {
550550
// even if error, we set the vm to be stopped
551551
p.Log(ERROR, "pod stopping failed, failed to decommit the resources: %v", err)
552+
err = nil
552553
}
553554

554555
err = p.removeSandboxFromDB()
555556
if err != nil {
556557
p.Log(ERROR, "pod stopping failed, failed to remove sandbox persist data: %v", err)
558+
err = nil
557559
}
558560

559561
p.Log(DEBUG, "tag pod as stopped")
@@ -573,6 +575,12 @@ func (p *XPod) cleanup() {
573575
func (p *XPod) decommissionResources() (err error) {
574576
p.Log(DEBUG, "umount all containers and volumes, release IP addresses")
575577

578+
err = p.flushPortMapping()
579+
if err != nil {
580+
p.Log(WARNING, "(ignored) flush port mappings failed: %v", err)
581+
err = nil
582+
}
583+
576584
for _, c := range p.containers {
577585
ec := c.umountRootVol()
578586
if ec != nil {

0 commit comments

Comments
 (0)