conlink 2.2.0 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -11,7 +11,7 @@ jobs:
11
11
  runs-on: ubuntu-latest
12
12
  steps:
13
13
  - name: Checkout
14
- uses: actions/checkout@v3
14
+ uses: actions/checkout@v4
15
15
 
16
16
  - name: npm install
17
17
  run: npm install
@@ -19,6 +19,6 @@ jobs:
19
19
  - name: compose build of conlink
20
20
  run: docker compose -f examples/test1-compose.yaml build
21
21
 
22
- - name: "./run-tests.sh"
22
+ - name: "dctest test/test*.yaml"
23
23
  timeout-minutes: 5
24
- run: time ./run-tests.sh
24
+ run: time node_modules/.bin/dctest --verbose-commands conlink-test $(ls -v test/test*.yaml)
package/Dockerfile CHANGED
@@ -24,7 +24,7 @@ FROM node:16-slim as run
24
24
  RUN apt-get -y update
25
25
  # Runtime deps and utilities
26
26
  RUN apt-get -y install libpcap-dev tcpdump iproute2 iputils-ping curl \
27
- iptables bridge-utils ethtool \
27
+ iptables bridge-utils ethtool jq \
28
28
  openvswitch-switch openvswitch-testcontroller
29
29
 
30
30
  COPY --from=build /app/ /app/
package/README.md CHANGED
@@ -1,9 +1,46 @@
1
1
  # conlink: Declarative Low-Level Networking for Containers
2
2
 
3
-
4
3
  Create (layer 2 and layer 3) networking between containers using
5
4
  a declarative configuration.
6
5
 
6
+ Conlink also includes scripts that make docker compose a much more
7
+ powerful development and testing environment (refer to
8
+ [Compose scripts](#compose-scripts-mdc-waitsh-and-copysh) for
9
+ details):
10
+
11
+ * [mdc](#mdc): modular management of multiple compose configurations
12
+ * [wait.sh](#waitsh): wait for network and file conditions before continuing
13
+ * [copy.sh](#copysh): recursively copy files with variable templating
14
+
15
+ ## Why conlink?
16
+
17
+ There are a number of limitations of docker-compose networking that
18
+ conlink addresses:
19
+
20
+ * Operates at layer 3 of the network stack (i.e. IP).
21
+ * Has a fixed model of container interface naming: first interface is
22
+ `eth0`, second is `eth1`, etc.
23
+ * For containers with multiple interfaces, the mapping between docker
24
+ compose networks and container interface is not controllable
25
+ (https://github.com/docker/compose/issues/4645#issuecomment-701351876,
26
+ https://github.com/docker/compose/issues/8561#issuecomment-1872510968)
27
+ * If a container uses the scale property, then IPs cannot be
28
+ assigned and user assigned MAC addresses will be the same for every
29
+ instance of that service.
30
+ * Docker bridge networking interferes with switch and bridge protocol
31
+ traffic (BPDU, STP, LLDP, etc). Conlink supports the "patch" link
32
+ mode that allows this type of traffic to pass correctly.
33
+
34
+ Conlink has the following features:
35
+
36
+ - Declarative network configuration (links, bridges, patches, etc)
37
+ - Event driven (container restarts and scale changes)
38
+ - Low-level control of network interfaces/links: MTU, routes, port
39
+ forwarding, netem properties, etc
40
+ - Automatic IP and MAC address incrementing for scaled containers
41
+ - Central network container for easy monitoring and debug
42
+ - Composable configuration from multiple sources/locations
43
+
7
44
  ## Prerequisites
8
45
 
9
46
  General:
@@ -74,12 +111,12 @@ same bridge (broadcast domain).
74
111
  Network configuration can either be loaded directly from configuration
75
112
  files using the `--network-config` option or it can be loaded from
76
113
  `x-network` properties contained in docker-compose files using the
77
- `--compose-file`. Multiple of each option may be specified and all the
78
- network configuration will be merged into a final network
79
- configuration.
114
+ `--compose-file` option. Multiple of each option may be specified and
115
+ all the network configuration will be merged into a final network
116
+ configuration. Both options also support colon separated lists.
80
117
 
81
- The network configuration can have three top level keys: `links`,
82
- `tunnels`, and `commands`.
118
+ The network configuration can have four top level keys: `links`,
119
+ `bridges`, `tunnels`, and `commands`.
83
120
 
84
121
  ### Links
85
122
 
@@ -103,12 +140,12 @@ The following table describes the link properties:
103
140
  | ip | * | CIDR | | IP CIDR 7 |
104
141
  | mac | 3 | MAC | | MAC addr 7 |
105
142
  | mtu | * | number 4 | 65535 | intf MTU |
106
- | route | * | string | | ip route add args |
143
+ | route | * | strings 8 | | ip route add args |
107
144
  | nat | * | IP | | DNAT/SNAT to IP |
108
- | netem | * | string | | tc qdisc NetEm options |
145
+ | netem | * | strings 8 | | tc qdisc NetEm options |
109
146
  | mode | 5 | string | | virt intf mode |
110
147
  | vlanid | vlan | number | | VLAN ID |
111
- | forward | veth | string array 6 | | forward conlink ports 7 |
148
+ | forward | veth | strings 6 8 | | forward conlink ports 7 |
112
149
 
113
150
  - 1 - veth, dummy, vlan, ipvlan, macvlan, ipvtap, macvtap
114
151
  - 2 - defaults to outer compose service
@@ -117,6 +154,7 @@ The following table describes the link properties:
117
154
  - 5 - macvlan, macvtap, ipvlan, ipvtap
118
155
  - 6 - string syntax: `conlink_port:container_port/proto`
119
156
  - 7 - offset by scale/replica index
157
+ - 8 - either a single string or an array of strings
120
158
 
121
159
  Each link has a 'type' key that defaults to "veth" and each link
122
160
  definition must also have either a `service` key or a `container` key.
@@ -531,13 +569,15 @@ validate connectivity using ping:
531
569
  ```
532
570
  export BRIDGE_MODE="linux" # "ovs", "patch", "auto"
533
571
  docker-compose -f examples/test9-compose.yaml up --build --force-recreate
534
- docker-compose -f examples/test9-compose.yaml exec node ping 10.0.1.2
572
+ docker-compose -f examples/test9-compose.yaml exec node1 ping 10.0.1.2
535
573
  ```
536
574
 
537
- ### test10: port forwarding
575
+ ### test10: port forwarding and routing
538
576
 
539
577
  This example demonstrates port forwarding from the conlink container
540
- to two containers running simple web servers.
578
+ to two containers running simple web servers. It also demonstrates the
579
+ use of a router container and multiple route rules in the other
580
+ containers.
541
581
 
542
582
  Start the test10 compose configuration:
543
583
 
@@ -581,6 +621,154 @@ curl 0.0.0.0:80
581
621
  curl 0.0.0.0:81
582
622
  ```
583
623
 
624
+ Start a two tcpdump processes in the conlink container to watch
625
+ routed ICMP traffic and then ping between containers across the router
626
+ container:
627
+
628
+ ```
629
+ docker compose -f examples/test10-compose.yaml exec network tcpdump -nli router_1-es1 icmp
630
+ docker compose -f examples/test10-compose.yaml exec network tcpdump -nli router_1-es2 icmp
631
+ ```
632
+
633
+ ```
634
+ docker-compose -f examples/test10-compose.yaml exec node1 ping 10.2.0.1
635
+ docker-compose -f examples/test10-compose.yaml exec node1 ping 10.2.0.2
636
+ docker-compose -f examples/test10-compose.yaml exec node2 ping 10.1.0.1
637
+
638
+ ```
639
+
640
+ ## Compose scripts: mdc, wait.sh, and copy.sh
641
+
642
+ ### mdc
643
+
644
+ The `mdc` command adds flexibility and power to the builtin overlay
645
+ capability of docker compose. Docker compose can specify multiple
646
+ compose files that will be combined into a single configuration.
647
+ Compose files that are specified later will overlay or override
648
+ earlier compose files. For example, if compose files A and B are
649
+ loaded by docker compose, then the `image` property of a service in
650
+ file B will take precedence (or override) the `image` property for the
651
+ same service in file A. Some properties such as `volumes` and
652
+ `environment` will have the sub-properties merged or appended to.
653
+
654
+ There are several ways that `mdc` adds to the composition capabilities
655
+ of docker compose:
656
+ 1. **Mode/module dependency resolution**. The modes or modules that
657
+ are combined by `mdc` are defined as directories that contain
658
+ mode/module specific content. A `deps` file in a mode/module
659
+ directory is used to specify dependencies on other modes/modules.
660
+ The syntax and resolution algorithm is defined by the
661
+ [resolve-deps](https://github.com/Viasat/resolve-deps) project.
662
+ 2. **Environment variable file combining/overlaying**. Each `.env`
663
+ file that appears in a mode/module directory will be appended into
664
+ a single `.env` file at the top-level where the `mdc` command is
665
+ invoked. Later environment variables will override earlier ones
666
+ with the same name. Variable interpolation and some shell-style
667
+ variable expansion can be used to combine/append environment
668
+ variables. For example if FOO and BAR are defined in an earlier
669
+ mode/module, then BAZ could be defined like this:
670
+ `BAZ="${FOO:-${BAR}-SUFF"` which will set BAZ to FOO if FOO is set,
671
+ otherwise, it will set BAZ to BAR with a "-SUFF" suffix.
672
+ 3. **Directory hierarchy combining/overlaying**. If the mode/module
673
+ directory has subdirectories that themselves contain a "files/"
674
+ sub-directory, then the mode subdirectories will be recursively
675
+ copied into the top-level ".files/" directory. For example,
676
+ consider if the following files exists under the modes "foo" and
677
+ "bar" (with a dependency of "bar" on "foo"):
678
+ `foo/svc1/files/etc/conf1`, `foo/svc2/files/etc/conf2`, and
679
+ `bar/svc1/files/etc/conf1`. When `mdc` is run this will result in
680
+ the following two files: `.files/svc1/etc/conf1` and
681
+ `.files/svc2/etc/conf2`. The content of `conf1` will come from the
682
+ "bar" mode because it is resolved second. The use of the `copy.sh`
683
+ script (described below) simplifies recursive file copying and also
684
+ provides variable templating of copied files.
685
+ 4. **Set environment variables based on the selected modes/modules**.
686
+ When `mdc` is run it will set the following special environment
687
+ variables in the top-level `.env` file:
688
+ * `COMPOSE_FILE`: A colon separated and dependency ordered list of
689
+ compose file paths from each resolved mode/module directory.
690
+ * `COMPOSE_DIR`: The directory where the top-level `.env` is
691
+ created.
692
+ * `COMPOSE_PRPOFILES`: A comma separated list of each resolved
693
+ mode/module with a `MODE_` prefix on the name. These are docker
694
+ compose profiles that can be used to enable services in one
695
+ mode/module compose file when a different mode/module is
696
+ selected/resolved by `mdc`. For example, if a compose file in
697
+ "bar" has a service that should only be enabled when the "foo"
698
+ mode/module is also requested/resolved, then the service can be
699
+ tagged with the `MODE_foo` profile.
700
+ * `MDC_MODE_DIRS`: A comma separated list of mode/module
701
+ directories. This can be used by other external tools that have
702
+ specific mode/module behavior.
703
+
704
+ Conlink network configuration can be specified in `x-network`
705
+ properties within compose files. This can be a problem with the
706
+ builtin overlay functionality of docker compose because `x-` prefixed
707
+ properties are simply overriden as a whole without any special merging
708
+ behavior. To work around this limitation, conlink has the ability to
709
+ directly merge `x-network` configuration from multiple compose files
710
+ by passing the `COMPOSE_FILE` variable to the conlink `--compose-file`
711
+ parameter (which supports a colon sperated list of compose files).
712
+
713
+ ### wait.sh
714
+
715
+ The dynamic event driven nature of conlink mean that interfaces may
716
+ appear after the container service code starts running (unlike plain
717
+ docker container networking). For this reason, the `wait.sh` script is
718
+ provided to simplify waiting for interfaces to appear (and other
719
+ network conditions). Here is a compose file snippit that will wait for
720
+ `eth0` to appear and for `eni1` to both appear and have an IP address
721
+ assigned before running the startup command (after the `--`):
722
+
723
+ ```
724
+ services:
725
+ svc1:
726
+ volumes:
727
+ - ./conlink/scripts:/scripts:ro
728
+ command: /scripts/wait.sh -i eth0 -I eni1 -- /start-cmd.sh arg1 arg2
729
+ ```
730
+
731
+ In addition to waiting for interfaces and address assignment,
732
+ `wait.sh` can also wait for a file to appear (`-f FILE`), a remote TCP
733
+ port to become accessible (`-t HOST:PORT`), or run a command until it
734
+ completes successfully (`-c COMMAND`).
735
+
736
+
737
+ ### copy.sh
738
+
739
+ One of the features of the `mdc` command is to collect directory
740
+ hierarchies from mode/module directories into a single `.files/`
741
+ directory at the top-level. The intended use of the merged directory
742
+ hierarchy is to be merged into file-systems of running containers.
743
+ However, simple volume mounts will replace entire directory
744
+ hierarchies (and hide all prior files under the mount point). The
745
+ `copy.sh` script is provided for easily merging/overlaying one
746
+ directory hierarchy onto another one. In addition, the `-T` option
747
+ will also replace special `{{VAR}}` tokens in the files being copied
748
+ with the value of the matching environment variable.
749
+
750
+ Here is a compose file snippit that shows the use of `copy.sh` to
751
+ recursively copy/overlay the directory tree in `./.files/svc2` onto
752
+ the container root file-system. In addition, due to the use of the
753
+ `-T` option, the script will replace any occurence of the string
754
+ `{{FOO}}` with the value of the `FOO` environment variable within any
755
+ of the files that are copied:
756
+
757
+ ```
758
+ services:
759
+ svc2:
760
+ environment:
761
+ - FOO=123
762
+ volumes:
763
+ - ./.files/svc2:/files:ro
764
+ command: /scripts/copy.sh -T /files / -- /start-cmd.sh arg1 arg2
765
+ ```
766
+
767
+ Note that instances of `copy.sh` and `wait.sh` can be easily chained
768
+ together like this:
769
+ ```
770
+ /scripts/copy.sh -T /files / -- /scripts/wait.sh -i eth0 -- cmd args
771
+ ```
584
772
 
585
773
  ## GraphViz network configuration rendering
586
774
 
@@ -7,7 +7,8 @@ services:
7
7
  command: "python3 -m http.server -d /var 80"
8
8
  x-network:
9
9
  links:
10
- - {bridge: s2, ip: "10.0.1.1/24", route: "default",
10
+ - {bridge: s1, ip: "10.1.0.1/24",
11
+ route: ["10.0.0.0/8 via 10.1.0.100"],
11
12
  forward: ["1080:80/tcp", "1180:80/tcp"]}
12
13
 
13
14
  node2:
@@ -17,8 +18,18 @@ services:
17
18
  command: "python3 -m http.server -d /usr 80"
18
19
  x-network:
19
20
  links:
20
- - {bridge: s1, ip: "10.0.2.1/24", route: "default",
21
- forward: ["80:80/tcp"]}
21
+ - {bridge: s2, ip: "10.2.0.1/24",
22
+ route: "10.0.0.0/8 via 10.2.0.100",
23
+ forward: "80:80/tcp"}
24
+
25
+ router:
26
+ image: python:3-alpine
27
+ network_mode: none
28
+ command: sleep Infinity
29
+ x-network:
30
+ links:
31
+ - {bridge: s1, ip: "10.1.0.100/24", dev: es1}
32
+ - {bridge: s2, ip: "10.2.0.100/24", dev: es2}
22
33
 
23
34
  network:
24
35
  build: {context: ../}
@@ -28,7 +28,12 @@ services:
28
28
  ip: 10.0.1.1/16
29
29
  mac: 00:0a:0b:0c:0d:01
30
30
  mtu: 4111
31
- netem: "delay 40ms rate 10mbit"
31
+ netem: "rate 10mbit delay 40ms"
32
32
  - bridge: s2
33
33
  ip: 100.0.1.1/16
34
34
  dev: eth1
35
+
36
+ x-network:
37
+ links:
38
+ # The delay setting is overridden by the one in the service
39
+ - {service: node, bridge: s1, netem: "delay 200ms"}
@@ -19,14 +19,19 @@ services:
19
19
  - BRIDGE_MODE
20
20
  command: /app/build/conlink.js --default-bridge-mode linux --compose-file /test/test9-compose.yaml
21
21
 
22
- node:
22
+ node1:
23
+ image: alpine
24
+ network_mode: none
25
+ command: sleep Infinity
26
+
27
+ node2:
23
28
  image: alpine
24
29
  network_mode: none
25
- scale: 2
26
30
  command: sleep Infinity
27
31
 
28
32
  x-network:
29
33
  links:
30
- - {bridge: s1, service: node, ip: 10.0.1.1/24}
34
+ - {bridge: s1, service: node1, ip: 10.0.1.1/24}
35
+ - {bridge: s1, service: node2, ip: 10.0.1.2/24}
31
36
  bridges:
32
37
  - {bridge: s1, mode: "${BRIDGE_MODE:-auto}"}
package/link-add.sh CHANGED
@@ -32,9 +32,8 @@ usage () {
32
32
  echo >&2 " --mac MAC0 - MAC address for INTF0"
33
33
  echo >&2 " --mac0 MAC0 - MAC address for INTF0"
34
34
  echo >&2 " --mac1 MAC1 - MAC address for INTF1"
35
- echo >&2 " --route 'ROUTE' - route to add to INTF0"
36
- echo >&2 " --route|--route0 'ROUTE' - route to add to INTF0"
37
- echo >&2 " --route1 'ROUTE' - route to add to INTF1"
35
+ echo >&2 " --route|--route0 'ROUTE' - route to add to INTF0 (can repeat)"
36
+ echo >&2 " --route1 'ROUTE' - route to add to INTF1 (can repeat)"
38
37
  echo >&2 " --mtu MTU - MTU for both interfaces"
39
38
  echo >&2 ""
40
39
  echo >&2 " --mode MODE - Mode settings for *vlan TYPEs"
@@ -43,7 +42,7 @@ usage () {
43
42
  echo >&2 " --remote REMOTE - Remote address for geneve/vxlan types"
44
43
  echo >&2 " --vni VNI - Virtual Network Identifier for geneve/vxlan types"
45
44
  echo >&2 ""
46
- echo >&2 " --netem NETEM - tc qdisc netem OPTIONS (man 8 netem)"
45
+ echo >&2 " --netem NETEM - tc qdisc netem OPTIONS (man 8 netem) (can repeat)"
47
46
  echo >&2 " --nat TARGET - Stateless NAT traffic to/from TARGET"
48
47
  echo >&2 " (in primary/PID0 netns)"
49
48
  echo >&2 ""
@@ -54,17 +53,21 @@ info() { echo "link-add [${LOG_ID}] ${*}"; }
54
53
  warn() { >&2 echo "link-add [${LOG_ID}] ${*}"; }
55
54
  die() { warn "ERROR: ${*}"; exit 1; }
56
55
 
57
- # Set MAC, IP, ROUTE, MTU, and up state for interface in netns
56
+ # Set MAC, IP, ROUTES, MTU, and up state for interface in netns
58
57
  setup_if() {
59
- local IF=$1 NS=$2 MAC=$3 IP=$4 ROUTE=$5 MTU=$6
58
+ local IF=$1 NS=$2 MAC=$3 IP=$4 MTU=$5 ROUTES=$6 routes=
59
+ echo >&2 "ROUTES: ${ROUTES}"
60
+ while read rt; do
61
+ [ "${rt}" ] && routes="${routes}\nroute add ${rt} dev ${IF}"
62
+ done < <(echo -e "${ROUTES}")
60
63
 
61
- info "Setting ${IP:+IP ${IP}, }${MAC:+MAC ${MAC}, }${MTU:+MTU ${MTU}, }${ROUTE:+ROUTE '${ROUTE}', }up state"
64
+ info "Setting ${IP:+IP ${IP}, }${MAC:+MAC ${MAC}, }${MTU:+MTU ${MTU}, }${ROUTES:+ROUTES '${ROUTES//$'\n'/,}', }up state"
62
65
  ip -netns ${NS} --force -b - <<EOF
63
66
  ${IP:+addr add ${IP} dev ${IF}}
64
67
  ${MAC:+link set dev ${IF} address ${MAC}}
65
68
  ${MTU:+link set dev ${IF} mtu ${MTU}}
66
69
  link set dev ${IF} up
67
- ${ROUTE:+route add ${ROUTE} dev ${IF}}
70
+ $(echo -e "${routes}")
68
71
  EOF
69
72
  }
70
73
 
@@ -78,7 +81,7 @@ IPTABLES() {
78
81
  # Parse arguments
79
82
  VERBOSE=${VERBOSE:-}
80
83
  PID1=${PID1:-<SELF>} IF1=${IF1:-eth0}
81
- IP0= IP1= MAC0= MAC1= ROUTE0= ROUTE1= MTU=
84
+ IP0= IP1= MAC0= MAC1= ROUTES0= ROUTES1= MTU=
82
85
  MODE= VLANID= REMOTE= VNI= NETEM= NAT=
83
86
  positional=
84
87
  while [ "${*}" ]; do
@@ -91,8 +94,8 @@ while [ "${*}" ]; do
91
94
  --ip1) IP1="${OPTARG}"; shift ;;
92
95
  --mac|--mac0) MAC0="${OPTARG}"; shift ;;
93
96
  --mac1) MAC1="${OPTARG}"; shift ;;
94
- --route|--route0) ROUTE0="${OPTARG}"; shift ;;
95
- --route1) ROUTE1="${OPTARG}"; shift ;;
97
+ --route|--route0) ROUTES0="${ROUTES0}\n${OPTARG}"; shift ;;
98
+ --route1) ROUTES1="${ROUTES1}\n${OPTARG}"; shift ;;
96
99
  --mtu) MTU="${OPTARG}"; shift ;;
97
100
 
98
101
  --mode) MODE="${OPTARG}"; shift ;;
@@ -101,13 +104,15 @@ while [ "${*}" ]; do
101
104
  --remote) REMOTE="${OPTARG}"; shift ;;
102
105
  --vni) VNI="${OPTARG}"; shift ;;
103
106
 
104
- --netem) NETEM="${OPTARG}"; shift ;;
107
+ --netem) NETEM="${NETEM} ${OPTARG}"; shift ;;
105
108
  --nat) NAT="${OPTARG}"; shift ;;
106
109
  -h|--help) usage ;;
107
110
  *) positional="${positional} $1" ;;
108
111
  esac
109
112
  shift
110
113
  done
114
+ ROUTES0="${ROUTES0#\\n}"
115
+ ROUTES1="${ROUTES1#\\n}"
111
116
  set -- ${positional}
112
117
  TYPE=$1 PID0=$2 IF0=$3
113
118
 
@@ -181,9 +186,9 @@ geneve|vxlan)
181
186
  ;;
182
187
  esac
183
188
 
184
- setup_if ${IF0} ${NS0} "${MAC0}" "${IP0}" "${ROUTE0}" "${MTU}"
189
+ setup_if ${IF0} ${NS0} "${MAC0}" "${IP0}" "${MTU}" "${ROUTES0}"
185
190
  [ "${TYPE}" = "veth" ] && \
186
- setup_if ${IF1} ${NS1} "${MAC1}" "${IP1}" "${ROUTE1}" "${MTU}"
191
+ setup_if ${IF1} ${NS1} "${MAC1}" "${IP1}" "${MTU}" "${ROUTES1}"
187
192
 
188
193
  if [ "${NETEM}" ]; then
189
194
  info "Setting tc qdisc netem: ${NETEM}"
package/link-forward.sh CHANGED
@@ -37,7 +37,8 @@ LOG_ID="${spec}"
37
37
  info "${action^} forwarding ${intf_a} -> ${intf_b}"
38
38
 
39
39
  IPTABLES PREROUTING -t nat -i ${intf_a} -p ${proto} --dport ${port_a} -j DNAT --to-destination ${ip}:${port_b}
40
- IPTABLES POSTROUTING -t nat -o ${intf_b} -j MASQUERADE
40
+ IPTABLES PREROUTING -t nat -i ${intf_a} -p ${proto} --dport ${port_a} -j MARK --set-mark 1
41
+ IPTABLES POSTROUTING -t nat -o ${intf_b} -m mark --mark 1 -j MASQUERADE
41
42
 
42
43
  case "${action}" in
43
44
  add) ip route replace ${ip} dev ${intf_b} ;;
package/link-mirred.sh CHANGED
@@ -34,41 +34,20 @@ die() { warn "ERROR: ${*}"; exit 1; }
34
34
  add_ingress() {
35
35
  local IF=$1 res=
36
36
 
37
- res=$(tc qdisc show dev ${IF} 2>&1)
38
- case "${res}" in
39
- *"qdisc ingress ffff:"*)
40
- info "${IF0} already has ingress qdisc"
41
- ;;
42
- ""|*"qdisc noqueue"*)
43
- info "Adding ingress qdisc to ${IF}"
44
- tc qdisc add dev "${IF}" ingress \
45
- || die "Could not add ingress qdisc to ${IF}"
46
- ;;
47
- *)
48
- die "${IF} has invalid ingress qdisc or could not be queried"
49
- ;;
50
- esac
37
+ info "Adding ingress qdisc to ${IF}"
38
+ tc qdisc replace dev "${IF}" ingress \
39
+ || die "Could not add ingress qdisc to ${IF}"
51
40
  }
52
41
 
53
42
  # Idempotently add mirred filter redirect rule to an interface
54
43
  add_mirred() {
55
44
  local IF0=$1 IF1=$2 res=
56
45
 
57
- res=$(tc filter show dev ${IF0} parent ffff: 2>&1)
58
- case "${res}" in
59
- *"action order 1: mirred (Egress Redirect to device ${IF1}"*)
60
- info "${IF0} already has filter redirect action"
61
- ;;
62
- "")
63
- info "Adding filter redirect action from ${IF0} to ${IF1}"
64
- tc filter add dev ${IF0} parent ffff: protocol all u32 match u8 0 0 action \
65
- mirred egress redirect dev ${IF1} \
66
- || die "Could not add filter redirect action from ${IF0} to ${IF1}"
67
- ;;
68
- *)
69
- die "${IF0} has invalid filter redirect action or could not be queried"
70
- ;;
71
- esac
46
+ info "Adding filter redirect action from ${IF0} to ${IF1}"
47
+ tc filter del dev ${IF0} root
48
+ tc filter replace dev ${IF0} parent ffff: protocol all u32 match u8 0 0 action \
49
+ mirred egress redirect dev ${IF1} \
50
+ || die "Could not add filter redirect action from ${IF0} to ${IF1}"
72
51
  }
73
52
 
74
53
  # Parse arguments
package/mdc CHANGED
@@ -17,21 +17,18 @@ LS=$(which ls)
17
17
  RESOLVE_DEPS="${RESOLVE_DEPS-./node_modules/@lonocloud/resolve-deps/resolve-deps.py}"
18
18
  DOCKER_COMPOSE="${DOCKER_COMPOSE:-docker-compose}"
19
19
 
20
- [ -f "${RESOLVE_DEPS}" ] || die "Missing ${RESOLVE_DEPS}. Perhaps 'npm install'?"
20
+ which ${RESOLVE_DEPS} >/dev/null 2>/dev/null \
21
+ || die "Missing ${RESOLVE_DEPS}. Perhaps 'npm install'?"
22
+
23
+ # Resolve mode directory paths
24
+
21
25
  MODE_SPEC="${1}"; shift
22
- if [ "${RESOLVE_DEPS}" ]; then
23
- MODES="$(${RESOLVE_DEPS} "${MODES_DIR}" ${MODE_SPEC})"
24
- else
25
- MODES="${MODE_SPEC//,/ }"
26
- fi
26
+ RESOLVED_MODES="$(${RESOLVE_DEPS} --path "${MODES_DIR}" --format=paths ${MODE_SPEC})"
27
27
 
28
- echo >&2 "MODES: ${MODES}"
29
- vecho "ENV_FILE: ${ENV_FILE}"
28
+ # Create base (empty) compose file to anchor mode relative paths
29
+ # to the same root directory. Create files dir.
30
30
 
31
- declare -A FINISHED
32
31
  COMPOSE_FILE=./.compose-empty.yaml
33
- COMPOSE_PROFILES=
34
- MDC_MODE_DIRS=
35
32
  cat /dev/null > ${ENV_FILE}-mdc-tmp
36
33
  echo -e "version: '2.4'\nservices: {}" > ./.compose-empty.yaml
37
34
 
@@ -42,19 +39,25 @@ esac
42
39
  [ -d "${MDC_FILES_DIR}" ] && rm -r${VERBOSE:+v} ${MDC_FILES_DIR}/
43
40
  mkdir -p "${MDC_FILES_DIR}"
44
41
 
45
- for mode in ${MODES}; do
42
+ # Incorporate modes' compose config, env, and files
43
+
44
+ declare -A FINISHED
45
+ COMPOSE_PROFILES=
46
+ MDC_MODE_NAMES=
47
+ MDC_MODE_DIRS=
48
+ for resolved in ${RESOLVED_MODES}; do
49
+ mode=${resolved%=*}
50
+ path=${resolved#*=}
51
+
46
52
  # Only process each mode once
47
53
  [ "${FINISHED[${mode}]}" ] && continue
48
54
  FINISHED["${mode}"]=1
49
55
 
50
- # mode dir must exist
51
- [ -d "${MODES_DIR}/${mode}" ] || \
52
- die "No mode dir found for ${mode}"
53
-
54
- MDC_MODE_DIRS="${MDC_MODE_DIRS},${MODES_DIR}/${mode}"
56
+ MDC_MODE_NAMES="${MDC_MODE_NAMES} ${mode}"
57
+ MDC_MODE_DIRS="${MDC_MODE_DIRS},${path}"
55
58
 
56
59
  # mode can refer to a compose file in multiple ways
57
- cfiles="${MODES_DIR}/${mode}/compose.yaml ${MODES_DIR}/${mode}/compose.yml ${MODES_DIR}/${mode}/docker-compose.yaml ${MODES_DIR}/${mode}/docker-compose.yml"
60
+ cfiles="${path}/compose.yaml ${path}/compose.yml ${path}/docker-compose.yaml ${path}/docker-compose.yml"
58
61
  for cfile in ${cfiles}; do
59
62
  if [ -e "${cfile}" ]; then
60
63
  COMPOSE_FILE="${COMPOSE_FILE}:${cfile}"
@@ -66,27 +69,35 @@ for mode in ${MODES}; do
66
69
  COMPOSE_PROFILES="${COMPOSE_PROFILES},MODE_${mode}"
67
70
 
68
71
  # if there is a mode specific env file then include it
69
- efiles="${MODES_DIR}/${mode}/env ${MODES_DIR}/${mode}/.env"
72
+ efiles="${path}/env ${path}/.env"
70
73
  for efile in ${efiles}; do
71
74
  if [ -e "${efile}" ]; then
72
- echo "### mdc begin mode ${mode}" >> ${ENV_FILE}-mdc-tmp
75
+ echo "### mdc begin mode ${mode} (${efile})" >> ${ENV_FILE}-mdc-tmp
73
76
  vecho "cat ${efile} >> ${ENV_FILE}-mdc-tmp"
74
77
  cat ${efile} >> ${ENV_FILE}-mdc-tmp
75
- echo "### mdc end mode ${mode}" >> ${ENV_FILE}-mdc-tmp
78
+ echo >> ${ENV_FILE}-mdc-tmp
79
+ echo "### mdc end mode ${mode} (${efile})" >> ${ENV_FILE}-mdc-tmp
76
80
  fi
77
81
  done
78
82
 
79
83
  # if there are mode specific files then copy them to MDC_FILES_DIR
80
- if [ -d "${MODES_DIR}/${mode}" ]; then
81
- for vfd in $(cd ${MODES_DIR}/${mode} && $LS -d */files 2>/dev/null || true); do
84
+ if [ -d "${path}" ]; then
85
+ for vfd in $(cd ${path} && $LS -d */files 2>/dev/null || true); do
82
86
  dest=${MDC_FILES_DIR}/${vfd%/files}
83
87
  mkdir -p ${dest}
84
- vecho cp -a ${MODES_DIR}/${mode}/${vfd}/* ${dest}
85
- cp -a ${MODES_DIR}/${mode}/${vfd}/* ${dest}
88
+ vecho cp -a ${path}/${vfd}/* ${dest}
89
+ cp -a ${path}/${vfd}/* ${dest}
86
90
  done
87
91
  fi
88
92
  done
89
93
 
94
+ vecho
95
+
96
+ # Summarize and set env
97
+
98
+ echo >&2 "MODES: ${MDC_MODE_NAMES}"
99
+ vecho "ENV_FILE: ${ENV_FILE}"
100
+
90
101
  COMPOSE_FILE="${COMPOSE_FILE#:}"
91
102
  vecho "COMPOSE_FILE: ${COMPOSE_FILE}"
92
103
  echo "COMPOSE_FILE=${COMPOSE_FILE}" >> ${ENV_FILE}-mdc-tmp
@@ -100,6 +111,8 @@ MDC_MODE_DIRS="${MDC_MODE_DIRS#,}"
100
111
  vecho "MDC_MODE_DIRS: ${MDC_MODE_DIRS}"
101
112
  echo "MDC_MODE_DIRS=\"${MDC_MODE_DIRS}\"" >> ${ENV_FILE}-mdc-tmp
102
113
 
114
+ vecho
115
+
103
116
  vecho mv ${ENV_FILE}-mdc-tmp ${ENV_FILE}
104
117
  mv ${ENV_FILE}-mdc-tmp ${ENV_FILE}
105
118
 
package/net2dot.cljs CHANGED
@@ -29,7 +29,7 @@
29
29
  (S/replace #"[*]" "_STAR_")
30
30
  (S/replace #"[$]" "_DOLLAR_")
31
31
  (S/replace #"[{]" "_LCURLY_")
32
- (S/replace #"[}]" "_LCURLY_")
32
+ (S/replace #"[}]" "_RCURLY_")
33
33
  (S/replace #"[ ]" "_SPACE_")))
34
34
 
35
35
  (defn node-props [label props]
@@ -61,7 +61,7 @@
61
61
  #(->> (subgraph conlink (str "cluster_bridge_" %2)
62
62
  %2 BRIDGE-PROPS)
63
63
  (assoc %1 %2))
64
- {} (keep :bridge (:links network-config)))
64
+ {} (map :bridge (keep :bridge (:links network-config))))
65
65
  services (reduce
66
66
  #(->> (subgraph host (str "cluster_service_" (dot-id %2))
67
67
  (str "service '" (name %2) "'") SVC-PROPS)
@@ -85,7 +85,7 @@
85
85
  "-" (name dev)))
86
86
  out-id (str "out-" outer-dev)
87
87
  out-parent (condp = (keyword base)
88
- :conlink (get bridges bridge)
88
+ :conlink (get bridges (:bridge bridge))
89
89
  :host host)
90
90
  {:keys [type vlanid]} link
91
91
  [elabel iprops] (if (= "host" base)