├── .gitignore
├── .gitmodules
├── LICENSE
├── Makefile
├── README.md
├── agent
├── cache
│ └── cache.go
├── enforcer
│ ├── base.go
│ ├── enforcer.go
│ ├── enforcer_test.go
│ ├── helpers.go
│ ├── helpers_test.go
│ ├── internal
│ │ └── gen
│ │ │ └── main.go
│ ├── ipsettools.go
│ ├── metrics.go
│ └── testdata
│ │ ├── any-source.iptables
│ │ ├── any-source.json
│ │ ├── demo-policy.iptables
│ │ ├── demo-policy.json
│ │ ├── foreing-source-tenant.iptables
│ │ ├── foreing-source-tenant.json
│ │ ├── no-rules.iptables
│ │ ├── no-rules.json
│ │ ├── no-source-segment.iptables
│ │ ├── no-source-segment.json
│ │ ├── no-target-segment.iptables
│ │ └── no-target-segment.json
├── exec
│ ├── doc.go
│ ├── exec.go
│ └── fake.go
├── firewall
│ ├── common.go
│ ├── defs.go
│ ├── iptables.go
│ ├── iptables_test.go
│ ├── iptsave.go
│ ├── iptsave_test.go
│ ├── mocks_test.go
│ └── store.go
├── hosts.go
├── interface.go
├── iptsave
│ ├── iptables.go
│ ├── iptablesSave
│ │ └── main.go
│ ├── iptables_test.go
│ ├── lexer.go
│ └── token.go
├── metrics.go
├── policycache
│ └── policy_storage.go
├── policycontroller
│ └── controller.go
├── policyhasher
│ ├── hasher.go
│ ├── hasher_test.go
│ └── types.go
├── romanavip.go
├── routes.go
├── routes_test.go
├── rtable
│ ├── route_table.go
│ ├── route_table_test.go
│ └── testdata
│ │ ├── rt_tables.case1
│ │ └── rt_tables.golden
└── sysctl
│ ├── sysctl.go
│ ├── sysctl_test.go
│ └── testdata
│ ├── sysctl.golden
│ └── sysctl.zero
├── cli
├── README.md
├── adaptor
│ └── client.go
├── commands
│ ├── block.go
│ ├── docs.go
│ ├── host.go
│ ├── network.go
│ ├── policy.go
│ ├── root.go
│ └── topology.go
├── kubernetes
│ └── tenant.go
├── openstack
│ └── tenant.go
├── romana.sample.yaml
└── util
│ ├── error.go
│ └── helpers.go
├── cmd
├── romana
│ └── main.go
├── romana_agent
│ └── main.go
├── romana_aws
│ └── main.go
├── romana_cni
│ └── main.go
├── romana_doc
│ └── main.go
├── romana_listener
│ └── main.go
├── romana_route_publisher
│ ├── examples
│ │ ├── endpoint-networks.template
│ │ └── host-groups.template
│ ├── main.go
│ └── routes.go
└── romanad
│ └── main.go
├── cni
├── cni.go
├── cnilink.go
├── divert_rules.go
├── kubernetes.go
├── netconfig.go
├── plugin.go
├── plugin_test.go
└── policy.go
├── common
├── api
│ ├── errors
│ │ ├── errors.go
│ │ └── helpers.go
│ ├── ipam.go
│ ├── policy.go
│ ├── policy_test.go
│ └── romanavip.go
├── auth.go
├── buildinfo.go
├── client
│ ├── client.go
│ ├── client_test.go
│ ├── idring
│ │ ├── idring.go
│ │ ├── idring_test.go
│ │ ├── merge.go
│ │ └── merge_test.go
│ ├── ipam.go
│ ├── ipam_test.go
│ ├── store.go
│ └── testdata
│ │ ├── Test32_1.json
│ │ ├── Test32_2.json
│ │ ├── TestBlackout.json
│ │ ├── TestBlockReuseMask30.json
│ │ ├── TestBlockReuseMask32.json
│ │ ├── TestHostAdd713.json
│ │ ├── TestHostAdditionSimple.json
│ │ ├── TestHostAdditionTags.json
│ │ ├── TestHostAllocation.json
│ │ ├── TestIPAM_DeallocateIP.json
│ │ ├── TestIPReuse.json
│ │ ├── TestLabelUpdate.json
│ │ ├── TestListBlocks.json
│ │ ├── TestMultiNetAllocate.json
│ │ ├── TestNodeAssignment.json
│ │ ├── TestOutOfBoundsError.json
│ │ ├── TestOverlappingCIDRs.json
│ │ ├── TestPanic.json
│ │ ├── TestParseMultiHostGroupsWithPrefix.json
│ │ ├── TestParsePrefixPerHostA.json
│ │ ├── TestParsePrefixPerHostB.json
│ │ ├── TestParseSimpleFlatNetworkA.json
│ │ ├── TestParseSimpleFlatNetworkB.json
│ │ ├── TestParseSimpleFlatNetworkC.json
│ │ ├── TestParseVPCRoutingForTwoAZs.json
│ │ ├── TestPredefinedHosts.json
│ │ ├── TestPrefixGenForEmptyGroups.json
│ │ ├── TestRepeatedNetwork.json
│ │ ├── TestSegments.json
│ │ ├── TestTenants.json
│ │ ├── TestTenantsBug701.json
│ │ ├── TestTopologyGetInputKubeadm.json
│ │ ├── TestTopologyGetInputMultiNetworks.json
│ │ ├── TestTopologyGetOutputKubeadm.json
│ │ ├── TestTopologyGetOutputMultiNetworks.json
│ │ ├── TestUpdateTopology.json
│ │ └── TestUpdateTopologyInvalidBlockMask.json
├── common_test.go
├── config.go
├── defs.go
├── doc.go
├── errors.go
├── helpers.go
├── log
│ └── trace
│ │ └── trace.go
├── middleware.go
├── rest_test.go
├── service.go
├── structures.go
└── testdata
│ ├── agent_proxy_add_policy.sh
│ ├── agent_proxy_delete_policy.sh
│ ├── demo.rsa
│ ├── demo.rsa.pkcs8
│ ├── demo.rsa.pub
│ ├── demo.rsa.pub.pkcs8
│ ├── romana.auth.yaml
│ ├── romana.hooks.yaml
│ ├── romana.sample.yaml
│ └── romanad.yaml
├── doc
├── ipam
│ └── ipam.yaml
├── policy-examples
│ ├── example-policy-dhcp-host.json
│ ├── example-policy-dhcp-vm.json
│ ├── example-policy-icmp-vm.json
│ ├── example-policy-ssh-vm.json
│ └── policy-service-agent.json
├── policy.md
├── policy
│ └── policy.yaml
├── root
│ └── root.yaml
├── security.md
├── tenant
│ └── tenant.yaml
├── tools
│ ├── code.go
│ └── swagger.go
└── topology
│ └── topology.yaml
├── listener
├── listener.go
├── nodes.go
├── processor.go
├── resources.go
├── resources_test.go
├── romanavip.go
├── testdata
│ ├── any-source.json
│ ├── any-source.kube
│ ├── any-source.yml
│ ├── demo-policy.json
│ ├── demo-policy.kube
│ ├── foreing-source-tenant.json
│ ├── foreing-source-tenant.kube
│ ├── foreing-source-tenant.yml
│ ├── no-rules.json
│ ├── no-rules.kube
│ ├── no-rules.yml
│ ├── no-source-segment.json
│ ├── no-source-segment.kube
│ ├── no-source-segment.yml
│ ├── no-target-segment.json
│ ├── no-target-segment.kube
│ └── no-target-segment.yml
├── translate.go
└── translate_test.go
├── pkg
└── policytools
│ ├── blueprint.go
│ ├── blueprint_table.go
│ ├── data
│ └── policy.tsv
│ ├── doc.go
│ ├── internal
│ └── gen
│ │ └── main.go
│ ├── iterator.go
│ ├── iterator_test.go
│ ├── templates
│ └── blueprint.go_template
│ └── validate.go
├── routepublisher
├── bird
│ └── bird.go
└── publisher
│ └── publisher.go
├── server
├── handlers.go
├── policy_test.go
└── romanad.go
└── test
├── cmd
└── fuzzer
│ └── main.go
├── doc.go
├── integration_chunk_test.go
└── integration_test.go
/.gitignore:
--------------------------------------------------------------------------------
1 | # Compiled Object files, Static and Dynamic libs (Shared Objects)
2 | *.o
3 | *.a
4 | *.so
5 | *.sqlite3
6 |
7 | # Folders
8 | _obj
9 | _test
10 | .idea
11 |
12 | # Architecture specific extensions/prefixes
13 | *.[568vq]
14 | [568vq].out
15 |
16 | *.cgo1.go
17 | *.cgo2.c
18 | _cgo_defun.c
19 | _cgo_gotypes.go
20 | _cgo_export.*
21 |
22 | _testmain.go
23 |
24 | *.exe
25 | *.test
26 | *.prof
27 |
28 | tags
29 |
30 | # Emacs artifacts
31 | *~
32 | \#*\#
33 | .\#*\#
34 |
35 | # Eclipse artifacts
36 | .project
37 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | #
2 | # test: run unit tests with coverage turned on.
3 | # vet: run go vet for catching subtle errors.
4 | # lint: run golint.
5 | #
6 |
7 | services = $$GOPATH/bin/romanad\
8 | $$GOPATH/bin/romana\
9 | $$GOPATH/bin/romana_agent\
10 | $$GOPATH/bin/romana_cni\
11 | $$GOPATH/bin/romana_aws\
12 | $$GOPATH/bin/romana_listener\
13 | $$GOPATH/bin/romana_route_publisher\
14 | $$GOPATH/bin/romana_doc
15 |
16 | UPX_VERSION := $(shell upx --version 2>/dev/null)
17 |
18 | all: fmt vet install
19 |
20 | buildbranch := $(shell git describe --all --abbrev=7 --always)
21 | buildcommit := $(shell git log --pretty=format:"%h" | head -n 1)
22 | buildinfo := ${buildbranch}-${buildcommit}
23 | install:
24 | go list -f '{{.ImportPath}}' "./..." | \
25 | grep -v /vendor/ | xargs go install -race -ldflags \
26 | "-X github.com/romana/core/common.buildInfo=$(buildinfo) \
27 | -X github.com/romana/core/common.buildTimeStamp=`date -u '+%Y-%m-%d_%I:%M:%S%p'`"
28 |
29 | test:
30 | go list -f '{{.ImportPath}}' "./..." | \
31 | grep -v /vendor/ | xargs go test -timeout=30s -cover
32 |
33 | testv:
34 | go list -f '{{.ImportPath}}' "./..." | \
35 | grep -v /vendor/ | xargs go test -v -timeout=30s -cover
36 |
37 | vet:
38 | go list -f '{{.ImportPath}}' "./..." | \
39 | grep -v /vendor/ | xargs go vet
40 |
41 | fmt:
42 | go list -f '{{.ImportPath}}' "./..." | \
43 | grep -v /vendor/ | xargs go fmt
44 |
45 | lint:
46 | go list -f '{{.Dir}}' "./..." | \
47 | grep -v /vendor/ | xargs -n 1 golint
48 |
49 | upx:
50 | ifndef UPX_VERSION
51 | $(error "No upx in $(PATH), consider doing apt-get install upx")
52 | endif
53 | upx $(services)
54 |
55 | clean:
56 | rm $(services)
57 |
58 | doc_main_file = $(shell go list -f '{{.Dir}}' "./..." |fgrep github.com/romana/core/tools/doc)/doc.go
59 | root_dir = $(shell go list -f '{{.Root}}' "./..." | head -1)
60 | swagger_dir_tmp = $(shell mktemp -d)
61 | index=$(shell for svc in agent ipam root tenant topology policy; do echo '
'$$svc''; echo; done)
62 |
63 | swagger:
64 | cd $(swagger_dir_tmp)
65 | echo In `pwd`
66 | go run $(doc_main_file) $(root_dir) > swagger.out 2>&1
67 | # If the above succeeded, we do not need to keep the output,
68 | # so remove it.
69 | rm swagger.out
70 | echo 'Romana services
' > doc/index.html
71 | echo '' >> doc/index.html
72 | echo "$(index)" >> doc/index.html
73 | echo '
' >> doc/index.html
74 | echo '' >> doc/index.html
75 | cat index.html
76 | aws s3 sync --acl public-read doc s3://swagger.romana.io/romana
77 | echo Latest doc uploaded to http://swagger.romana.io.s3-website-us-west-1.amazonaws.com/romana/index.html
78 | rm -rf $(swagger_dir_tmp)
79 |
80 | .PHONY: test vet lint all install clean fmt upx testv
81 |
--------------------------------------------------------------------------------
/agent/cache/cache.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2017 Pani Networks
2 | // All Rights Reserved.
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may
5 | // not use this file except in compliance with the License. You may obtain
6 | // a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | // License for the specific language governing permissions and limitations
14 | // under the License.
15 |
16 | package cache
17 |
18 | import "sync"
19 |
20 | type Interface interface {
21 | Put(string, interface{})
22 | Get(string) (interface{}, bool)
23 | Delete(string)
24 | List() []interface{}
25 | Keys() []string
26 | }
27 |
28 | func New() Interface {
29 | items := make(map[string]interface{})
30 | m := &sync.Mutex{}
31 | return Cache{Items: items, Mutex: m}
32 | }
33 |
34 | // note: implementation here uses methods on struct
35 | // instead of pointers because map itself is a pointer.
36 |
37 | type Cache struct {
38 | Items map[string]interface{}
39 | *sync.Mutex
40 | }
41 |
42 | func (s Cache) Put(key string, item interface{}) {
43 | s.Lock()
44 | defer s.Unlock()
45 | s.Items[key] = item
46 | }
47 |
48 | func (s Cache) Get(key string) (interface{}, bool) {
49 | s.Lock()
50 | defer s.Unlock()
51 | item, ok := s.Items[key]
52 | return item, ok
53 | }
54 |
55 | func (s Cache) Delete(key string) {
56 | s.Lock()
57 | defer s.Unlock()
58 | delete(s.Items, key)
59 | }
60 |
61 | func (s Cache) List() []interface{} {
62 | s.Lock()
63 | defer s.Unlock()
64 | var result []interface{}
65 |
66 | for _, v := range s.Items {
67 | result = append(result, v)
68 | }
69 |
70 | return result
71 | }
72 |
73 | func (s Cache) Keys() []string {
74 | s.Lock()
75 | defer s.Unlock()
76 | var result []string
77 |
78 | for k, _ := range s.Items {
79 | result = append(result, k)
80 | }
81 |
82 | return result
83 | }
84 |
--------------------------------------------------------------------------------
/agent/enforcer/helpers_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2016 Pani Networks
2 | // All Rights Reserved.
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may
5 | // not use this file except in compliance with the License. You may obtain
6 | // a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | // License for the specific language governing permissions and limitations
14 | // under the License.
15 |
16 | // +build ignore
17 |
18 | package enforcer
19 |
20 | import (
21 | "net"
22 | "testing"
23 |
24 | "github.com/romana/core/agent/iptsave"
25 | "github.com/romana/core/common"
26 | )
27 |
28 | type MockNC struct {
29 | netBits uint
30 | portBits uint
31 | tenantBits uint
32 | segmentBits uint
33 | endpointBits uint
34 | }
35 |
36 | func (m MockNC) PrefixBits() uint {
37 | return m.netBits
38 | }
39 |
40 | func (m MockNC) PortBits() uint {
41 | return m.portBits
42 | }
43 |
44 | func (m MockNC) TenantBits() uint {
45 | return m.tenantBits
46 | }
47 |
48 | func (m MockNC) SegmentBits() uint {
49 | return m.segmentBits
50 | }
51 |
52 | func (m MockNC) EndpointBits() uint {
53 | return m.endpointBits
54 | }
55 |
56 | func (m MockNC) EndpointNetmaskSize() uint64 {
57 | return uint64(0)
58 | }
59 |
60 | func (m MockNC) RomanaGW() net.IP {
61 | return net.ParseIP("10.0.0.1")
62 | }
63 |
64 | func (m MockNC) PNetCIDR() (cidr *net.IPNet, err error) {
65 | return &net.IPNet{}, nil
66 | }
67 |
68 | func TestMakeIngressTenantJumpRule(t *testing.T) {
69 | netConfig := MockNC{uint(8), uint(8), uint(4), uint(4), uint(8)}
70 | rule := MakeIngressTenantJumpRule(common.Tenant{NetworkID: uint64(2)}, netConfig)
71 | t.Log(rule)
72 | }
73 |
74 | func makeMockRule(body string, target string) *iptsave.IPrule {
75 | return &iptsave.IPrule{
76 | Match: []*iptsave.Match{
77 | &iptsave.Match{
78 | Body: body,
79 | },
80 | },
81 | Action: iptsave.IPtablesAction{
82 | Type: iptsave.ActionDefault,
83 | Body: target,
84 | },
85 | }
86 | }
87 |
88 | func TestInsertNormalRule(t *testing.T) {
89 | fakeChain := &iptsave.IPchain{
90 | Name: "Test",
91 | Rules: []*iptsave.IPrule{
92 | makeMockRule("Header Rule 1", "ACCEPT"),
93 | makeMockRule("Header Rule 2", "ACCEPT"),
94 | makeMockRule("Footer Rule 1", "RETURN"),
95 | makeMockRule("Footer Rule 2", "DROP"),
96 | },
97 | }
98 |
99 | InsertNormalRule(fakeChain, makeMockRule("Normal Rule 1", "LOG"))
100 | if fakeChain.Rules[2].Action.Body != "LOG" {
101 | t.Errorf("Unexpected rule at first normal position, expect LOG got %s", fakeChain.Rules[2])
102 | }
103 | t.Logf("%s", fakeChain)
104 |
105 | fakeChain = &iptsave.IPchain{
106 | Name: "Test",
107 | Rules: []*iptsave.IPrule{
108 | makeMockRule("Footer Rule 1", "RETURN"),
109 | makeMockRule("Footer Rule 2", "DROP"),
110 | },
111 | }
112 |
113 | InsertNormalRule(fakeChain, makeMockRule("Normal Rule 1", "LOG"))
114 | if fakeChain.Rules[0].Action.Body != "LOG" {
115 | t.Errorf("Unexpected rule at first normal position, expect LOG got %s", fakeChain.Rules[0])
116 | }
117 | t.Logf("%s", fakeChain)
118 |
119 | fakeChain = &iptsave.IPchain{
120 | Name: "Test",
121 | Rules: []*iptsave.IPrule{
122 | makeMockRule("Header Rule 1", "ACCEPT"),
123 | makeMockRule("Header Rule 2", "ACCEPT"),
124 | },
125 | }
126 |
127 | InsertNormalRule(fakeChain, makeMockRule("Normal Rule 1", "LOG"))
128 | if fakeChain.Rules[2].Action.Body != "LOG" {
129 | t.Errorf("Unexpected rule at first normal position, expect LOG got %s", fakeChain.Rules[2])
130 | }
131 | t.Logf("%s", fakeChain)
132 |
133 | }
134 |
--------------------------------------------------------------------------------
/agent/enforcer/internal/gen/main.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2016 Pani Networks
2 | // All Rights Reserved.
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may
5 | // not use this file except in compliance with the License. You may obtain
6 | // a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | // License for the specific language governing permissions and limitations
14 | // under the License.
15 |
16 | // +build ignore
17 |
18 | package main
19 |
20 | import (
21 | "encoding/csv"
22 | "flag"
23 | "os"
24 | "text/template"
25 | )
26 |
27 | func main() {
28 | templatePath := flag.String("template", "", "template to render")
29 | dataFilePath := flag.String("data", "", "")
30 | outputPath := flag.String("out", "", "")
31 | flag.Parse()
32 |
33 | template, err := template.ParseFiles(*templatePath)
34 | if err != nil {
35 | panic(err)
36 | }
37 |
38 | data, err := os.Open(*dataFilePath)
39 | if err != nil {
40 | panic(err)
41 | }
42 |
43 | r := csv.NewReader(data)
44 | r.Comma = '\t'
45 | r.LazyQuotes = true
46 |
47 | records, err := r.ReadAll()
48 | if err != nil {
49 | panic(err)
50 | }
51 |
52 | out, err := os.OpenFile(*outputPath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644)
53 | if err != nil {
54 | panic(err)
55 | }
56 | defer out.Close()
57 |
58 | err = template.Execute(out, records)
59 | if err != nil {
60 | panic(err)
61 | }
62 |
63 | err = out.Close()
64 | if err != nil {
65 | panic(err)
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/agent/enforcer/ipsettools.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2017 Pani Networks
2 | // All Rights Reserved.
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may
5 | // not use this file except in compliance with the License. You may obtain
6 | // a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | // License for the specific language governing permissions and limitations
14 | // under the License.
15 |
16 | // Policy enforcer package translates romana policies into iptables rules.
17 | package enforcer
18 |
19 | import (
20 | "context"
21 | "time"
22 |
23 | "github.com/romana/ipset"
24 | )
25 |
26 | // updateIpsets is a flush/rebuild implementation of ipset update
27 | // it will wipe all sets in a system and populate new ones.
28 | // TODO for Stas this monopolizes ipsets
29 | // and also pollutes with sets which never deleted.
30 | // Need cleaner implementation
31 | func updateIpsets(ctx context.Context, sets *ipset.Ipset) error {
32 |
33 | err := attemptIpsetCleanup(ctx, sets)
34 | if err != nil {
35 | return err
36 | }
37 |
38 | ipsetHandle, err := ipset.NewHandle()
39 | if err != nil {
40 | return err
41 | }
42 |
43 | err = ipsetHandle.Start()
44 | if err != nil {
45 | return err
46 | }
47 |
48 | err = ipsetHandle.Create(sets)
49 | if err != nil {
50 | return err
51 | }
52 |
53 | err = ipsetHandle.Add(sets)
54 | if err != nil {
55 | return err
56 | }
57 |
58 | err = ipsetHandle.Quit()
59 | if err != nil {
60 | return err
61 | }
62 |
63 | cTimout, cancel := context.WithTimeout(ctx, 10*time.Second)
64 | defer cancel()
65 | err = ipsetHandle.Wait(cTimout)
66 | if err != nil {
67 | return err
68 | }
69 |
70 | return nil
71 |
72 | }
73 |
74 | // attemptIpsetCleanup attempts to destroy every set.
75 | // TODO make it less nuclear.
76 | func attemptIpsetCleanup(ctx context.Context, sets *ipset.Ipset) error {
77 | iset, _ := ipset.Load(ctx)
78 | for _, set := range iset.Sets {
79 | _, _ = ipset.Destroy(set)
80 | }
81 |
82 | // flush everything that survived mass destroy.
83 | _, err := ipset.Flush(nil)
84 |
85 | return err
86 | }
87 |
--------------------------------------------------------------------------------
/agent/enforcer/metrics.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2016 Pani Networks
2 | // All Rights Reserved.
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may
5 | // not use this file except in compliance with the License. You may obtain
6 | // a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | // License for the specific language governing permissions and limitations
14 | // under the License.
15 |
16 | package enforcer
17 |
18 | import (
19 | "fmt"
20 |
21 | "github.com/prometheus/client_golang/prometheus"
22 | )
23 |
24 | var (
25 | ErrMakeSets = prometheus.NewCounter(
26 | prometheus.CounterOpts{
27 | Name: "romana_err_make_sets_total",
28 | Help: "Number of errors attempting to build ipset Sets.",
29 | },
30 | )
31 | ErrApplySets = prometheus.NewCounter(
32 | prometheus.CounterOpts{
33 | Name: "romana_err_apply_sets_total",
34 | Help: "Number of errors attempting to apply ipset Sets.",
35 | },
36 | )
37 | ErrValidateIptables = prometheus.NewCounter(
38 | prometheus.CounterOpts{
39 | Name: "romana_err_validate_iptables_total",
40 | Help: "Number of errors when validating iptables.",
41 | },
42 | )
43 | ErrApplyIptables = prometheus.NewCounter(
44 | prometheus.CounterOpts{
45 | Name: "romana_err_apply_iptables_total",
46 | Help: "Number of errors attempting to apply iptables.",
47 | },
48 | )
49 | NumPolicyUpdates = prometheus.NewCounter(
50 | prometheus.CounterOpts{
51 | Name: "romana_policy_updates_total",
52 | Help: "Number of policy updates processed.",
53 | },
54 | )
55 | NumBlockUpdates = prometheus.NewCounter(
56 | prometheus.CounterOpts{
57 | Name: "romana_block_updates_total",
58 | Help: "Number of block updates processed.",
59 | },
60 | )
61 | NumEnforcerTick = prometheus.NewCounter(
62 | prometheus.CounterOpts{
63 | Name: "romana_enforcer_ticks_total",
64 | Help: "Number of enforcer ticks since start.",
65 | },
66 | )
67 | NumManagedSets = prometheus.NewGauge(
68 | prometheus.GaugeOpts{
69 | Name: "romana_managed_sets",
70 | Help: "Number ipset sets managed by Romana policy.",
71 | },
72 | )
73 | NumPolicyRules = prometheus.NewGauge(
74 | prometheus.GaugeOpts{
75 | Name: "romana_policy_rules",
76 | Help: "Number of Romana policy rules applied to the host.",
77 | },
78 | )
79 | )
80 |
81 | // MetricsRegister registers package global metrics into registry provided,
82 | // for later exposure.
83 | func MetricsRegister(registry *prometheus.Registry) error {
84 | if registry == nil {
85 | return fmt.Errorf("registry must not be nil")
86 | }
87 |
88 | for _, counter := range []prometheus.Counter{
89 | ErrMakeSets,
90 | ErrApplySets,
91 | ErrValidateIptables,
92 | ErrApplyIptables,
93 | NumPolicyUpdates,
94 | NumBlockUpdates,
95 | NumEnforcerTick,
96 | NumManagedSets,
97 | NumPolicyRules,
98 | } {
99 | err := registry.Register(counter)
100 | if err != nil {
101 | return err
102 | }
103 | }
104 |
105 | return nil
106 | }
107 |
--------------------------------------------------------------------------------
/agent/enforcer/testdata/any-source.iptables:
--------------------------------------------------------------------------------
1 | *filter
2 | :ROMANA-FORWARD-IN -
3 | :ROMANA-P-702122ee51ef3cb5 -
4 | :ROMANA-P-702122ee51ef3cb5_X -
5 | :ROMANA-P-702122ee51ef3cb5_R -
6 | -A ROMANA-FORWARD-IN -j ROMANA-P-702122ee51ef3cb5
7 | -A ROMANA-P-702122ee51ef3cb5 -m set --match-set ROMANA-446f0022c0a89d93 dst -j ROMANA-P-702122ee51ef3cb5_X
8 | -A ROMANA-P-702122ee51ef3cb5_X -j ROMANA-P-702122ee51ef3cb5_R
9 | -A ROMANA-P-702122ee51ef3cb5_R -p tcp --dport 80 -j ACCEPT
10 | COMMIT
11 |
--------------------------------------------------------------------------------
/agent/enforcer/testdata/any-source.json:
--------------------------------------------------------------------------------
1 | {"id":"kube.tenant-a.pol1.d3122af3-a4cc-11e7-a1ea-068bf013416e","direction":"ingress","applied_to":[{"tenant_id":"tenant-a","segment_id":"backend"}],"ingress":[{"peers":[{"peer":"any"}],"rules":[{"protocol":"tcp","ports":[80]}]}]}
2 |
--------------------------------------------------------------------------------
/agent/enforcer/testdata/demo-policy.iptables:
--------------------------------------------------------------------------------
1 | *filter
2 | :ROMANA-FORWARD-IN -
3 | :ROMANA-P-aca884e8b43cc0e7 -
4 | :ROMANA-P-aca884e8b43cc0e7_X -
5 | :ROMANA-P-aca884e8b43cc0e7_R -
6 | -A ROMANA-FORWARD-IN -j ROMANA-P-aca884e8b43cc0e7
7 | -A ROMANA-P-aca884e8b43cc0e7 -m set --match-set ROMANA-446f0022c0a89d93 dst -j ROMANA-P-aca884e8b43cc0e7_X
8 | -A ROMANA-P-aca884e8b43cc0e7_X -m set --match-set ROMANA-78c82e6c585c36a6 src -j ROMANA-P-aca884e8b43cc0e7_R
9 | -A ROMANA-P-aca884e8b43cc0e7_R -p tcp --dport 80 -j ACCEPT
10 | COMMIT
11 |
--------------------------------------------------------------------------------
/agent/enforcer/testdata/demo-policy.json:
--------------------------------------------------------------------------------
1 | {"id":"kube.tenant-a.pol1.a8e9618f-ab1d-11e7-a1ea-068bf013416e","direction":"ingress","applied_to":[{"tenant_id":"tenant-a","segment_id":"backend"}],"ingress":[{"peers":[{"tenant_id":"tenant-a","segment_id":"frontend"}],"rules":[{"protocol":"tcp","ports":[80]}]}]}
2 |
--------------------------------------------------------------------------------
/agent/enforcer/testdata/foreing-source-tenant.iptables:
--------------------------------------------------------------------------------
1 | *filter
2 | :ROMANA-FORWARD-IN -
3 | :ROMANA-P-b62df31062b6b496 -
4 | :ROMANA-P-b62df31062b6b496_X -
5 | :ROMANA-P-b62df31062b6b496_R -
6 | -A ROMANA-FORWARD-IN -j ROMANA-P-b62df31062b6b496
7 | -A ROMANA-P-b62df31062b6b496 -m set --match-set ROMANA-446f0022c0a89d93 dst -j ROMANA-P-b62df31062b6b496_X
8 | -A ROMANA-P-b62df31062b6b496_X -m set --match-set ROMANA-fe6dda4923beb047 src -j ROMANA-P-b62df31062b6b496_R
9 | -A ROMANA-P-b62df31062b6b496_R -p tcp --dport 80 -j ACCEPT
10 | COMMIT
11 |
--------------------------------------------------------------------------------
/agent/enforcer/testdata/foreing-source-tenant.json:
--------------------------------------------------------------------------------
1 | {"id":"kube.tenant-a.pol1.0ef621c5-a4cd-11e7-a1ea-068bf013416e","direction":"ingress","applied_to":[{"tenant_id":"tenant-a","segment_id":"backend"}],"ingress":[{"peers":[{"tenant_id":"kube-system"}],"rules":[{"protocol":"tcp","ports":[80]}]}]}
2 |
--------------------------------------------------------------------------------
/agent/enforcer/testdata/no-rules.iptables:
--------------------------------------------------------------------------------
1 | *filter
2 | :ROMANA-FORWARD-IN -
3 | :ROMANA-P-a70997e12148f097 -
4 | :ROMANA-P-a70997e12148f097_X -
5 | :ROMANA-P-a70997e12148f097_R -
6 | -A ROMANA-FORWARD-IN -j ROMANA-P-a70997e12148f097
7 | -A ROMANA-P-a70997e12148f097 -m set --match-set ROMANA-446f0022c0a89d93 dst -j ROMANA-P-a70997e12148f097_X
8 | -A ROMANA-P-a70997e12148f097_X -m set --match-set ROMANA-78c82e6c585c36a6 src -j ROMANA-P-a70997e12148f097_R
9 | -A ROMANA-P-a70997e12148f097_R -j ACCEPT
10 | COMMIT
11 |
--------------------------------------------------------------------------------
/agent/enforcer/testdata/no-rules.json:
--------------------------------------------------------------------------------
1 | {"id":"kube.tenant-a.pol1.4bf9aba4-a4cd-11e7-a1ea-068bf013416e","direction":"ingress","applied_to":[{"tenant_id":"tenant-a","segment_id":"backend"}],"ingress":[{"peers":[{"tenant_id":"tenant-a","segment_id":"frontend"}],"rules":[{"protocol":"any"}]}]}
2 |
--------------------------------------------------------------------------------
/agent/enforcer/testdata/no-source-segment.iptables:
--------------------------------------------------------------------------------
1 | *filter
2 | :ROMANA-FORWARD-IN -
3 | :ROMANA-P-d881c0948d2a25ec -
4 | :ROMANA-P-d881c0948d2a25ec_X -
5 | :ROMANA-P-d881c0948d2a25ec_R -
6 | -A ROMANA-FORWARD-IN -j ROMANA-P-d881c0948d2a25ec
7 | -A ROMANA-P-d881c0948d2a25ec -m set --match-set ROMANA-446f0022c0a89d93 dst -j ROMANA-P-d881c0948d2a25ec_X
8 | -A ROMANA-P-d881c0948d2a25ec_X -m set --match-set ROMANA-bc42e93f0aa666e1 src -j ROMANA-P-d881c0948d2a25ec_R
9 | -A ROMANA-P-d881c0948d2a25ec_R -p tcp --dport 80 -j ACCEPT
10 | COMMIT
11 |
--------------------------------------------------------------------------------
/agent/enforcer/testdata/no-source-segment.json:
--------------------------------------------------------------------------------
1 | {"id":"kube.tenant-a.pol1.7bcdb586-a4cd-11e7-a1ea-068bf013416e","direction":"ingress","applied_to":[{"tenant_id":"tenant-a","segment_id":"backend"}],"ingress":[{"peers":[{"tenant_id":"tenant-a"}],"rules":[{"protocol":"tcp","ports":[80]}]}]}
2 |
--------------------------------------------------------------------------------
/agent/enforcer/testdata/no-target-segment.iptables:
--------------------------------------------------------------------------------
1 | *filter
2 | :ROMANA-FORWARD-IN -
3 | :ROMANA-P-1853ba0d91f85673 -
4 | :ROMANA-P-1853ba0d91f85673_X -
5 | :ROMANA-P-1853ba0d91f85673_R -
6 | -A ROMANA-FORWARD-IN -j ROMANA-P-1853ba0d91f85673
7 | -A ROMANA-P-1853ba0d91f85673 -m set --match-set ROMANA-bc42e93f0aa666e1 dst -j ROMANA-P-1853ba0d91f85673_X
8 | -A ROMANA-P-1853ba0d91f85673_X -m set --match-set ROMANA-78c82e6c585c36a6 src -j ROMANA-P-1853ba0d91f85673_R
9 | -A ROMANA-P-1853ba0d91f85673_R -p tcp --dport 80 -j ACCEPT
10 | COMMIT
11 |
--------------------------------------------------------------------------------
/agent/enforcer/testdata/no-target-segment.json:
--------------------------------------------------------------------------------
1 | {"id":"kube.tenant-a.pol1.a7cac6f1-a4cd-11e7-a1ea-068bf013416e","direction":"ingress","applied_to":[{"tenant_id":"tenant-a"}],"ingress":[{"peers":[{"tenant_id":"tenant-a","segment_id":"frontend"}],"rules":[{"protocol":"tcp","ports":[80]}]}]}
2 |
--------------------------------------------------------------------------------
/agent/exec/doc.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2016 Pani Networks
2 | // All Rights Reserved.
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may
5 | // not use this file except in compliance with the License. You may obtain
6 | // a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | // License for the specific language governing permissions and limitations
14 | // under the License.
15 |
16 | // Package provides wrapper around os.exec for purpose of testing
17 | package exec
18 |
--------------------------------------------------------------------------------
/agent/exec/exec.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2016 Pani Networks
2 | // All Rights Reserved.
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may
5 | // not use this file except in compliance with the License. You may obtain
6 | // a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | // License for the specific language governing permissions and limitations
14 | // under the License.
15 |
16 | package exec
17 |
18 | import (
19 | "io"
20 | "os/exec"
21 | )
22 |
23 | // Interfaces in this file are designed to provide
24 | // a "mockable" implementation of os/exec for testing purposes.
25 |
26 | // Executable is a facade to exec.Command().Output().
27 | type Executable interface {
28 | Exec(cmd string, args []string) ([]byte, error)
29 | Cmd(cmd string, args []string) Cmd
30 | }
31 |
32 | // Cmd is a facade to exec.Cmd.
33 | type Cmd interface {
34 | StdinPipe() (io.WriteCloser, error)
35 | CombinedOutput() ([]byte, error)
36 | Start() error
37 | Wait() error
38 | }
39 |
40 | // DefaultExecutor is a default implementation of Executable that passes
41 | // back to standard library.
42 | type DefaultExecutor struct{}
43 |
44 | // Exec implements Executable interface by proxiyng all requests to exec.Command().
45 | func (DefaultExecutor) Exec(cmd string, args []string) ([]byte, error) {
46 | cmdObj := exec.Command(cmd, args...)
47 | out, err := cmdObj.CombinedOutput()
48 | return out, err
49 | }
50 |
51 | // Cmd implements Executable interface.
52 | func (DefaultExecutor) Cmd(cmd string, args []string) Cmd {
53 | return exec.Command(cmd, args...)
54 | }
55 |
--------------------------------------------------------------------------------
/agent/exec/fake.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2016 Pani Networks
2 | // All Rights Reserved.
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may
5 | // not use this file except in compliance with the License. You may obtain
6 | // a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | // License for the specific language governing permissions and limitations
14 | // under the License.
15 |
16 | package exec
17 |
18 | import (
19 | "fmt"
20 | "io"
21 | "strings"
22 | )
23 |
24 | // FakeExecutor implements Executable
25 | // stores faked Output, Error and commands recorded by Exec.
26 | type FakeExecutor struct {
27 | Output []byte
28 | Error error
29 | Commands *string
30 | }
31 |
32 | // FakeCmd implement Cmd interface for testing purposes.
33 | type FakeCmd struct{}
34 |
35 | func (FakeCmd) StdinPipe() (io.WriteCloser, error) {
36 | // TODO
37 | return nil, nil
38 | }
39 |
40 | func (FakeCmd) CombinedOutput() ([]byte, error) {
41 | // TODO
42 | var empty []byte
43 | return empty, nil
44 | }
45 |
46 | func (FakeCmd) Start() error {
47 | return nil
48 | }
49 |
50 | func (FakeCmd) Wait() error {
51 | return nil
52 | }
53 |
54 | // Exec is a method of fake executor that will record all incoming commands
55 | // and use faked Output and Error.
56 | func (x *FakeExecutor) Exec(cmd string, args []string) ([]byte, error) {
57 | var c string
58 | if x.Commands == nil {
59 | c = fmt.Sprintf("%s %s", cmd, strings.Join(args, " "))
60 | } else {
61 | c = fmt.Sprintf("%s\n%s %s", *x.Commands, cmd, strings.Join(args, " "))
62 | }
63 | x.Commands = &c
64 | return x.Output, x.Error
65 | }
66 |
67 | func (x *FakeExecutor) Cmd(cmd string, args []string) Cmd {
68 | return FakeCmd{}
69 | }
70 |
--------------------------------------------------------------------------------
/agent/firewall/iptsave_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2016 Pani Networks
2 | // All Rights Reserved.
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may
5 | // not use this file except in compliance with the License. You may obtain
6 | // a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | // License for the specific language governing permissions and limitations
14 | // under the License.
15 | //
16 | // IPtables save/restore based firewall implementations.
17 |
18 | package firewall
19 |
20 | import (
21 | "bytes"
22 | "net"
23 | "testing"
24 |
25 | "github.com/romana/core/agent/iptsave"
26 | )
27 |
28 | const (
29 | defaultRule = `
30 | # Generated by iptables-save v1.4.21 on Tue Aug 30 12:21:37 2016
31 | *filter
32 | :INPUT ACCEPT [2258:359835]
33 | :FORWARD ACCEPT [0:0]
34 | :OUTPUT ACCEPT [2202:367244]
35 | :DOCKER - [0:0]
36 | :DOCKER-ISOLATION - [0:0]
37 | :KUBE-SERVICES - [0:0]
38 | -A FORWARD -j DOCKER-ISOLATION
39 | -A FORWARD -o docker0 -j DOCKER
40 | -A FORWARD -o docker0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
41 | -A FORWARD -i docker0 ! -o docker0 -j ACCEPT
42 | -A FORWARD -i docker0 -o docker0 -j ACCEPT
43 | -A OUTPUT -m comment --comment "kubernetes service portals" -j KUBE-SERVICES
44 | -A DOCKER-ISOLATION -j RETURN
45 | COMMIT
46 | `
47 |
48 | testRule1 = "OTHERCHAIN -p tcp -m tcp --dport 442 ! -m tcp --sport 443 -j ACCEPT"
49 | testRule2 = "MYCHAIN -p tcp -m tcp --dport 443 ! -m tcp --sport 442 -j OTHERCHAIN"
50 | testRule3 = "MYCHAIN -p tcp -m tcp --dport 80 ! -m tcp --sport 71 -j OTHERCHAIN"
51 | )
52 |
53 | /*
54 | func TestInit(t *testing.T) {
55 | currentRules := iptsave.Parse(bytes.NewReader([]byte(defaultRule)))
56 | }
57 | */
58 |
59 | func TestSetDefaultRules(t *testing.T) {
60 | rules := []FirewallRule{}
61 |
62 | rules = append(rules, &IPtablesRule{Body: testRule1})
63 | rules = append(rules, &IPtablesRule{Body: testRule2})
64 | rules = append(rules, &IPtablesRule{Body: testRule3})
65 |
66 | firewall := IPTsaveFirewall{DesiredState: &iptsave.IPtables{}}
67 | firewall.DesiredState.Tables = append(firewall.DesiredState.Tables, &iptsave.IPtable{Name: "filter"})
68 |
69 | err := firewall.SetDefaultRules(rules)
70 | if err != nil {
71 | t.Error(err)
72 | }
73 |
74 | expect := `*filter
75 | :MYCHAIN
76 | :OTHERCHAIN
77 | -A MYCHAIN -p tcp -m tcp --dport 443 ! -m tcp --sport 442 -j OTHERCHAIN
78 | -A MYCHAIN -p tcp -m tcp --dport 80 ! -m tcp --sport 71 -j OTHERCHAIN
79 | -A OTHERCHAIN -p tcp -m tcp --dport 442 ! -m tcp --sport 443 -j ACCEPT
80 | COMMIT`
81 |
82 | if len(firewall.DesiredState.Render()) != 253 {
83 | t.Errorf("expect %s\n got %s\n", firewall.DesiredState.Render(), expect)
84 | }
85 | }
86 |
87 | func TestSetEndpoint(t *testing.T) {
88 | firewall := IPTsaveFirewall{
89 | DesiredState: &iptsave.IPtables{},
90 | CurrentState: &iptsave.IPtables{},
91 | networkConfig: mockNetworkConfig{},
92 | }
93 | firewall.CurrentState.Parse(bytes.NewReader([]byte(defaultRule)))
94 | firewall.DesiredState.Tables = append(firewall.DesiredState.Tables, &iptsave.IPtable{Name: "filter"})
95 |
96 | if err := firewall.SetEndpoint(mockFirewallEndpoint{"eth0", "A", net.ParseIP("127.0.0.1")}); err != nil {
97 | t.Errorf("%s", err)
98 | }
99 |
100 | expect := `
101 | *filter
102 | :INPUT -
103 | :OUTPUT -
104 | :FORWARD -
105 | :ROMANA-INPUT -
106 | :ROMANA-FORWARD-OUT -
107 | :ROMANA-FORWARD-IN -
108 | -A INPUT -i eth -j ROMANA-INPUT
109 | -A OUTPUT -o eth -j ROMANA-FORWARD-IN
110 | -A FORWARD -i eth -j ROMANA-FORWARD-OUT
111 | -A FORWARD -o eth -j ROMANA-FORWARD-IN
112 | COMMIT`
113 |
114 | if len(firewall.DesiredState.Render()) != 263 {
115 | t.Errorf("expect %s\n got %s\n", expect, firewall.DesiredState.Render())
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/agent/firewall/mocks_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2016 Pani Networks
2 | // All Rights Reserved.
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may
5 | // not use this file except in compliance with the License. You may obtain
6 | // a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | // License for the specific language governing permissions and limitations
14 | // under the License.
15 | //
16 | // Fake structures used in testing.
17 |
18 | package firewall
19 |
20 | import (
21 | "database/sql"
22 | _ "github.com/mattn/go-sqlite3"
23 | "net"
24 | "sync"
25 | )
26 |
27 | // mockNetworkConfig implements NetConfig
28 | type mockNetworkConfig struct{}
29 |
30 | // EndpointNetmaskSize returns integer value (aka size) of endpoint netmask.
31 | func (c mockNetworkConfig) EndpointNetmaskSize() uint64 {
32 | // dc.EndpointSpaceBits = 0
33 | return 32
34 | }
35 |
36 | // PNetCIDR returns pseudo net cidr in net.IPNet format.
37 | func (c mockNetworkConfig) PNetCIDR() (cidr *net.IPNet, err error) {
38 | // dc.Cidr = "10.0.0.0/8"
39 | _, cidr, err = net.ParseCIDR("10.0.0.0/8")
40 | return
41 | }
42 |
43 | // PrefixBits returns tenant bits value from POC config.
44 | func (c mockNetworkConfig) PrefixBits() uint {
45 | return uint(8)
46 | }
47 |
48 | // PortBits returns tenant bits value from POC config.
49 | func (c mockNetworkConfig) PortBits() uint {
50 | return uint(8)
51 | }
52 |
53 | // TenantBits returns tenant bits value from POC config.
54 | func (c mockNetworkConfig) TenantBits() uint {
55 | // dc.TenantBits = 4
56 | return uint(4)
57 | }
58 |
59 | // SegmentBits returns segment bits value from POC config.
60 | func (c mockNetworkConfig) SegmentBits() uint {
61 | // dc.SegmentBits = 4
62 | return uint(4)
63 | }
64 |
65 | // EndpointBits returns endpoint bits value from POC config.
66 | func (c mockNetworkConfig) EndpointBits() uint {
67 | // dc.EndpointBits = 8
68 | return uint(8)
69 | }
70 |
71 | // RomanaGW returns current romana gateway.
72 | func (c mockNetworkConfig) RomanaGW() net.IP {
73 | // {Ip: "172.17.0.1"}
74 | return net.ParseIP("172.17.0.1")
75 | }
76 |
77 | // mockNetworkConfig implements FirewallEndpoint
78 | type mockFirewallEndpoint struct {
79 | Name string
80 | Mac string
81 | IP net.IP
82 | }
83 |
84 | func (c mockFirewallEndpoint) GetName() string {
85 | return c.Name
86 | }
87 |
88 | func (c mockFirewallEndpoint) GetMac() string {
89 | return c.Mac
90 | }
91 |
92 | func (c mockFirewallEndpoint) GetIP() net.IP {
93 | return c.IP
94 | }
95 |
96 | func makeMockStore() firewallStore {
97 | mockStore := firewallStore{}
98 | db, _ := sql.Open("sqlite3", "/tmp/firewall.db")
99 |
100 | createTable := `CREATE TABLE IF NOT EXISTS iptables_rules (
101 | body TEXT PRIMARY KEY,
102 | state VARCHAR
103 | )`
104 | db.Exec(createTable)
105 | deleteFromTable := `DELETE FROM iptables_rules`
106 | db.Exec(deleteFromTable)
107 |
108 | mockStore.mu = new(sync.RWMutex)
109 | mockStore.db = db
110 | return mockStore
111 | }
112 |
--------------------------------------------------------------------------------
/agent/hosts.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2017 Pani Networks
2 | // All Rights Reserved.
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may
5 | // not use this file except in compliance with the License. You may obtain
6 | // a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | // License for the specific language governing permissions and limitations
14 | // under the License.
15 |
16 | package agent
17 |
18 | import (
19 | "github.com/romana/core/common/api"
20 | )
21 |
22 | // IpamHosts is a collection of hosts with Get method.
23 | type IpamHosts []api.Host
24 |
25 | func (hosts IpamHosts) GetHost(hostname string) *api.Host {
26 | for hid, h := range hosts {
27 | if h.Name == hostname {
28 | return &hosts[hid]
29 | }
30 | }
31 | return nil
32 | }
33 |
--------------------------------------------------------------------------------
/agent/interface.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2017 Pani Networks
2 | // All Rights Reserved.
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may
5 | // not use this file except in compliance with the License. You may obtain
6 | // a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | // License for the specific language governing permissions and limitations
14 | // under the License.
15 |
16 | package agent
17 |
18 | import (
19 | "fmt"
20 | "net"
21 |
22 | log "github.com/romana/rlog"
23 | "github.com/vishvananda/netlink"
24 | "golang.org/x/sys/unix"
25 | )
26 |
27 | func CreateRomanaGW() error {
28 | rgw := &netlink.Dummy{LinkAttrs: netlink.LinkAttrs{Name: "romana-lo", TxQLen: 1000}}
29 | if err := netlink.LinkAdd(rgw); err != nil {
30 | if err == unix.EEXIST {
31 | log.Warn("Romana gateway already exists.")
32 | } else {
33 | log.Info("Error adding Romana gateway to node:", err)
34 | return err
35 | }
36 | } else {
37 | log.Info("Successfully added romana gateway to node.")
38 | }
39 |
40 | if err := netlink.LinkSetUp(rgw); err != nil {
41 | log.Error("Error while brining up romana gateway:", err)
42 | return err
43 | }
44 |
45 | return nil
46 | }
47 |
48 | func SetRomanaGwIP(romanaIp string) error {
49 | nip := net.ParseIP(romanaIp)
50 | if nip == nil {
51 | return fmt.Errorf("Failed to parse ip address %s", romanaIp)
52 | }
53 |
54 | ipnet := &net.IPNet{IP: nip, Mask: net.IPMask([]byte{0xff, 0xff, 0xff, 0xff})}
55 |
56 | link, err := netlink.LinkByName("romana-lo")
57 | if err != nil {
58 | return err
59 | }
60 |
61 | addrs, err := netlink.AddrList(link, unix.AF_INET)
62 | if err != nil {
63 | return err
64 | }
65 |
66 | for _, addr := range addrs {
67 | if addr.IPNet.String() == ipnet.String() {
68 | log.Debugf("Address %s already installed in interface %s", addr, link)
69 | return nil
70 | }
71 | }
72 |
73 | ip := &netlink.Addr{
74 | IPNet: ipnet,
75 | }
76 |
77 | err = netlink.AddrAdd(link, ip)
78 | if err != nil {
79 | return err
80 | }
81 |
82 | return nil
83 | }
84 |
--------------------------------------------------------------------------------
/agent/iptsave/iptablesSave/main.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2017 Pani Networks
2 | // All Rights Reserved.
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may
5 | // not use this file except in compliance with the License. You may obtain
6 | // a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | // License for the specific language governing permissions and limitations
14 | // under the License.
15 |
16 | package main
17 |
18 | import (
19 | "flag"
20 | "fmt"
21 | "os"
22 |
23 | "github.com/romana/core/agent/iptsave"
24 | )
25 |
26 | func main() {
27 | flag.Parse()
28 |
29 | ipt := iptsave.IPtables{}
30 | ipt.Parse(os.Stdin)
31 |
32 | fmt.Println(ipt.Render())
33 | }
34 |
--------------------------------------------------------------------------------
/agent/iptsave/iptables_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2016 Pani Networks
2 | // All Rights Reserved.
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may
5 | // not use this file except in compliance with the License. You may obtain
6 | // a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | // License for the specific language governing permissions and limitations
14 | // under the License.
15 |
16 | // Tests for iptsave library.
17 | package iptsave
18 |
19 | // Some comments on use of mocking framework in helpers_test.go.
20 |
21 | import (
22 | "bufio"
23 | "bytes"
24 | "testing"
25 | )
26 |
27 | func makeChains() (srcChain, dstChain IPchain) {
28 | dstChain = IPchain{
29 | Name: "Dest",
30 | Rules: []*IPrule{
31 | &IPrule{Action: IPtablesAction{Body: "Common"}},
32 | &IPrule{Action: IPtablesAction{Body: "uniqDest"}},
33 | &IPrule{Action: IPtablesAction{Body: "Common2"}},
34 | },
35 | }
36 |
37 | srcChain = IPchain{
38 | Name: "Src",
39 | Rules: []*IPrule{
40 | &IPrule{Action: IPtablesAction{Body: "Common"}},
41 | &IPrule{Action: IPtablesAction{Body: "Common2"}},
42 | &IPrule{Action: IPtablesAction{Body: "UniqSrc"}},
43 | },
44 | }
45 |
46 | return
47 | }
48 |
49 | func TestDiffRules(t *testing.T) {
50 | srcChain, dstChain := makeChains()
51 |
52 | uniqDest, uniqSrc, common := DiffRules(dstChain.Rules, srcChain.Rules)
53 |
54 | for _, r := range uniqSrc {
55 | t.Logf("src --> %s\n", r)
56 | }
57 |
58 | for _, r := range uniqDest {
59 | t.Logf("dest --> %s\n", r)
60 | }
61 |
62 | for _, r := range common {
63 | t.Logf("comm --> %s\n", r)
64 | }
65 |
66 | if len(uniqSrc) != 1 {
67 | t.Errorf("Expecting exactly 1 entry in source chain, got %d", len(uniqSrc))
68 | }
69 |
70 | if len(uniqDest) != 1 {
71 | t.Errorf("Expecting exactly 1 entry in dest chain, got %d", len(uniqDest))
72 | }
73 |
74 | if len(common) != 2 {
75 | t.Errorf("Expecting exactly 2 entries in common chain, got %d", len(common))
76 | }
77 |
78 | }
79 |
80 | func TestMergeRules(t *testing.T) {
81 | srcChain, dstChain := makeChains()
82 | newRules := MergeChains(&dstChain, &srcChain)
83 |
84 | for _, r := range dstChain.Rules {
85 | t.Logf("new dest --> %s\n", r)
86 | }
87 |
88 | for _, r := range newRules {
89 | t.Logf("new rules --> %s\n", r)
90 | }
91 |
92 | if len(dstChain.Rules) != 4 {
93 | t.Errorf("Expecting exactly 4 entries in dest chain, got %d", len(dstChain.Rules))
94 | }
95 |
96 | }
97 |
98 | func TestRuleParser(t *testing.T) {
99 | rule := "MYCHAIN -p tcp --dport 55 ! -p tcp --sport 80 -j TARGET"
100 | reader := bufio.NewReader(bytes.NewReader([]byte(rule)))
101 | chain := ParseRule(reader)
102 | if chain.Name != "MYCHAIN" || chain.Rules[0].String() != "-p tcp --dport 55 ! -p tcp --sport 80 -j TARGET" {
103 | t.Errorf("%s\n%s", chain.Name, chain.Rules[0].String())
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/agent/iptsave/token.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2016 Pani Networks
2 | // All Rights Reserved.
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may
5 | // not use this file except in compliance with the License. You may obtain
6 | // a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | // License for the specific language governing permissions and limitations
14 | // under the License.
15 |
16 | // Provides a definition of Item for the Lexer.
17 | package iptsave
18 |
19 | import (
20 | "fmt"
21 | )
22 |
23 | type Item struct {
24 | Type ItemType
25 | Body string
26 | }
27 |
28 | type ItemType int
29 |
30 | const (
31 | ItemError ItemType = iota
32 | ItemEOF
33 | itemComment
34 | itemTable
35 | itemChain
36 | itemChainPolicy
37 | itemChainCounter
38 | itemRule
39 | itemRuleMatch
40 | itemModule
41 | itemAction
42 | itemOptions
43 | itemCommit
44 | )
45 |
46 | func (i ItemType) String() string {
47 | switch i {
48 | case ItemError:
49 | return fmt.Sprintf("Error")
50 | case ItemEOF:
51 | return fmt.Sprintf("EOF")
52 | case itemComment:
53 | return fmt.Sprintf("Comment")
54 | case itemTable:
55 | return fmt.Sprintf("Table")
56 | case itemChain:
57 | return fmt.Sprintf("Chain")
58 | case itemChainPolicy:
59 | return fmt.Sprintf("ChainPolicy")
60 | case itemChainCounter:
61 | return fmt.Sprintf("ChainCounter")
62 | case itemRule:
63 | return fmt.Sprintf("Rule")
64 | case itemRuleMatch:
65 | return fmt.Sprintf("RuleMatch")
66 | case itemModule:
67 | return fmt.Sprintf("Module")
68 | case itemAction:
69 | return fmt.Sprintf("Action")
70 | case itemOptions:
71 | return fmt.Sprintf("Options")
72 | case itemCommit:
73 | return fmt.Sprintf("Commit")
74 | default:
75 | return fmt.Sprintf("Unknown item")
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/agent/metrics.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2017 Pani Networks
2 | // All Rights Reserved.
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may
5 | // not use this file except in compliance with the License. You may obtain
6 | // a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | // License for the specific language governing permissions and limitations
14 | // under the License.
15 |
16 | package agent
17 |
18 | import (
19 | "fmt"
20 | "net/http"
21 |
22 | "github.com/prometheus/client_golang/prometheus"
23 | "github.com/prometheus/client_golang/prometheus/promhttp"
24 | "github.com/romana/core/agent/enforcer"
25 | log "github.com/romana/rlog"
26 | )
27 |
28 | var (
29 | NumManagedRoutes = prometheus.NewGauge(
30 | prometheus.GaugeOpts{
31 | Name: "romana_managed_routes",
32 | Help: "Number of routes managed by Romana agent on the host.",
33 | },
34 | )
35 | )
36 |
37 | func MetricStart(port int) error {
38 | if port <= 0 {
39 | return nil
40 | }
41 |
42 | registry := prometheus.NewRegistry()
43 | err := enforcer.MetricsRegister(registry)
44 | if err != nil {
45 | return err
46 | }
47 |
48 | err = registry.Register(NumManagedRoutes)
49 | if err != nil {
50 | return err
51 | }
52 |
53 | handler := promhttp.HandlerFor(registry, promhttp.HandlerOpts{ErrorHandling: promhttp.HTTPErrorOnError})
54 |
55 | go func() {
56 | http.Handle("/", handler)
57 | log.Errorf("Metrics publishing stopped due to %s", http.ListenAndServe(fmt.Sprintf(":%d", port), nil))
58 | }()
59 |
60 | return nil
61 | }
62 |
--------------------------------------------------------------------------------
/agent/policycache/policy_storage.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2017 Pani Networks
2 | // All Rights Reserved.
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may
5 | // not use this file except in compliance with the License. You may obtain
6 | // a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | // License for the specific language governing permissions and limitations
14 | // under the License.
15 |
16 | package policycache
17 |
18 | import (
19 | "github.com/romana/core/agent/cache"
20 | "github.com/romana/core/common/api"
21 | )
22 |
23 | type Interface interface {
24 | Put(string, api.Policy)
25 | Get(string) (api.Policy, bool)
26 | Delete(string)
27 | List() []api.Policy
28 | Keys() []string
29 | }
30 |
31 | type PolicyStorage struct {
32 | store cache.Interface
33 | }
34 |
35 | func New() Interface {
36 | return &PolicyStorage{cache.New()}
37 | }
38 |
39 | func (p *PolicyStorage) Put(key string, policy api.Policy) {
40 | p.store.Put(key, policy)
41 | }
42 |
43 | func (p *PolicyStorage) Get(key string) (api.Policy, bool) {
44 | item, ok := p.store.Get(key)
45 | if !ok {
46 | return api.Policy{}, ok
47 | }
48 |
49 | policy, ok := item.(api.Policy)
50 | if !ok {
51 | return api.Policy{}, ok
52 | }
53 |
54 | return policy, ok
55 | }
56 |
57 | func (p *PolicyStorage) List() []api.Policy {
58 | var result []api.Policy
59 | items := p.store.List()
60 | for _, item := range items {
61 | policy, ok := item.(api.Policy)
62 | if !ok {
63 | continue
64 | }
65 | result = append(result, policy)
66 | }
67 | return result
68 | }
69 |
70 | func (p *PolicyStorage) Keys() []string {
71 | return p.store.Keys()
72 | }
73 |
74 | func (p *PolicyStorage) Delete(key string) {
75 | p.store.Delete(key)
76 | }
77 |
--------------------------------------------------------------------------------
/agent/policycontroller/controller.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2017 Pani Networks
2 | // All Rights Reserved.
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may
5 | // not use this file except in compliance with the License. You may obtain
6 | // a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | // License for the specific language governing permissions and limitations
14 | // under the License.
15 |
16 | package policycontroller
17 |
18 | import (
19 | "context"
20 | "encoding/json"
21 | "time"
22 |
23 | "github.com/romana/core/agent/policycache"
24 | "github.com/romana/core/common/api"
25 | "github.com/romana/core/common/client"
26 |
27 | "github.com/docker/libkv/store"
28 | "github.com/pkg/errors"
29 | log "github.com/romana/rlog"
30 | )
31 |
32 | const (
33 | defaultWatcherReconnectTime = 5 * time.Second
34 | )
35 |
36 | func Run(ctx context.Context, key string, client *client.Client, storage policycache.Interface) (<-chan api.Policy, error) {
37 | policies, err := client.Store.GetExt(key, store.GetOptions{Recursive: true})
38 | if err != nil {
39 | return nil, errors.Wrap(err, "controller init fail")
40 | }
41 |
42 | for _, val := range policies.GetResponse().Node.Nodes {
43 | var policy api.Policy
44 | err := json.Unmarshal([]byte(val.Value), &policy)
45 | if err != nil {
46 | return nil, errors.Wrap(err, "failed to unmarshal policy")
47 | }
48 |
49 | storage.Put(val.Key, policy)
50 | }
51 |
52 | respCh, err := client.Store.WatchExt(
53 | key, store.WatcherOptions{Recursive: true, NoList: true}, ctx.Done())
54 | if err != nil {
55 | return nil, errors.Wrap(err, "failed to start watching")
56 | }
57 |
58 | updateStorage := func(action, key string, policy api.Policy) {
59 | switch action {
60 | case "set", "update", "create", "compareAndSwap":
61 | storage.Put(key, policy)
62 | case "delete":
63 | storage.Delete(key)
64 | }
65 | }
66 |
67 | policyOut := make(chan api.Policy)
68 | var LastIndex uint64
69 | go func() {
70 | var err error
71 | for {
72 | if err != nil {
73 | log.Errorf("policy watcher store error: %s", err)
74 | // if we can't connect to the kvstore, wait for
75 | // few seconds and try reconnecting.
76 | time.Sleep(defaultWatcherReconnectTime)
77 | respCh, _ = client.Store.WatchExt(
78 | key,
79 | store.WatcherOptions{Recursive: true,
80 | NoList: true,
81 | AfterIndex: LastIndex,
82 | },
83 | ctx.Done())
84 | }
85 |
86 | select {
87 | case <-ctx.Done():
88 | log.Printf("Stopping policy watcher module.")
89 | return
90 |
91 | case resp, ok := <-respCh:
92 | if !ok || resp == nil {
93 | err = errors.New("kvstore policy events channel closed")
94 | continue
95 | }
96 |
97 | LastIndex = resp.LastIndex
98 | var p api.Policy
99 |
100 | value := resp.Value
101 | if resp.Action == "delete" {
102 | value = resp.PrevValue
103 | }
104 |
105 | if errp := json.Unmarshal([]byte(value), &p); errp != nil {
106 | log.Printf("failed to unmarshal policy %v, err=%s", value, errp)
107 | continue
108 | }
109 |
110 | updateStorage(resp.Action, resp.Key, p)
111 | policyOut <- p
112 | }
113 |
114 | }
115 | }()
116 |
117 | return policyOut, nil
118 | }
119 |
--------------------------------------------------------------------------------
/agent/policyhasher/hasher.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2016 Pani Networks
2 | // All Rights Reserved.
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may
5 | // not use this file except in compliance with the License. You may obtain
6 | // a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | // License for the specific language governing permissions and limitations
14 | // under the License.
15 |
16 | package policyhasher
17 |
18 | import (
19 | "crypto/sha1"
20 | "encoding/hex"
21 | "fmt"
22 | "strings"
23 |
24 | "github.com/romana/core/common/api"
25 | )
26 |
27 | // HashRomanaPolicies generates unique hash for a list of romana policies.
28 | func HashRomanaPolicies(policies []api.Policy) string {
29 | var hashes []string
30 | for _, policy := range policies {
31 | hashes = append(hashes, HashRomanaPolicy(policy))
32 | }
33 |
34 | return HashListOfStrings(hashes)
35 | }
36 |
37 | // HashRomanaPolicies generates sha1 hash from a canonical form of the policy.
38 | func HashRomanaPolicy(policy api.Policy) string {
39 | sorted := PolicyToCanonical(policy)
40 |
41 | var data string
42 |
43 | data = fmt.Sprintf("%s.%s.%s", policy.Direction, policy.Description, policy.ID)
44 |
45 | for _, e := range sorted.AppliedTo {
46 | data = fmt.Sprintf("%s.%s", data, EndpointToString(e))
47 | }
48 |
49 | for _, i := range sorted.Ingress {
50 | for _, e := range i.Peers {
51 | data = fmt.Sprintf("%s.%s", data, EndpointToString(e))
52 | }
53 |
54 | for _, r := range i.Rules {
55 | data = fmt.Sprintf("%s.%s%d%d%t", data, r.Protocol, r.IcmpType, r.IcmpCode, r.IsStateful)
56 |
57 | for _, p := range r.Ports {
58 | data = fmt.Sprintf("%s%d", data, p)
59 | }
60 |
61 | for _, p := range r.PortRanges {
62 | data = fmt.Sprintf("%s%d%d", data, p[0], p[1])
63 | }
64 | }
65 | }
66 |
67 | hasher := sha1.New()
68 | hasher.Write([]byte(data))
69 | sum := hasher.Sum(nil)
70 |
71 | return fmt.Sprint(hex.EncodeToString(sum))
72 |
73 | }
74 |
75 | // HashListOfStrings generates sha1 hash from a list of strings.
76 | func HashListOfStrings(hashes []string) string {
77 | data := strings.Join(hashes, "")
78 | hasher := sha1.New()
79 | hasher.Write([]byte(data))
80 | sum := hasher.Sum(nil)
81 |
82 | return fmt.Sprint(hex.EncodeToString(sum))
83 | }
84 |
--------------------------------------------------------------------------------
/agent/policyhasher/hasher_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2016 Pani Networks
2 | // All Rights Reserved.
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may
5 | // not use this file except in compliance with the License. You may obtain
6 | // a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | // License for the specific language governing permissions and limitations
14 | // under the License.
15 |
16 | // +build ignore
17 |
18 | package policyhasher
19 |
20 | import (
21 | "testing"
22 |
23 | "github.com/romana/core/common"
24 | )
25 |
26 | func TestNewEndpointList(t *testing.T) {
27 |
28 | endpointList := []common.Endpoint{
29 | common.Endpoint{
30 | Peer: "Foo",
31 | Dest: "Bar",
32 | },
33 | common.Endpoint{
34 | TenantName: "alice",
35 | TenantNetworkID: nil,
36 | },
37 | common.Endpoint{
38 | Peer: "Dead",
39 | Dest: "Beef",
40 | },
41 | }
42 |
43 | t.Logf("Before sort %v", endpointList)
44 | newList := NewEndpointList(endpointList).Sort().List()
45 | t.Logf("After sort %v", newList)
46 |
47 | if newList[2].Peer != "Foo" {
48 | t.Errorf("Unexpected result from NewEndpointList, %v", newList)
49 | }
50 | }
51 |
52 | func TestSortRules(t *testing.T) {
53 | ruleList := []common.Rule{
54 | common.Rule{
55 | Protocol: "TCP",
56 | Ports: []uint{1, 2, 3},
57 | },
58 | common.Rule{
59 | Protocol: "ICMP",
60 | IcmpType: uint(7),
61 | IcmpCode: uint(8),
62 | },
63 | common.Rule{
64 | Protocol: "TCP",
65 | PortRanges: []common.PortRange{
66 | common.PortRange{0, 9},
67 | common.PortRange{8, 2},
68 | },
69 | },
70 | common.Rule{
71 | Protocol: "UDP",
72 | Ports: []uint{6, 5, 4},
73 | },
74 | }
75 |
76 | t.Logf("Before sort %v", ruleList)
77 | newList := RulesToCanonical(ruleList)
78 | t.Logf("After sort %v", newList)
79 |
80 | if newList[3].Ports[2] != 6 {
81 | t.Errorf("Unexpected result from SortRules, %v", newList)
82 | }
83 | }
84 |
85 | func TestHashRomanaPolicies(t *testing.T) {
86 | policyList := []common.Policy{
87 | common.Policy{
88 | Direction: "ingress",
89 | Description: "test policy one",
90 | Name: "one",
91 | ExternalID: "foo",
92 | AppliedTo: []common.Endpoint{
93 | common.Endpoint{
94 | Peer: "Host",
95 | Dest: "Local",
96 | },
97 | },
98 | Ingress: []common.RomanaIngress{
99 | common.RomanaIngress{
100 | Peers: []common.Endpoint{
101 | common.Endpoint{
102 | TenantName: "Alice",
103 | SegmentName: "AliceVille",
104 | },
105 | },
106 | Rules: []common.Rule{
107 | common.Rule{
108 | Protocol: "TCP",
109 | Ports: []uint{5, 4, 3},
110 | },
111 | },
112 | },
113 | },
114 | },
115 | common.Policy{
116 | Direction: "ingress",
117 | Description: "test policy two",
118 | Name: "two",
119 | ExternalID: "bar",
120 | AppliedTo: []common.Endpoint{
121 | common.Endpoint{
122 | Peer: "Local",
123 | Dest: "Host",
124 | },
125 | },
126 | Ingress: []common.RomanaIngress{
127 | common.RomanaIngress{
128 | Peers: []common.Endpoint{
129 | common.Endpoint{
130 | TenantName: "Bob",
131 | SegmentName: "BobHold",
132 | },
133 | },
134 | Rules: []common.Rule{
135 | common.Rule{
136 | Protocol: "TCP",
137 | Ports: []uint{9, 8, 7},
138 | },
139 | },
140 | },
141 | },
142 | },
143 | }
144 |
145 | hash := HashRomanaPolicies(policyList)
146 | if hash != "fcfe27cb94edeb6cdb186192ed32e1c83374d3a5" {
147 | t.Errorf("Unexpected result from HashRomanaPolicies %s", hash)
148 | } else {
149 | t.Logf("Hash is %s", hash)
150 | }
151 | }
152 |
--------------------------------------------------------------------------------
/agent/routes.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2017 Pani Networks
2 | // All Rights Reserved.
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may
5 | // not use this file except in compliance with the License. You may obtain
6 | // a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | // License for the specific language governing permissions and limitations
14 | // under the License.
15 |
16 | package agent
17 |
18 | import (
19 | "fmt"
20 | "net"
21 |
22 | "github.com/pkg/errors"
23 | "github.com/romana/core/common/api"
24 | log "github.com/romana/rlog"
25 | "github.com/vishvananda/netlink"
26 | )
27 |
28 | // CreateRouteToBlocks loops over list of blocks and creates routes when needed.
29 | func CreateRouteToBlocks(blocks []api.IPAMBlockResponse,
30 | hosts IpamHosts,
31 | romanaRouteTableId int,
32 | hostname string,
33 | multihop bool,
34 | nlHandle nlHandleRoute) {
35 |
36 | var managedRoutes int
37 | for _, block := range blocks {
38 | if block.Host == hostname {
39 | log.Debugf("Block %v is local and does not require a route on that host", block)
40 | continue
41 | }
42 |
43 | host := hosts.GetHost(block.Host)
44 | if host == nil {
45 | log.Debugf("Block %v belongs to unknown host %s, ignoring", block, block.Host)
46 | continue
47 | }
48 |
49 | if err := createRouteToBlock(block, host, romanaRouteTableId, multihop, nlHandle); err != nil {
50 | _, ok := err.(RouteAdjacencyError)
51 | if ok {
52 | // Lower severity for expected error
53 | log.Tracef(4, "%s", err)
54 | continue
55 | }
56 |
57 | log.Errorf("%s", err)
58 | continue
59 | }
60 | managedRoutes += 1
61 | }
62 |
63 | NumManagedRoutes.Set(float64(managedRoutes))
64 | }
65 |
66 | type nlHandleRoute interface {
67 | RouteGet(net.IP) ([]netlink.Route, error)
68 | RouteAdd(*netlink.Route) error
69 | }
70 |
71 | // createRouteToBlock creates ip route for given block->host pair in Romana routing table,
72 | // the function will fail if requested block is not directly adjacent and multihop false.
73 | func createRouteToBlock(block api.IPAMBlockResponse, host *api.Host, romanaRouteTableId int, multihop bool, nlHandle nlHandleRoute) error {
74 | testRoutes, err := nlHandle.RouteGet(host.IP)
75 | if err != nil {
76 | return errors.Wrapf(err, "couldn't test host %s adjacency", host.IP)
77 | }
78 |
79 | if len(testRoutes) > 1 {
80 | return errors.New(fmt.Sprintf("more then one path available for host %s, multipath not currently supported", host.IP))
81 | }
82 |
83 | if len(testRoutes) == 0 {
84 | return errors.New(fmt.Sprintf("no way to reach %s, no default gateway?", host.IP))
85 | }
86 |
87 | if testRoutes[0].Gw != nil && multihop == false {
88 | return RouteAdjacencyError{}
89 | }
90 |
91 | route := netlink.Route{
92 | Dst: &block.CIDR.IPNet,
93 | Gw: host.IP,
94 | Table: romanaRouteTableId,
95 | }
96 |
97 | log.Debugf("About to create route %v", route)
98 | return nlHandle.RouteAdd(&route)
99 | }
100 |
101 | type RouteAdjacencyError struct{}
102 |
103 | func (RouteAdjacencyError) Error() string {
104 | return "no directly adjacent route and multihop is prohibited"
105 | }
106 |
--------------------------------------------------------------------------------
/agent/rtable/route_table.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2017 Pani Networks
2 | // All Rights Reserved.
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may
5 | // not use this file except in compliance with the License. You may obtain
6 | // a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | // License for the specific language governing permissions and limitations
14 | // under the License.
15 |
16 | package rtable
17 |
18 | import (
19 | "bufio"
20 | "fmt"
21 | "os"
22 | "os/exec"
23 |
24 | "github.com/pkg/errors"
25 | "github.com/romana/rlog"
26 | "github.com/vishvananda/netlink"
27 | "golang.org/x/sys/unix"
28 | )
29 |
30 | const (
31 | RT_TABLES_FILE = "/etc/iproute2/rt_tables"
32 | )
33 |
34 | // EnsureRouteTableExist verifies that romana route table with appropriate index
35 | // exist in RT_TABLES_FILE file.
36 | func EnsureRouteTableExist(routeTableId int) (err error) {
37 |
38 | file, err := os.OpenFile(RT_TABLES_FILE, os.O_RDWR, 0644)
39 | if err != nil {
40 | return errors.Wrapf(err,
41 | "failed to open file %s to ensure that romana table name is configured",
42 | RT_TABLES_FILE)
43 | }
44 | defer func() {
45 | if err2 := file.Close(); err2 != nil {
46 | err = errors.Wrapf(err, "couldn't close the file %s", err)
47 | }
48 | }()
49 |
50 | tableName := "romana"
51 | err = ensureRouteTableName(file, tableName, routeTableId)
52 | if err != nil {
53 | return errors.Wrap(err, "couldn't verify that romana table name is configured")
54 | }
55 | return nil
56 |
57 | }
58 |
59 | func ensureRouteTableName(file *os.File, tableName string, routeTableId int) error {
60 | targetEntry := fmt.Sprintf("%d %s", routeTableId, tableName)
61 |
62 | scanner := bufio.NewScanner(file)
63 | for scanner.Scan() {
64 | if scanner.Text() == targetEntry {
65 | return nil
66 | }
67 | }
68 |
69 | _, err := file.Write([]byte(targetEntry + "\n"))
70 | if err != nil {
71 | return err
72 | }
73 |
74 | return nil
75 | }
76 |
77 | // nlRuleHandle subset of netlink.Handle methods isolated for mocking.
78 | type nlRuleHandle interface {
79 | RuleList(family int) ([]netlink.Rule, error)
80 | RuleAdd(*netlink.Rule) error
81 | }
82 |
83 | // EnsureRomanaRouteRule verifies that rule for romana routing table installed.
84 | func EnsureRomanaRouteRule(romanaRouteTableId int, nl nlRuleHandle) error {
85 | rules, err := nl.RuleList(unix.AF_INET)
86 | if err != nil {
87 | return err
88 | }
89 |
90 | for _, rule := range rules {
91 | if rule.Table == romanaRouteTableId {
92 | return nil
93 | }
94 | }
95 |
96 | inRule := netlink.NewRule()
97 | inRule.Table = romanaRouteTableId
98 |
99 | rlog.Infof("Adding routing rule %v", inRule)
100 | err = nl.RuleAdd(inRule)
101 | if err != nil {
102 | return err
103 | }
104 |
105 | return nil
106 | }
107 |
108 | // FlushRomanaTable attempts to delete all routes from table called romana.
109 | func FlushRomanaTable() error {
110 | command := exec.Command("ip", "ro", "flush", "table", "romana")
111 |
112 | out, err := command.CombinedOutput()
113 | if err != nil {
114 | return fmt.Errorf("failed to flush romana route table out=%s, err=%s", string(out), err)
115 | }
116 |
117 | return nil
118 |
119 | }
120 |
--------------------------------------------------------------------------------
/agent/rtable/testdata/rt_tables.case1:
--------------------------------------------------------------------------------
1 | #
2 | # reserved values
3 | #
4 | 255 local
5 | 254 main
6 | 253 default
7 | 0 unspec
8 | #
9 | # local
10 | #
11 | #1 inr.ruhep
12 |
--------------------------------------------------------------------------------
/agent/rtable/testdata/rt_tables.golden:
--------------------------------------------------------------------------------
1 | #
2 | # reserved values
3 | #
4 | 255 local
5 | 254 main
6 | 253 default
7 | 0 unspec
8 | #
9 | # local
10 | #
11 | #1 inr.ruhep
12 | 10 romana
13 |
--------------------------------------------------------------------------------
/agent/sysctl/sysctl.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2017 Pani Networks
2 | // All Rights Reserved.
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may
5 | // not use this file except in compliance with the License. You may obtain
6 | // a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | // License for the specific language governing permissions and limitations
14 | // under the License.
15 |
16 | package sysctl
17 |
18 | import (
19 | "bytes"
20 | "io/ioutil"
21 | "strings"
22 | )
23 |
24 | // Set systcl value to 1, the specified path has to start with /proc/sys.
25 | // Example: /proc/sys/net/ipv4/conf/default/proxy_arp
26 | func Set(path string) error {
27 | if !strings.HasPrefix(path, "/proc/sys") {
28 | return errorSetBoundary("systl.Set() called with path outside of /proc/sys")
29 | }
30 |
31 | return ioutil.WriteFile(path, []byte("1"), 0644)
32 | }
33 |
34 | // errorSetBoundary indicates that Set function got called
35 | // with invalid path.
36 | type errorSetBoundary string
37 |
38 | func (e errorSetBoundary) Error() string { return string(e) }
39 |
40 | // Check that sysctl value is 1, the specified path has to start with /proc/sys.
41 | // For paths outside of /proc/sys result is undetermented.
42 | func Check(path string) (bool, error) {
43 | data, err := ioutil.ReadFile(path)
44 | if err != nil {
45 | return false, err
46 | }
47 |
48 | if bytes.Equal(data, []byte("1\n")) {
49 | return true, nil
50 | }
51 |
52 | return false, nil
53 | }
54 |
--------------------------------------------------------------------------------
/agent/sysctl/sysctl_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2017 Pani Networks
2 | // All Rights Reserved.
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may
5 | // not use this file except in compliance with the License. You may obtain
6 | // a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | // License for the specific language governing permissions and limitations
14 | // under the License.
15 |
16 | package sysctl
17 |
18 | import (
19 | "path/filepath"
20 | "testing"
21 | )
22 |
23 | const testFileDir = "testdata"
24 |
25 | func TestCheck(t *testing.T) {
26 | cases := []struct {
27 | name, path string
28 | expect, expectErr bool
29 | }{
30 | {"check positive", "sysctl.golden", true, false},
31 | {"check negative", "sysctl.zero", false, false},
32 | {"check fail", "none", false, true},
33 | }
34 |
35 | for _, tc := range cases {
36 | t.Run(tc.name, func(t *testing.T) {
37 | ok, err := Check(filepath.Join(testFileDir, tc.path))
38 | if err != nil && tc.expectErr == false {
39 | t.Errorf("Received unexpected error %s", err)
40 | }
41 |
42 | if ok != tc.expect {
43 | t.Errorf("Unexpected return value, expected %t, got %t", tc.expect, ok)
44 | }
45 | })
46 | }
47 | }
48 |
49 | func TestSet(t *testing.T) {
50 | err := Set("/tmp/doesnotexist")
51 | if err == nil {
52 | t.Fatalf("didn't fail when expected")
53 | }
54 |
55 | if _, ok := err.(errorSetBoundary); !ok {
56 | t.Fatalf("received unexpected error type %s.(%T)", err, err)
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/agent/sysctl/testdata/sysctl.golden:
--------------------------------------------------------------------------------
1 | 1
2 |
--------------------------------------------------------------------------------
/agent/sysctl/testdata/sysctl.zero:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/romana/core/6139692fadcb6b4b67767576809c223f557aef47/agent/sysctl/testdata/sysctl.zero
--------------------------------------------------------------------------------
/cli/adaptor/client.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2016 Pani Networks
2 | // All Rights Reserved.
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may
5 | // not use this file except in compliance with the License. You may obtain
6 | // a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | // License for the specific language governing permissions and limitations
14 | // under the License.
15 |
16 | // Package adaptor implements glue code for multiple
17 | // platforms like openstack, kubernetes, etc. It
18 | // interfaces directly with REST API for them and
19 | // allows same interface for other command line tools.
20 | package adaptor
21 |
22 | import (
23 | "github.com/romana/core/cli/kubernetes"
24 | "github.com/romana/core/cli/openstack"
25 | "github.com/romana/core/cli/util"
26 |
27 | config "github.com/spf13/viper"
28 | )
29 |
30 | // GetTenantName returns platform specific tenant name
31 | // corresponding to the UUID being used in romana tenants.
32 | func GetTenantName(uuid string) (string, error) {
33 | if platform := config.GetString("Platform"); platform == "kubernetes" {
34 | return kubernetes.GetTenantName(uuid)
35 | } else if platform == "openstack" {
36 | return openstack.GetTenantName(uuid)
37 | } else if platform == "mesos" {
38 | return "", util.ErrUnimplementedPlatform
39 | }
40 | return "", util.ErrInvalidPlatform
41 | }
42 |
43 | // TenantExists returns true/false depending on platform
44 | // specific tenant name or uuid exists or not.
45 | func TenantExists(name string) bool {
46 | if platform := config.GetString("Platform"); platform == "kubernetes" {
47 | return kubernetes.TenantExists(name)
48 | } else if platform == "openstack" {
49 | return openstack.TenantExists(name)
50 | } else if platform == "mesos" {
51 | // Unimplemented Platform.
52 | return false
53 | }
54 | // Error
55 | return false
56 | }
57 |
58 | // GetTenantUUID returns platform specific tenant UUID
59 | // corresponding to the name.
60 | func GetTenantUUID(name string) (string, error) {
61 | if platform := config.GetString("Platform"); platform == "kubernetes" {
62 | return kubernetes.GetTenantUUID(name)
63 | } else if platform == "openstack" {
64 | return openstack.GetTenantUUID(name)
65 | } else if platform == "mesos" {
66 | return "", util.ErrUnimplementedPlatform
67 | }
68 | return "", util.ErrInvalidPlatform
69 | }
70 |
71 | // CreateTenant creates platform specific tenant
72 | // corresponding to the name given.
73 | func CreateTenant(name string) error {
74 | if platform := config.GetString("Platform"); platform == "kubernetes" {
75 | return kubernetes.CreateTenant(name)
76 | } else if platform == "openstack" {
77 | return openstack.CreateTenant(name)
78 | } else if platform == "mesos" {
79 | return util.ErrUnimplementedPlatform
80 | }
81 | return util.ErrInvalidPlatform
82 | }
83 |
--------------------------------------------------------------------------------
/cli/commands/docs.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2018 Pani Networks
2 | // All Rights Reserved.
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may
5 | // not use this file except in compliance with the License. You may obtain
6 | // a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | // License for the specific language governing permissions and limitations
14 | // under the License.
15 |
16 | // Package commands contains various files for adding commands and subcommands
17 | // to romana command line tools.
18 | //
19 | // Each file in the package directory contains exactly one command and
20 | // multiple sub-commands for it, except the root.go file which initializes
21 | // the config and cli package and sets various command line parameters.
22 | //
23 | // The package used for configuration is called viper and it allows
24 | // configuration to be read from configuration files or to be set from
25 | // command line parameters.
26 | //
27 | // The package used for command line interface is called cobra and it
28 | // is the main library behind providing usage, flag support and various
29 | // other command line features.
30 | //
31 | package commands
32 |
--------------------------------------------------------------------------------
/cli/commands/host.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2016 Pani Networks
2 | // All Rights Reserved.
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may
5 | // not use this file except in compliance with the License. You may obtain
6 | // a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | // License for the specific language governing permissions and limitations
14 | // under the License.
15 |
16 | package commands
17 |
18 | import (
19 | "encoding/json"
20 | "fmt"
21 | "net/http"
22 | "os"
23 | "text/tabwriter"
24 |
25 | "github.com/romana/core/common/api"
26 |
27 | "github.com/go-resty/resty"
28 | cli "github.com/spf13/cobra"
29 | config "github.com/spf13/viper"
30 | )
31 |
32 | // hostCmd represents the host commands
33 | var hostCmd = &cli.Command{
34 | Use: "host [add|show|list|remove]",
35 | Short: "Add, Remove or Show hosts for romana services.",
36 | Long: `Add, Remove or Show hosts for romana services.
37 |
38 | host requires a subcommand, e.g. ` + "`romana host add`." + `
39 |
40 | For more information, please check http://romana.io
41 | `,
42 | }
43 |
44 | func init() {
45 | hostCmd.AddCommand(hostAddCmd)
46 | hostCmd.AddCommand(hostShowCmd)
47 | hostCmd.AddCommand(hostListCmd)
48 | hostCmd.AddCommand(hostRemoveCmd)
49 | }
50 |
51 | var hostAddCmd = &cli.Command{
52 | Use: "add [hostip][(optional)romana cidr][(optional)agent port]",
53 | Short: "Add a new host.",
54 | Long: `Add a new host.`,
55 | RunE: hostAdd,
56 | SilenceUsage: true,
57 | }
58 |
59 | var hostShowCmd = &cli.Command{
60 | Use: "show [hostip1][hostip2]...",
61 | Short: "Show details for a specific host.",
62 | Long: `Show details for a specific host.`,
63 | RunE: hostShow,
64 | SilenceUsage: true,
65 | }
66 |
67 | var hostListCmd = &cli.Command{
68 | Use: "list",
69 | Short: "List all hosts.",
70 | Long: `List all hosts.`,
71 | RunE: hostList,
72 | SilenceUsage: true,
73 | }
74 |
75 | var hostRemoveCmd = &cli.Command{
76 | Use: "remove [hostip]",
77 | Short: "Remove a host.",
78 | Long: `Remove a host.`,
79 | RunE: hostRemove,
80 | SilenceUsage: true,
81 | }
82 |
83 | func hostAdd(cmd *cli.Command, args []string) error {
84 | fmt.Println("Unimplemented: Add host/s.")
85 | return nil
86 | }
87 |
88 | func hostShow(cmd *cli.Command, args []string) error {
89 | fmt.Println("Unimplemented: Show host details.")
90 | return nil
91 | }
92 |
93 | func hostList(cmd *cli.Command, args []string) error {
94 | rootURL := config.GetString("RootURL")
95 | resp, err := resty.R().Get(rootURL + "/hosts")
96 | if err != nil {
97 | return err
98 | }
99 |
100 | if config.GetString("Format") == "json" {
101 | JSONFormat(resp.Body(), os.Stdout)
102 | } else {
103 | w := tabwriter.NewWriter(os.Stdout, 0, 8, 0, '\t', 0)
104 |
105 | if resp.StatusCode() == http.StatusOK {
106 | var hosts api.HostList
107 | err := json.Unmarshal(resp.Body(), &hosts)
108 | if err == nil {
109 | fmt.Println("Host List")
110 | fmt.Fprintf(w, "Host IP\tHost Name\n")
111 | for _, host := range hosts.Hosts {
112 | fmt.Fprintf(w, "%s\t%s\n",
113 | host.IP.String(),
114 | host.Name,
115 | )
116 | }
117 | } else {
118 | fmt.Printf("Error: %s \n", err)
119 | }
120 | } else {
121 | var e Error
122 | json.Unmarshal(resp.Body(), &e)
123 |
124 | fmt.Println("Host Error")
125 | fmt.Fprintf(w, "Fields\t%s\n", e.Fields)
126 | fmt.Fprintf(w, "Message\t%s\n", e.Message)
127 | fmt.Fprintf(w, "Status\t%d\n", resp.StatusCode())
128 | }
129 | w.Flush()
130 | }
131 |
132 | return nil
133 | }
134 |
135 | func hostRemove(cmd *cli.Command, args []string) error {
136 | fmt.Println("Unimplemented: Remove a host.")
137 | return nil
138 | }
139 |
--------------------------------------------------------------------------------
/cli/kubernetes/tenant.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2016 Pani Networks
2 | // All Rights Reserved.
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may
5 | // not use this file except in compliance with the License. You may obtain
6 | // a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | // License for the specific language governing permissions and limitations
14 | // under the License.
15 |
16 | // Package kubernetes implements kubernetes API specific
17 | // helper functions.
18 | package kubernetes
19 |
20 | import (
21 | "github.com/romana/core/cli/util"
22 | )
23 |
24 | // GetTenantName returns kubernetes tenant name corresponding
25 | // to the UUID being used in romana tenants.
26 | func GetTenantName(uid string) (string, error) {
27 | return "", util.ErrUnimplementedFeature
28 | }
29 |
30 | // TenantExists returns true/false depending on
31 | // kubernetes tenant name or uuid exists.
32 | func TenantExists(name string) bool {
33 | // Unimplemented
34 | return false
35 | }
36 |
37 | // GetTenantUUID returns kubernetes tenant
38 | // UUID corresponding to the name.
39 | func GetTenantUUID(name string) (string, error) {
40 | return "", util.ErrUnimplementedFeature
41 | }
42 |
43 | // CreateTenant creates kubernetes specific tenant
44 | // corresponding to the name given.
45 | func CreateTenant(name string) error {
46 | return util.ErrUnimplementedFeature
47 | }
48 |
--------------------------------------------------------------------------------
/cli/romana.sample.yaml:
--------------------------------------------------------------------------------
1 | {
2 | #
3 | # Default romana configuration file.
4 | # please move it to ~/.romana.yaml
5 | #
6 | "RootURL": "http://192.168.99.10:9600",
7 | "LogFile": "/var/tmp/romana-cli.log",
8 | "Format": "table", # options are table/json
9 | "Platform": "kubernetes", # options are openstack/kubernetes
10 | "Verbose": false,
11 | }
12 |
--------------------------------------------------------------------------------
/cli/util/error.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2016 Pani Networks
2 | // All Rights Reserved.
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may
5 | // not use this file except in compliance with the License. You may obtain
6 | // a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | // License for the specific language governing permissions and limitations
14 | // under the License.
15 |
16 | // Package util contains common utility functions.
17 | package util
18 |
19 | import (
20 | "errors"
21 | )
22 |
23 | var (
24 | ErrTenantNotFound = errors.New("tenant not found")
25 | ErrUnimplementedFeature = errors.New("unimplemented feature")
26 | ErrInvalidPlatform = errors.New("invalid platform")
27 | ErrUnimplementedPlatform = errors.New("unimplemented platform")
28 | )
29 |
--------------------------------------------------------------------------------
/cli/util/helpers.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2016 Pani Networks
2 | // All Rights Reserved.
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may
5 | // not use this file except in compliance with the License. You may obtain
6 | // a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | // License for the specific language governing permissions and limitations
14 | // under the License.
15 |
16 | // Package util contains common utility functions.
17 | package util
18 |
19 | import (
20 | "bytes"
21 | "encoding/json"
22 | "fmt"
23 |
24 | cli "github.com/spf13/cobra"
25 | )
26 |
27 | // UsageError shows command line help for errors caused due
28 | // to few or more arguments being passed to the commands or
29 | // sub-commands of romana command line tools.
30 | func UsageError(cmd *cli.Command, format string, args ...interface{}) error {
31 | return fmt.Errorf("%s\nCheck '%s -h' for help",
32 | fmt.Sprintf(format, args...),
33 | cmd.CommandPath())
34 | }
35 |
36 | // JSONIndent indents the JSON input string, return input string if it fails.
37 | func JSONIndent(inStr string) string {
38 | var out bytes.Buffer
39 | err := json.Indent(&out, []byte(inStr), "", "\t")
40 | if err != nil {
41 | return inStr
42 | }
43 | return out.String()
44 | }
45 |
--------------------------------------------------------------------------------
/cmd/romana/main.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2016 Pani Networks
2 | // All Rights Reserved.
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may
5 | // not use this file except in compliance with the License. You may obtain
6 | // a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | // License for the specific language governing permissions and limitations
14 | // under the License.
15 |
16 | // Package main implements command line tools for various romana services.
17 | package main
18 |
19 | import (
20 | "github.com/romana/core/cli/commands"
21 | )
22 |
23 | func main() {
24 | commands.Execute()
25 | }
26 |
--------------------------------------------------------------------------------
/cmd/romana_cni/main.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2017 Pani Networks
2 | // All Rights Reserved.
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may
5 | // not use this file except in compliance with the License. You may obtain
6 | // a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | // License for the specific language governing permissions and limitations
14 | // under the License.
15 |
16 | package main
17 |
18 | import (
19 | "github.com/romana/core/cni"
20 |
21 | "github.com/containernetworking/cni/pkg/skel"
22 | "github.com/containernetworking/cni/pkg/version"
23 | )
24 |
25 | func main() {
26 | skel.PluginMain(cni.CmdAdd, cni.CmdDel, version.All)
27 | }
28 |
--------------------------------------------------------------------------------
/cmd/romana_doc/main.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2015 Pani Networks
2 | // All Rights Reserved.
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may
5 | // not use this file except in compliance with the License. You may obtain
6 | // a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | // License for the specific language governing permissions and limitations
14 | // under the License.
15 |
16 | package main
17 |
18 | import (
19 | "fmt"
20 | "io/ioutil"
21 | "log"
22 | "os"
23 |
24 | "github.com/romana/core/common"
25 | "github.com/romana/core/doc/tools"
26 | "github.com/romana/core/server"
27 | )
28 |
29 | func main() {
30 |
31 | if len(os.Args) != 2 {
32 | log.Fatalf("%s ", os.Args[0])
33 | }
34 | path := os.Args[1]
35 | log.Printf("Analyzing %s", path)
36 | a := tools.NewAnalyzer(path)
37 | a.Analyze()
38 |
39 | // TODO this can be introspectable as well
40 | // serviceInterfaceName := "github.com/romana/core/common.Service"
41 | // implementors := a.FindImplementors(serviceInterfaceName)
42 | // log.Printf("The following implement the %s interface: %+v", serviceInterfaceName, implementors)
43 |
44 | services := []common.Service{&server.Romanad{}}
45 | for _, service := range services {
46 | rd := tools.NewSwaggerer(a, service)
47 | json, err := rd.Process()
48 | if err != nil {
49 | panic(err)
50 | }
51 | serviceName := service.Name()
52 | dir := fmt.Sprintf("doc/%s", serviceName)
53 | err = os.MkdirAll(dir, os.ModeDir|os.ModePerm)
54 | if err != nil {
55 | panic(err)
56 | }
57 | fname := fmt.Sprintf("%s/%s.yaml", dir, serviceName)
58 | err = ioutil.WriteFile(fname, json, 0644)
59 | if err != nil {
60 | panic(err)
61 | }
62 | log.Printf("Wrote %s", fname)
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/cmd/romana_listener/main.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2016-2017 Pani Networks
2 | // All Rights Reserved.
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may
5 | // not use this file except in compliance with the License. You may obtain
6 | // a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | // License for the specific language governing permissions and limitations
14 | // under the License.
15 |
16 | // Command for running the Kubernetes Listener.
17 | package main
18 |
19 | import (
20 | "flag"
21 | "fmt"
22 |
23 | "os"
24 | "strings"
25 |
26 | "github.com/romana/core/common"
27 | "github.com/romana/core/common/client"
28 | "github.com/romana/core/listener"
29 | log "github.com/romana/rlog"
30 | )
31 |
32 | func main() {
33 | endpointsStr := flag.String("etcd-endpoints", client.DefaultEtcdEndpoints, "Comma-separated list of etcd endpoints.")
34 | host := flag.String("host", "localhost", "Host to listen on.")
35 | port := flag.Int("port", 9602, "Port to listen on.")
36 | prefix := flag.String("etcd-prefix", client.DefaultEtcdPrefix, "Prefix to use for etcd data.")
37 | flag.Parse()
38 |
39 | fmt.Println(common.BuildInfo())
40 |
41 | if endpointsStr == nil {
42 | log.Errorf("No etcd endpoints specified")
43 | os.Exit(1)
44 | }
45 | endpoints := strings.Split(*endpointsStr, ",")
46 | listener := &listener.KubeListener{Addr: fmt.Sprintf("%s:%d", *host, *port)}
47 |
48 | pr := *prefix
49 | if !strings.HasPrefix(pr, "/") {
50 | pr = "/" + pr
51 | }
52 | config := common.Config{EtcdEndpoints: endpoints,
53 | EtcdPrefix: pr,
54 | }
55 | svcInfo, err := common.InitializeService(listener, config)
56 | if err != nil {
57 | log.Error(err)
58 | os.Exit(2)
59 | }
60 | if svcInfo != nil {
61 | for {
62 | msg := <-svcInfo.Channel
63 | log.Info(msg)
64 | }
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/cmd/romana_route_publisher/examples/endpoint-networks.template:
--------------------------------------------------------------------------------
1 | {{/*
2 | This is a template to that can be rendered by
3 | Romana route publisher into bird.conf
4 |
5 | It will export list of CIDRs (blocks) which
6 | are served by the current host.
7 | */}}
8 | protocol static static_bgp {
9 | {{range .Networks}}
10 | route {{.}} reject;
11 | {{end}}
12 | }
13 |
14 | protocol bgp bgp_def {
15 | export where proto = "static_bgp";
16 | direct;
17 | local as {{.LocalAS}};
18 | neighbor 192.168.99.1 as {{.LocalAS}};
19 | }
20 |
--------------------------------------------------------------------------------
/cmd/romana_route_publisher/examples/host-groups.template:
--------------------------------------------------------------------------------
1 | {{/*
2 | This is a template to that can be rendered by
3 | Romana route publisher into bird.conf
4 |
5 | It will export list of CIDRs (prefix groups)
6 | which current host is a part of.
7 | */}}
8 | {{ $hg := index .Args "HostGroups" -}}
9 |
10 | protocol static static_bgp {
11 | {{ range $net, $group := $hg }}
12 | # Network {{ $net }}
13 | route {{ $group.CIDR }} reject;
14 | {{ end }}
15 | }
16 |
17 | protocol bgp bgp_def {
18 | export where proto = "static_bgp";
19 | direct;
20 | local as {{.LocalAS}};
21 | neighbor 192.168.99.1 as {{.LocalAS}};
22 | }
23 |
--------------------------------------------------------------------------------
/cmd/romana_route_publisher/routes.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2017 Pani Networks
2 | // All Rights Reserved.
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may
5 | // not use this file except in compliance with the License. You may obtain
6 | // a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | // License for the specific language governing permissions and limitations
14 | // under the License.
15 |
16 | package main
17 |
18 | import (
19 | "net"
20 |
21 | "github.com/romana/core/common/api"
22 | "github.com/romana/core/routepublisher/publisher"
23 |
24 | log "github.com/romana/rlog"
25 | )
26 |
27 | // createRouteToBlocks loops over list of blocks and creates routes when needed.
28 | func createRouteToBlocks(blocks []api.IPAMBlockResponse, args map[string]interface{}, hostname string, bird publisher.Interface) {
29 | var networks []net.IPNet
30 |
31 | for _, block := range blocks {
32 | if block.Host != hostname {
33 | log.Tracef(4, "Block %v is remote and should not be advertised", block)
34 | continue
35 | }
36 |
37 | networks = append(networks, block.CIDR.IPNet)
38 | }
39 |
40 | err := bird.Update(networks, args)
41 | if err != nil {
42 | log.Error(err)
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/cmd/romanad/main.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2016 Pani Networks
2 | // All Rights Reserved.
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may
5 | // not use this file except in compliance with the License. You may obtain
6 | // a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | // License for the specific language governing permissions and limitations
14 | // under the License.
15 |
16 | // Command for running the IPAM service.
17 | package main
18 |
19 | import (
20 | "flag"
21 | "fmt"
22 |
23 | "os"
24 | "strings"
25 |
26 | "github.com/romana/core/common"
27 | "github.com/romana/core/common/client"
28 | "github.com/romana/core/server"
29 | log "github.com/romana/rlog"
30 | )
31 |
32 | func main() {
33 | endpointsStr := flag.String("etcd-endpoints", client.DefaultEtcdEndpoints, "Comma-separated list of etcd endpoints.")
34 | host := flag.String("host", "localhost", "Host to listen on.")
35 | port := flag.Int("port", 9600, "Port to listen on.")
36 | prefix := flag.String("etcd-prefix", client.DefaultEtcdPrefix, "Prefix to use for etcd data.")
37 | topologyFile := flag.String("initial-topology-file", "", "Initial topology")
38 | flag.Parse()
39 |
40 | fmt.Println(common.BuildInfo())
41 |
42 | if endpointsStr == nil {
43 | log.Errorf("No etcd endpoints specified")
44 | os.Exit(1)
45 | }
46 | endpoints := strings.Split(*endpointsStr, ",")
47 | romanad := &server.Romanad{Addr: fmt.Sprintf("%s:%d", *host, *port)}
48 |
49 | pr := *prefix
50 | if !strings.HasPrefix(pr, "/") {
51 | pr = "/" + pr
52 | }
53 |
54 | config := common.Config{EtcdEndpoints: endpoints,
55 | EtcdPrefix: pr,
56 | InitialTopologyFile: topologyFile,
57 | }
58 | svcInfo, err := common.InitializeService(romanad, config)
59 | if err != nil {
60 | log.Error(err)
61 | os.Exit(3)
62 | }
63 | if svcInfo != nil {
64 | for {
65 | msg := <-svcInfo.Channel
66 | log.Info(msg)
67 | }
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/cni/cnilink.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2017 Pani Networks
2 | // All Rights Reserved.
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may
5 | // not use this file except in compliance with the License. You may obtain
6 | // a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | // License for the specific language governing permissions and limitations
14 | // under the License.
15 |
16 | package cni
17 |
18 | import (
19 | "fmt"
20 | "net"
21 |
22 | "github.com/containernetworking/cni/pkg/ns"
23 | "github.com/vishvananda/netlink"
24 | )
25 |
26 | //
27 | // TODO
28 | // Warning!
29 | // Functions in this file are ported over from containernetworking/cni/ip/link.go
30 | // for a reason that we can't use ip.SetupVeth with random hostVeth name.
31 | //
32 |
33 | // SetupVeth sets up a pair of virtual ethernet devices.
34 | // Call SetupVeth from inside the container netns. It will create both veth
35 | // devices and move the host-side veth into the provided hostNS namespace.
36 | // On success, SetupVeth returns (hostVeth, containerVeth, nil)
37 | func SetupVeth(contVethName, hostVethName string, mtu int, hostNS ns.NetNS) (net.Interface, net.Interface, error) {
38 | contVeth, err := makeVethPair(contVethName, hostVethName, mtu)
39 | if err != nil {
40 | return net.Interface{}, net.Interface{}, err
41 | }
42 |
43 | if err = netlink.LinkSetUp(contVeth); err != nil {
44 | return net.Interface{}, net.Interface{}, fmt.Errorf("failed to set %q up: %v", contVethName, err)
45 | }
46 |
47 | hostVeth, err := netlink.LinkByName(hostVethName)
48 | if err != nil {
49 | return net.Interface{}, net.Interface{}, fmt.Errorf("failed to lookup %q: %v", hostVethName, err)
50 | }
51 |
52 | if err = netlink.LinkSetNsFd(hostVeth, int(hostNS.Fd())); err != nil {
53 | return net.Interface{}, net.Interface{}, fmt.Errorf("failed to move veth to host netns: %v", err)
54 | }
55 |
56 | err = hostNS.Do(func(_ ns.NetNS) error {
57 | hostVeth, err = netlink.LinkByName(hostVethName)
58 | if err != nil {
59 | return fmt.Errorf("failed to lookup %q in %q: %v", hostVethName, hostNS.Path(), err)
60 | }
61 |
62 | if err = netlink.LinkSetUp(hostVeth); err != nil {
63 | return fmt.Errorf("failed to set %q up: %v", hostVethName, err)
64 | }
65 | return nil
66 | })
67 | if err != nil {
68 | return net.Interface{}, net.Interface{}, err
69 | }
70 | return ifaceFromNetlinkLink(hostVeth), ifaceFromNetlinkLink(contVeth), nil
71 | }
72 |
73 | func makeVethPair(name, peer string, mtu int) (netlink.Link, error) {
74 | veth := &netlink.Veth{
75 | LinkAttrs: netlink.LinkAttrs{
76 | Name: name,
77 | Flags: net.FlagUp,
78 | MTU: mtu,
79 | },
80 | PeerName: peer,
81 | }
82 | if err := netlink.LinkAdd(veth); err != nil {
83 | return nil, err
84 | }
85 |
86 | return veth, nil
87 | }
88 |
89 | func ifaceFromNetlinkLink(l netlink.Link) net.Interface {
90 | a := l.Attrs()
91 | return net.Interface{
92 | Index: a.Index,
93 | MTU: a.MTU,
94 | Name: a.Name,
95 | HardwareAddr: a.HardwareAddr,
96 | Flags: a.Flags,
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/cni/divert_rules.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2016 Pani Networks
2 | // All Rights Reserved.
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may
5 | // not use this file except in compliance with the License. You may obtain
6 | // a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | // License for the specific language governing permissions and limitations
14 | // under the License.
15 |
16 | package cni
17 |
18 | import (
19 | "github.com/romana/core/agent/iptsave"
20 | )
21 |
22 | func MakeDivertRules(nodename string, op iptsave.RenderState) []*iptsave.IPchain {
23 | return []*iptsave.IPchain{
24 | &iptsave.IPchain{
25 | Name: "INPUT",
26 | Policy: "-",
27 | Rules: []*iptsave.IPrule{
28 | &iptsave.IPrule{
29 | RenderState: op,
30 | Match: []*iptsave.Match{
31 | &iptsave.Match{
32 | Body: "-i " + nodename,
33 | },
34 | },
35 | Action: iptsave.IPtablesAction{
36 | Type: iptsave.ActionDefault,
37 | Body: "ROMANA-INPUT",
38 | },
39 | },
40 | },
41 | },
42 | &iptsave.IPchain{
43 | Name: "FORWARD",
44 | Policy: "-",
45 | Rules: []*iptsave.IPrule{
46 | &iptsave.IPrule{
47 | RenderState: op,
48 | Match: []*iptsave.Match{
49 | &iptsave.Match{
50 | Body: "-i " + nodename,
51 | },
52 | },
53 | Action: iptsave.IPtablesAction{
54 | Type: iptsave.ActionDefault,
55 | Body: "ROMANA-FORWARD-OUT",
56 | },
57 | },
58 | },
59 | },
60 | &iptsave.IPchain{
61 | Name: "FORWARD",
62 | Policy: "-",
63 | Rules: []*iptsave.IPrule{
64 | &iptsave.IPrule{
65 | RenderState: op,
66 | Match: []*iptsave.Match{
67 | &iptsave.Match{
68 | Body: "-o " + nodename,
69 | },
70 | },
71 | Action: iptsave.IPtablesAction{
72 | Type: iptsave.ActionDefault,
73 | Body: "ROMANA-FORWARD-IN",
74 | },
75 | },
76 | },
77 | },
78 | &iptsave.IPchain{
79 | Name: "OUTPUT",
80 | Policy: "-",
81 | Rules: []*iptsave.IPrule{
82 | &iptsave.IPrule{
83 | RenderState: op,
84 | Match: []*iptsave.Match{
85 | &iptsave.Match{
86 | Body: "-o " + nodename,
87 | },
88 | },
89 | Action: iptsave.IPtablesAction{
90 | Type: iptsave.ActionDefault,
91 | Body: "ROMANA-OUTPUT",
92 | },
93 | },
94 | },
95 | },
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/cni/kubernetes.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2017 Pani Networks
2 | // All Rights Reserved.
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may
5 | // not use this file except in compliance with the License. You may obtain
6 | // a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | // License for the specific language governing permissions and limitations
14 | // under the License.
15 |
16 | package cni
17 |
18 | import (
19 | "fmt"
20 | "net"
21 |
22 | "github.com/containernetworking/cni/pkg/types"
23 | "k8s.io/client-go/kubernetes"
24 | "k8s.io/client-go/tools/clientcmd"
25 | )
26 |
27 | type PodDescription struct {
28 | Name string
29 | Namespace string
30 | Labels map[string]string
31 | Annotations map[string]string
32 | }
33 |
34 | // K8sArgs is the valid CNI_ARGS used for Kubernetes.
35 | type K8sArgs struct {
36 | types.CommonArgs
37 | IP net.IP
38 | K8S_POD_NAME types.UnmarshallableString
39 | K8S_POD_NAMESPACE types.UnmarshallableString
40 | K8S_POD_INFRA_CONTAINER_ID types.UnmarshallableString
41 | }
42 |
43 | // MakeVethName generates veth name that can be used for external part
44 | // of the veth interface.
45 | func (k8s K8sArgs) MakeVethName() string {
46 | const suffixLength = 8
47 | const vethPrefix = "romana"
48 | var suffix string
49 | infra := string(k8s.K8S_POD_INFRA_CONTAINER_ID)
50 | if len(infra) > suffixLength {
51 | suffix = infra[:suffixLength]
52 | } else {
53 | suffix = infra
54 | }
55 |
56 | return fmt.Sprintf("%s-%s", vethPrefix, suffix)
57 | }
58 |
59 | // MakePodName returns unique pod name.
60 | func (k8s K8sArgs) MakePodName() string {
61 | const suffixLength = 8
62 | var suffix string
63 | infra := string(k8s.K8S_POD_INFRA_CONTAINER_ID)
64 | if len(infra) > suffixLength {
65 | suffix = infra[:suffixLength]
66 | } else {
67 | suffix = infra
68 | }
69 |
70 | return fmt.Sprintf("%s.%s.%s", k8s.K8S_POD_NAME, k8s.K8S_POD_NAMESPACE, suffix)
71 | }
72 |
73 | // GetPodDescription retrieves additional information about pod that being created
74 | // or deleted using CNI.
75 | func GetPodDescription(args K8sArgs, configFile string) (*PodDescription, error) {
76 | // Init kubernetes client. Attempt to load from statically configured k8s config or fallback on in-cluster
77 | kubeClientConfig, err := clientcmd.BuildConfigFromFlags("", configFile)
78 | if err != nil {
79 | return nil, err
80 | }
81 | kubeClient, err := kubernetes.NewForConfig(kubeClientConfig)
82 | if err != nil {
83 | return nil, err
84 | }
85 |
86 | pod, err := kubeClient.Core().Pods(string(args.K8S_POD_NAMESPACE)).Get(fmt.Sprintf("%s", args.K8S_POD_NAME))
87 | if err != nil {
88 | return nil, fmt.Errorf("Failed to discover a pod %s, err=(%s)", args.K8S_POD_NAME, err)
89 | }
90 |
91 | res := PodDescription{
92 | Name: args.MakePodName(),
93 | Namespace: string(args.K8S_POD_NAMESPACE),
94 | Labels: pod.Labels,
95 | Annotations: pod.Annotations,
96 | }
97 |
98 | return &res, nil
99 | }
100 |
--------------------------------------------------------------------------------
/cni/netconfig.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2017 Pani Networks
2 | // All Rights Reserved.
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may
5 | // not use this file except in compliance with the License. You may obtain
6 | // a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | // License for the specific language governing permissions and limitations
14 | // under the License.
15 |
16 | package cni
17 |
18 | import (
19 | "database/sql/driver"
20 | "encoding/json"
21 | "fmt"
22 | "net"
23 | )
24 |
25 | // NetworkRequest specifies messages sent to the
26 | // agent containing information on how to configure network
27 | // on its host.
28 | type NetworkRequest struct {
29 | NetIf NetIf `json:"net_if,omitempty"`
30 | // TODO we should not need this tag
31 | Options map[string]string `json:"options,omitempty"`
32 | }
33 |
34 | // NetIf is a structure that represents
35 | // network interface and its IP configuration
36 | // together with basic methods operating on this structure.
37 | type NetIf struct {
38 | Name string `form:"interface_name" sql:"unique"`
39 | Mac string `form:"mac_address" gorm:"primary_key"`
40 | IP IP `form:"ip_address" sql:"TYPE:varchar"`
41 | }
42 |
43 | // MarshalJSON properly marshals NetIf structure.
44 | func (n NetIf) MarshalJSON() ([]byte, error) {
45 | m := make(map[string]string)
46 | m["interface_name"] = n.Name
47 | m["mac_address"] = n.Mac
48 | m["ip_address"] = n.IP.String()
49 | return json.Marshal(m)
50 | }
51 |
52 | // GetName implements firewall.FirewallEndpoint
53 | func (n NetIf) GetName() string {
54 | return n.Name
55 | }
56 |
57 | // GetMac implements firewall.FirewallEndpoint
58 | func (n NetIf) GetMac() string {
59 | return n.Mac
60 | }
61 |
62 | // GetIP implements firewall.FirewallEndpoint
63 | func (n NetIf) GetIP() net.IP {
64 | return n.IP.IP
65 | }
66 |
67 | // SetIP parses and sets the IP address of the interface.
68 | func (n *NetIf) SetIP(ip string) error {
69 | n.IP.IP = net.ParseIP(ip)
70 | if n.IP.IP == nil && ip != "" {
71 | return fmt.Errorf("failed to parse Netif, bad IP: %s", ip)
72 | }
73 |
74 | return nil
75 | }
76 |
77 | // UnmarshalJSON results in having NetIf implement Unmarshaler
78 | // interface from encoding/json. This is needed because we use
79 | // a type like net.IP here, not a simple type, and so a call to
80 | // net.ParseIP is required to unmarshal this properly.
81 | func (n *NetIf) UnmarshalJSON(data []byte) error {
82 | m := make(map[string]string)
83 | json.Unmarshal(data, &m)
84 | ip := m["ip_address"]
85 | n.IP.IP = net.ParseIP(ip)
86 | if n.IP.IP == nil && ip != "" {
87 | return fmt.Errorf("failed to parse Netif, bad IP: %s", ip)
88 | }
89 |
90 | n.Name = m["interface_name"]
91 | n.Mac = m["mac_address"]
92 | return nil
93 | }
94 |
95 | // IP structure is basically net.IP, but we redefine it so we
96 | // can implement Valuer and Scanner interfaces on it for storage.
97 | type IP struct {
98 | net.IP
99 | }
100 |
101 | // Value implements driver.Valuer interface on IP
102 | func (i IP) Value() (driver.Value, error) {
103 | return driver.Value(i.String()), nil
104 | }
105 |
106 | // Scan implements driver.Scanner interface on IP
107 | func (i *IP) Scan(src interface{}) error {
108 | switch src := src.(type) {
109 | case string:
110 | ip := net.ParseIP(src)
111 | if ip == nil {
112 | return fmt.Errorf("cannot parse IP %s", src)
113 | }
114 | i.IP = ip
115 | return nil
116 | case []uint8:
117 | i.IP = net.ParseIP(string(src))
118 | return nil
119 | default:
120 | return fmt.Errorf("incompatible type for IP %s", src)
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/cni/plugin_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2017 Pani Networks
2 | // All Rights Reserved.
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may
5 | // not use this file except in compliance with the License. You may obtain
6 | // a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | // License for the specific language governing permissions and limitations
14 | // under the License.
15 |
16 | package cni
17 |
18 | import (
19 | "fmt"
20 | "net"
21 | "strings"
22 | "testing"
23 |
24 | "github.com/vishvananda/netlink"
25 | "golang.org/x/sys/unix"
26 | )
27 |
28 | type mockNlRouteHandle struct {
29 | linkByNameErr error
30 | addRouteErr error
31 | replaceRouteErr error
32 | }
33 |
34 | func (m mockNlRouteHandle) LinkByName(name string) (netlink.Link, error) {
35 | return &netlink.Device{}, m.linkByNameErr
36 | }
37 |
38 | func (m mockNlRouteHandle) RouteAdd(*netlink.Route) error {
39 | return m.addRouteErr
40 | }
41 |
42 | func (m mockNlRouteHandle) RouteReplace(*netlink.Route) error {
43 | return m.replaceRouteErr
44 | }
45 |
46 | func (m mockNlRouteHandle) Delete() {}
47 |
48 | func (m mockNlRouteHandle) RouteGet(net.IP) ([]netlink.Route, error) {
49 | return []netlink.Route{}, nil
50 | }
51 |
52 | func TestAddEndpointRoute(t *testing.T) {
53 | dummyIpnet := &net.IPNet{IP: net.ParseIP("127.0.0.1"), Mask: net.IPMask([]byte{0xff, 0xff, 0xff, 0xff})}
54 |
55 | cases := []struct {
56 | name string
57 | ifaceName string
58 | ip *net.IPNet
59 | msg string
60 | mock mockNlRouteHandle
61 | test func(error) bool
62 | }{
63 | {
64 | "detect failure during LinkByName",
65 | "dummy",
66 | dummyIpnet,
67 | "",
68 | mockNlRouteHandle{linkByNameErr: fmt.Errorf("bang")},
69 | func(err error) bool { return err.Error() == "bang" },
70 | },
71 | {
72 | "detect failure during route add",
73 | "dummy",
74 | dummyIpnet,
75 | "",
76 | mockNlRouteHandle{addRouteErr: fmt.Errorf("bang")},
77 | func(err error) bool { return strings.Contains(err.Error(), "couldn't create route") },
78 | },
79 | {
80 | "check switch to ReplaceRoute when unix.EEXIST",
81 | "dummy",
82 | dummyIpnet,
83 | "",
84 | mockNlRouteHandle{
85 | addRouteErr: unix.EEXIST,
86 | replaceRouteErr: fmt.Errorf("bang"),
87 | },
88 | func(err error) bool { return strings.Contains(err.Error(), "couldn't replace route") },
89 | },
90 | {
91 | "check RouteAdd success",
92 | "dummy",
93 | dummyIpnet,
94 | "",
95 | mockNlRouteHandle{},
96 | func(err error) bool { return err == nil },
97 | },
98 | {
99 | "check switch to ReplaceRoute success",
100 | "dummy",
101 | dummyIpnet,
102 | "",
103 | mockNlRouteHandle{
104 | addRouteErr: unix.EEXIST,
105 | },
106 | func(err error) bool { return err == nil },
107 | },
108 | }
109 |
110 | for _, tc := range cases {
111 | t.Run(tc.name, func(t *testing.T) {
112 | err := AddEndpointRoute(tc.ifaceName, tc.ip, tc.mock)
113 | if !tc.test(err) {
114 | t.Fatalf("%s, %s", tc.msg, err)
115 | }
116 | })
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/cni/policy.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2017 Pani Networks
2 | // All Rights Reserved.
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may
5 | // not use this file except in compliance with the License. You may obtain
6 | // a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | // License for the specific language governing permissions and limitations
14 | // under the License.
15 |
16 | package cni
17 |
18 | import (
19 | "fmt"
20 | "os/exec"
21 | "strings"
22 |
23 | "github.com/romana/core/agent/iptsave"
24 | "github.com/romana/rlog"
25 | )
26 |
27 | func enablePodPolicy(ifaceName string) error {
28 | return manageDivertRules(MakeDivertRules(ifaceName, iptsave.RenderAppendRule))
29 | }
30 |
31 | func disablePodPolicy(ifaceName string) error {
32 | return manageDivertRules(MakeDivertRules(ifaceName, iptsave.RenderDeleteRule))
33 | }
34 |
35 | func manageDivertRules(divertRules []*iptsave.IPchain) error {
36 | IptablesBin, err := exec.LookPath("iptables")
37 | if err != nil {
38 | return err
39 | }
40 |
41 | var rules string
42 | for _, chain := range divertRules {
43 | rules += chain.RenderFooter()
44 | }
45 |
46 | makeArgs := func(a []string, b ...string) []string {
47 | var result []string
48 | result = append(b, a...)
49 | return result
50 | }
51 |
52 | for _, rule := range strings.Split(rules, "\n") {
53 | if rule == "" {
54 | continue
55 | }
56 | rlog.Debugf("EXEC %s", makeArgs(strings.Split(rule, " ")), IptablesBin, "-t", "filter")
57 | data, err := exec.Command(IptablesBin, makeArgs(strings.Split(rule, " "), "-t", "filter")...).CombinedOutput()
58 | if err != nil {
59 | return fmt.Errorf("%s, err=%s", data, err)
60 | }
61 | }
62 |
63 | return nil
64 | }
65 |
--------------------------------------------------------------------------------
/common/api/errors/errors.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2017 Pani Networks
2 | // All Rights Reserved.
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may
5 | // not use this file except in compliance with the License. You may obtain
6 | // a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | // License for the specific language governing permissions and limitations
14 | // under the License.
15 |
16 | package errors
17 |
18 | import (
19 | "fmt"
20 | "strings"
21 | )
22 |
23 | // RomanaNotFoundError represents an error when an entity (or resource)
24 | // is not found. It is a separate error because clients may wish to check for this
25 | // error.
26 | type RomanaNotFoundError struct {
27 | // Attributes represent key-value pairs used to search
28 | // for the object.
29 | Attributes map[string]string
30 | Type string
31 | Message string
32 | }
33 |
34 | // NewRomanaNotFoundError creates a RomanaNotFoundError. Each element
35 | // of attrs is interpreted as a "key=value" pair.
36 | func NewRomanaNotFoundError(message string, t string, attrs ...string) RomanaNotFoundError {
37 | attrMap := make(map[string]string)
38 | for _, attr := range attrs {
39 | kv := strings.SplitN(attr, "=", 2)
40 | k := kv[0]
41 | v := kv[1]
42 | attrMap[k] = v
43 | }
44 | err := RomanaNotFoundError{Message: message,
45 | Type: t,
46 | Attributes: attrMap,
47 | }
48 | return err
49 | }
50 |
51 | func (rnfe RomanaNotFoundError) Error() string {
52 | if rnfe.Message == "" {
53 | return fmt.Sprintf("An %s object with attributes %v not found", rnfe.Type, rnfe.Attributes)
54 | } else {
55 | return rnfe.Message
56 | }
57 | }
58 |
59 | // RomanaExistsError represents an error when an entity already
60 | // exists.
61 | type RomanaExistsError struct {
62 | Type string
63 | // Attributes represent key-value pairs used to add
64 | // the object.
65 | Attributes map[string]string
66 | Object interface{}
67 | Message string
68 | }
69 |
70 | func NewRomanaExistsError(obj interface{}, t string, attrs ...string) RomanaExistsError {
71 | attrMap := make(map[string]string)
72 | for _, attr := range attrs {
73 | kv := strings.SplitN(attr, "=", 2)
74 | k := kv[0]
75 | v := kv[1]
76 | attrMap[k] = v
77 | }
78 | err := RomanaExistsError{
79 | Type: t,
80 | Object: obj,
81 | Attributes: attrMap,
82 | }
83 | return err
84 | }
85 |
86 | func NewRomanaExistsErrorWithMessage(msg string, obj interface{}, t string, attrs ...string) RomanaExistsError {
87 | err := NewRomanaExistsError(obj, t, attrs...)
88 | err.Message = msg
89 | return err
90 | }
91 |
92 | func (ree RomanaExistsError) Error() string {
93 | if ree.Message == "" {
94 | return fmt.Sprintf("A[n] '%s' object identified by %+v already exists: %s", ree.Type, ree.Attributes, ree.Object)
95 | } else {
96 | return ree.Message
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/common/api/errors/helpers.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2017 Pani Networks
2 | // All Rights Reserved.
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may
5 | // not use this file except in compliance with the License. You may obtain
6 | // a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | // License for the specific language governing permissions and limitations
14 | // under the License.
15 |
16 | package errors
17 |
18 | import (
19 | "fmt"
20 |
21 | "github.com/romana/core/common"
22 | )
23 |
24 | // romanaErrorToHTTPError is a helper method that creates an
25 | // HTTP error (one that the middleware automatically converts to the right
26 | // HTTP status code and response) from the provided Romana error, if possible.
27 | // If the provided is not a Romana error, or if no corresponding HTTP errror
28 | // can be provided, the original error is returned. Thus the signature takes a
29 | // generic error and also returns it.
30 | func RomanaErrorToHTTPError(err error) error {
31 | if err == nil {
32 | return nil
33 | }
34 | switch err := err.(type) {
35 | case RomanaNotFoundError:
36 | return common.NewError404(err.Type, fmt.Sprintf("%v", err.Attributes))
37 | case RomanaExistsError:
38 | common.NewErrorConflict(err)
39 |
40 | }
41 | return err
42 | }
43 |
--------------------------------------------------------------------------------
/common/api/policy.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2017 Pani Networks
2 | // All Rights Reserved.
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may
5 | // not use this file except in compliance with the License. You may obtain
6 | // a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | // License for the specific language governing permissions and limitations
14 | // under the License.
15 |
16 | package api
17 |
18 | import (
19 | "fmt"
20 |
21 | "github.com/romana/core/common"
22 | )
23 |
24 | const (
25 | // Max port number for TCP/UDP.
26 | MaxPortNumber = 65535
27 | MaxIcmpType = 255
28 |
29 | // Wildcard
30 | Wildcard = "any"
31 | )
32 |
33 | // Endpoint represents an endpoint - that is, something that
34 | // has an IP address and routes to/from. It can be a container,
35 | // a Kubernetes POD, a VM, etc.
36 | type Endpoint struct {
37 | Peer string `json:"peer,omitempty"`
38 | Cidr string `json:"cidr,omitempty"`
39 | Dest string `json:"dest,omitempty"`
40 | TenantID string `json:"tenant_id,omitempty"`
41 | SegmentID string `json:"segment_id,omitempty"`
42 | }
43 |
44 | func (e Endpoint) String() string {
45 | return common.String(e)
46 | }
47 |
48 | const (
49 | PolicyDirectionIngress = "ingress"
50 | PolicyDirectionEgress = "egress"
51 | )
52 |
53 | type PortRange [2]uint
54 |
55 | func (p PortRange) String() string {
56 | return fmt.Sprintf("%d-%d", p[0], p[1])
57 | }
58 |
59 | // Rule describes a rule of the policy. The following requirements apply
60 | // (the policy would not be validated otherwise):
61 | // 1. Protocol must be specified.
62 | // 2. Protocol must be one of those validated by isValidProto().
63 | // 3. Ports cannot be negative or greater than 65535.
64 | // 4. If Protocol specified is "icmp", Ports and PortRanges fields should be blank.
65 | // 5. If Protocol specified is not "icmp", Icmptype and IcmpCode should be unspecified.
66 | type Rule struct {
67 | Protocol string `json:"protocol,omitempty"`
68 | Ports []uint `json:"ports,omitempty"`
69 | PortRanges []PortRange `json:"port_ranges,omitempty"`
70 | // IcmpType only applies if Protocol value is ICMP and
71 | // is mutually exclusive with Ports or PortRanges
72 | IcmpType uint `json:"icmp_type,omitempty"`
73 | IcmpCode uint `json:"icmp_code,omitempty"`
74 | IsStateful bool `json:"is_stateful,omitempty"`
75 | }
76 |
77 | func (r Rule) String() string {
78 | return common.String(r)
79 | }
80 |
81 | type Rules []Rule
82 |
83 | // Metadata attached to entities for various external environments like Open Stack / Kubernetes
84 | type Tag struct {
85 | Key string `json:"key,omitempty"`
86 | Value string `json:"value,omitempty"`
87 | }
88 |
89 | // Policy describes Romana network security policy.
90 | // For examples, see:
91 | // 1. https://github.com/romana/core/blob/master/policy/policy.sample.json
92 | // 2. https://github.com/romana/core/blob/master/policy/policy.example.agent.json
93 | type Policy struct {
94 | ID string `json:"id"`
95 | // Direction is one of common.PolicyDirectionIngress or common.PolicyDirectionIngress,
96 | // otherwise common.Validate will return an error.
97 | Direction string `json:"direction,omitempty" romana:"desc:Direction is one of 'ingress' or egress'."`
98 | // Description is human-redable description of the policy.
99 | Description string `json:"description,omitempty"`
100 | // Datacenter describes a Romana deployment.
101 | AppliedTo []Endpoint `json:"applied_to,omitempty"`
102 | Ingress []RomanaIngress `json:"ingress,omitempty"`
103 | // Tags []Tag `json:"tags,omitempty"`
104 | }
105 |
106 | type RomanaIngress struct {
107 | Peers []Endpoint `json:"peers,omitempty"`
108 | Rules []Rule `json:"rules,omitempty"`
109 | }
110 |
111 | func (p Policy) String() string {
112 | return common.String(p)
113 | }
114 |
--------------------------------------------------------------------------------
/common/api/romanavip.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2017 Pani Networks
2 | // All Rights Reserved.
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may
5 | // not use this file except in compliance with the License. You may obtain
6 | // a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | // License for the specific language governing permissions and limitations
14 | // under the License.
15 |
16 | package api
17 |
18 | type RomanaVIP struct {
19 | Auto bool `json:"auto"`
20 | IP string `json:"ip"`
21 | }
22 |
23 | type ExposedIPSpec struct {
24 | RomanaVIP RomanaVIP
25 | NodeIPAddress string
26 | Activated bool
27 | Namespace string
28 | }
29 |
--------------------------------------------------------------------------------
/common/buildinfo.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2016 Pani Networks
2 | // All Rights Reserved.
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may
5 | // not use this file except in compliance with the License. You may obtain
6 | // a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | // License for the specific language governing permissions and limitations
14 | // under the License.
15 |
16 | package common
17 |
18 | // Build information support.
19 |
20 | import (
21 | "fmt"
22 | )
23 |
24 | // Build Information and Timestamp.
25 | // Pass build information to the executable using go run as below:
26 | //
27 | // go run -ldflags \
28 | // "-X github.com/romana/core/common.buildInfo=`git describe --always` \
29 | // -X github.com/romana/core/common.buildTimeStamp=`date -u '+%Y-%m-%d_%I:%M:%S%p'`" \
30 | // main.go -version
31 | //
32 | // or using go build as below:
33 | //
34 | // go build -ldflags \
35 | // "-X github.com/romana/core/common.buildInfo=`git describe --always` \
36 | // -X github.com/romana/core/common.buildTimeStamp=`date -u '+%Y-%m-%d_%I:%M:%S%p'`" \
37 | // main.go
38 | //
39 | var buildInfo = "No Build Information Provided"
40 | var buildTimeStamp = "No Build Time Provided"
41 |
42 | // BuildInfo return build revision and time string.
43 | func BuildInfo() string {
44 | return fmt.Sprintf("Build Revision: %s\nBuild Time: %s", buildInfo, buildTimeStamp)
45 | }
46 |
--------------------------------------------------------------------------------
/common/client/idring/merge.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2016-2017 Pani Networks
2 | // All Rights Reserved.
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may
5 | // not use this file except in compliance with the License. You may obtain
6 | // a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | // License for the specific language governing permissions and limitations
14 | // under the License.
15 |
16 | package idring
17 |
18 | import "sort"
19 |
20 | type rangeSorter []Range
21 |
22 | func (a rangeSorter) Len() int { return len(a) }
23 | func (a rangeSorter) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
24 | func (a rangeSorter) Less(i, j int) bool { return a[i].Min < a[j].Min }
25 |
26 | // Merge a slice of ranges combining adjacent ranges together
27 | // and skipping included ranges.
28 | func Merge(ranges []Range) []Range {
29 | if len(ranges) < 2 {
30 | return ranges
31 | }
32 |
33 | var result []Range
34 | sort.Sort(rangeSorter(ranges))
35 | for i, _ := range ranges {
36 | if len(result) == 0 {
37 | result = append(result, ranges[i])
38 | continue
39 | }
40 |
41 | resultLastIndex := len(result) - 1
42 | lastResult := result[resultLastIndex]
43 |
44 | // skip if current element fully included
45 | // in last result element
46 | if lastResult.Min <= ranges[i].Min && lastResult.Max >= ranges[i].Max {
47 | continue
48 | }
49 |
50 | // merge
51 | if lastResult.Max+1 >= ranges[i].Min {
52 | result[resultLastIndex] = Range{lastResult.Min, ranges[i].Max}
53 | continue
54 | }
55 |
56 | // can't merge, add as is
57 | result = append(result, ranges[i])
58 | }
59 |
60 | return result
61 | }
62 |
--------------------------------------------------------------------------------
/common/client/idring/merge_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2016-2017 Pani Networks
2 | // All Rights Reserved.
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may
5 | // not use this file except in compliance with the License. You may obtain
6 | // a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | // License for the specific language governing permissions and limitations
14 | // under the License.
15 |
16 | package idring
17 |
18 | import "testing"
19 |
20 | func TestMerge(t *testing.T) {
21 | cases := []struct {
22 | name string
23 | ranges []Range
24 | test func([]Range) bool
25 | }{
26 | {
27 | name: "Full collapse",
28 | ranges: []Range{Range{Min: 43, Max: 44}, Range{Min: 45, Max: 48}, Range{Min: 40, Max: 43}},
29 | test: func(r []Range) bool { return r[0].Min == 40 && r[0].Max == 48 },
30 | },
31 | {
32 | name: "Merge adjacent",
33 | ranges: []Range{Range{Min: 43, Max: 44}, Range{Min: 45, Max: 48}},
34 | test: func(r []Range) bool { return r[0].Min == 43 && r[0].Max == 48 },
35 | },
36 | {
37 | name: "Merge adjacent2",
38 | ranges: []Range{Range{Min: 43, Max: 45}, Range{Min: 45, Max: 48}},
39 | test: func(r []Range) bool { return r[0].Min == 43 && r[0].Max == 48 },
40 | },
41 | {
42 | name: "Merge overlap",
43 | ranges: []Range{Range{Min: 43, Max: 47}, Range{Min: 45, Max: 48}},
44 | test: func(r []Range) bool { return r[0].Min == 43 && r[0].Max == 48 },
45 | },
46 | {
47 | name: "Skip included",
48 | ranges: []Range{Range{Min: 45, Max: 47}, Range{Min: 40, Max: 40}, Range{Min: 40, Max: 43}},
49 | test: func(r []Range) bool { return r[0].Min == 40 && r[1].Max == 47 },
50 | },
51 | {
52 | name: "Dont merge unmergable",
53 | ranges: []Range{Range{Min: 45, Max: 47}, Range{Min: 40, Max: 41}},
54 | test: func(r []Range) bool { return r[0].Min == 40 && r[1].Max == 47 },
55 | },
56 | }
57 |
58 | for _, tc := range cases {
59 | t.Run(tc.name, func(t *testing.T) {
60 | r := Merge(tc.ranges)
61 | t.Logf("Name %s, result %+v", tc.name, r)
62 | if !tc.test(r) {
63 | t.Fatal(tc.name)
64 | }
65 | })
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/common/client/testdata/Test32_1.json:
--------------------------------------------------------------------------------
1 | {
2 | "networks":[
3 | {
4 | "name":"net1",
5 | "cidr":"10.0.0.0/24",
6 | "block_mask":32
7 | }
8 | ],
9 | "topologies":[
10 | {
11 | "networks":[
12 | "net1"
13 | ],
14 | "map":[
15 | {
16 | "routing":"foo",
17 | "groups":[{
18 | "name":"host1",
19 | "ip":"192.168.0.1"
20 | }]
21 | }
22 | ]
23 | }
24 | ]
25 | }
--------------------------------------------------------------------------------
/common/client/testdata/Test32_2.json:
--------------------------------------------------------------------------------
1 | {
2 | "networks":[
3 | {
4 | "name":"net1",
5 | "cidr":"10.0.0.0/32",
6 | "block_mask":32
7 | }
8 | ],
9 | "topologies":[
10 | {
11 | "networks":[
12 | "net1"
13 | ],
14 | "map":[
15 | {
16 | "routing":"foo",
17 | "groups": [{
18 | "name":"host1",
19 | "ip":"192.168.0.1"
20 | }]
21 | }
22 | ]
23 | }
24 | ]
25 | }
--------------------------------------------------------------------------------
/common/client/testdata/TestBlackout.json:
--------------------------------------------------------------------------------
1 | {
2 | "networks":[
3 | {
4 | "name":"net1",
5 | "cidr":"10.0.0.0/30",
6 | "block_mask":30
7 | }
8 | ],
9 | "topologies":[
10 | {
11 | "networks":[
12 | "net1"
13 | ],
14 | "map":[
15 | {
16 | "routing":"foo",
17 | "groups":[{
18 | "name":"host1",
19 | "ip":"192.168.0.1"
20 | }]
21 | }
22 | ]
23 | }
24 | ]
25 | }
--------------------------------------------------------------------------------
/common/client/testdata/TestBlockReuseMask30.json:
--------------------------------------------------------------------------------
1 | {
2 | "networks":[
3 | {
4 | "name":"net1",
5 | "cidr":"10.0.0.0/8",
6 | "block_mask":30
7 | }
8 | ],
9 | "topologies":[
10 | {
11 | "networks":[
12 | "net1"
13 | ],
14 | "map":[
15 | {
16 | "routing":"foo",
17 | "groups":[{
18 | "name":"host1",
19 | "ip":"192.168.0.1"
20 | }]
21 | }
22 | ]
23 | }
24 | ]
25 | }
--------------------------------------------------------------------------------
/common/client/testdata/TestBlockReuseMask32.json:
--------------------------------------------------------------------------------
1 | {
2 | "networks":[
3 | {
4 | "name":"net1",
5 | "cidr":"10.0.0.0/31",
6 | "block_mask":32
7 | }
8 | ],
9 | "topologies":[
10 | {
11 | "networks":[
12 | "net1"
13 | ],
14 | "map":[
15 | {
16 | "routing":"foo",
17 | "groups":[{
18 | "name":"host1",
19 | "ip":"192.168.0.1"
20 | }]
21 | }
22 | ]
23 | }
24 | ]
25 | }
--------------------------------------------------------------------------------
/common/client/testdata/TestHostAdd713.json:
--------------------------------------------------------------------------------
1 | {
2 | "networks": [
3 | {
4 | "name": "k8-rack-2a",
5 | "cidr": "10.28.0.0/19",
6 | "block_mask": 24
7 | },
8 | {
9 | "name": "k8-rack-2b",
10 | "cidr": "10.28.32.0/19",
11 | "block_mask": 24
12 | },
13 | {
14 | "name": "k8-rack-3a",
15 | "cidr": "10.28.64.0/19",
16 | "block_mask": 24
17 | },
18 | {
19 | "name": "k8-rack-3b",
20 | "cidr": "10.28.96.0/19",
21 | "block_mask": 24
22 | },
23 | {
24 | "name": "k8-rack-4a",
25 | "cidr": "10.28.128.0/19",
26 | "block_mask": 24
27 | },
28 | {
29 | "name": "k8-rack-4b",
30 | "cidr": "10.28.160.0/19",
31 | "block_mask": 24
32 | },
33 | {
34 | "name": "k8-rack-5a",
35 | "cidr": "10.28.192.0/19",
36 | "block_mask": 24
37 | },
38 | {
39 | "name": "k8-rack-5b",
40 | "cidr": "10.28.224.0/19",
41 | "block_mask": 24
42 | }
43 | ],
44 | "topologies": [
45 | {
46 | "networks": [
47 | "k8-rack-2a"
48 | ],
49 | "map": [
50 | {
51 | "groups": [
52 | {
53 | "name": "rack-2a",
54 | "groups": [],
55 | "assignment": {
56 | "rack": "rack-2a"
57 | }
58 | }
59 | ]
60 | }
61 | ]
62 | },
63 | {
64 | "networks": [
65 | "k8-rack-2b"
66 | ],
67 | "map": [
68 | {
69 | "groups": [
70 | {
71 | "name": "rack-2b",
72 | "groups": [],
73 | "assignment": {
74 | "rack": "rack-2b"
75 | }
76 | }
77 | ]
78 | }
79 | ]
80 | },
81 | {
82 | "networks": [
83 | "k8-rack-3a"
84 | ],
85 | "map": [
86 | {
87 | "groups": [
88 | {
89 | "name": "rack-3a",
90 | "groups": [],
91 | "assignment": {
92 | "rack": "rack-3a"
93 | }
94 | }
95 | ]
96 | }
97 | ]
98 | },
99 | {
100 | "networks": [
101 | "k8-rack-3b"
102 | ],
103 | "map": [
104 | {
105 | "groups": [
106 | {
107 | "name": "rack-3b",
108 | "groups": [],
109 | "assignment": {
110 | "rack": "rack-3b"
111 | }
112 | }
113 | ]
114 | }
115 | ]
116 | },
117 | {
118 | "networks": [
119 | "k8-rack-4a"
120 | ],
121 | "map": [
122 | {
123 | "groups": [
124 | {
125 | "name": "rack-4a",
126 | "groups": [],
127 | "assignment": {
128 | "rack": "rack-4a"
129 | }
130 | }
131 | ]
132 | }
133 | ]
134 | },
135 | {
136 | "networks": [
137 | "k8-rack-4b"
138 | ],
139 | "map": [
140 | {
141 | "groups": [
142 | {
143 | "name": "rack-4b",
144 | "groups": [],
145 | "assignment": {
146 | "rack": "rack-4b"
147 | }
148 | }
149 | ]
150 | }
151 | ]
152 | },
153 | {
154 | "networks": [
155 | "k8-rack-5a"
156 | ],
157 | "map": [
158 | {
159 | "groups": [
160 | {
161 | "name": "rack-5a",
162 | "groups": [],
163 | "assignment": {
164 | "rack": "rack-5a"
165 | }
166 | }
167 | ]
168 | }
169 | ]
170 | },
171 | {
172 | "networks": [
173 | "k8-rack-5b"
174 | ],
175 | "map": [
176 | {
177 | "groups": [
178 | {
179 | "name": "rack-5b",
180 | "groups": [],
181 | "assignment": {
182 | "rack": "rack-5b"
183 | }
184 | }
185 | ]
186 | }
187 | ]
188 | }
189 | ]
190 | }
--------------------------------------------------------------------------------
/common/client/testdata/TestHostAdditionSimple.json:
--------------------------------------------------------------------------------
1 | {
2 | "networks": [
3 | {
4 | "name": "net1",
5 | "cidr": "10.0.0.0/16",
6 | "block_mask": 30
7 | },
8 | {
9 | "name": "net2",
10 | "cidr": "10.20.0.0/16",
11 | "block_mask": 30
12 | }
13 | ],
14 | "topologies": [
15 | {
16 | "networks": [
17 | "net1", "net2"
18 | ],
19 | "map": [
20 | {
21 | "groups": [
22 |
23 | ],
24 | "name": "group1"
25 | },
26 | {
27 | "groups": [
28 |
29 | ],
30 | "name": "group2"
31 | }
32 | ]
33 | }
34 | ]
35 | }
36 |
--------------------------------------------------------------------------------
/common/client/testdata/TestHostAdditionTags.json:
--------------------------------------------------------------------------------
1 | {
2 | "networks": [
3 | {
4 | "name": "net1",
5 | "cidr": "10.0.0.0/16",
6 | "block_mask": 30
7 | }
8 | ],
9 | "topologies": [
10 | {
11 | "networks": [
12 | "net1"
13 | ],
14 | "map": [
15 | {
16 | "assignment" : { "tier" : "backend" },
17 | "groups": [
18 |
19 | ],
20 | "name": "group1"
21 | },
22 | {
23 | "assignment" : { "tier" : "frontend" },
24 | "groups": [
25 |
26 | ],
27 | "name": "group2"
28 | },
29 | {
30 | "assignment" : { "tier" : "backend" },
31 | "groups": [
32 |
33 | ],
34 | "name": "group3"
35 | },
36 | {
37 | "assignment" : { "tier" : "frontend" },
38 | "groups": [
39 |
40 | ],
41 | "name": "group4"
42 | }
43 | ]
44 | }
45 | ]
46 | }
--------------------------------------------------------------------------------
/common/client/testdata/TestHostAllocation.json:
--------------------------------------------------------------------------------
1 | {
2 | "networks":[
3 | {
4 | "name":"net1",
5 | "cidr":"10.0.0.0/8",
6 | "block_mask":30
7 | }
8 | ],
9 | "topologies":[
10 | {
11 | "networks":[
12 | "net1"
13 | ],
14 | "map":[
15 | {
16 | "routing":"test",
17 | "groups":[
18 | {
19 | "name":"ip-192-168-99-10",
20 | "ip":"192.168.99.10"
21 | },
22 | {
23 | "name":"ip-192-168-99-11",
24 | "ip":"192.168.99.11"
25 | }
26 | ]
27 | }
28 | ]
29 | }
30 | ]
31 | }
--------------------------------------------------------------------------------
/common/client/testdata/TestIPAM_DeallocateIP.json:
--------------------------------------------------------------------------------
1 | {
2 | "networks":[
3 | {
4 | "name":"net1",
5 | "cidr":"10.0.0.0/31",
6 | "block_mask":31
7 | }
8 | ],
9 | "topologies":[
10 | {
11 | "networks":[
12 | "net1"
13 | ],
14 | "map":[
15 | {
16 | "routing":"foo",
17 | "groups":[{
18 | "name":"host1",
19 | "ip":"192.168.0.1"
20 | }]
21 | }
22 | ]
23 | }
24 | ]
25 | }
26 |
--------------------------------------------------------------------------------
/common/client/testdata/TestIPReuse.json:
--------------------------------------------------------------------------------
1 | {
2 | "networks":[
3 | {
4 | "name":"net1",
5 | "cidr":"10.0.0.0/31",
6 | "block_mask":31
7 | }
8 | ],
9 | "topologies":[
10 | {
11 | "networks":[
12 | "net1"
13 | ],
14 | "map":[
15 | {
16 | "routing":"foo",
17 | "groups":[{
18 | "name":"host1",
19 | "ip":"192.168.0.1"
20 | }]
21 | }
22 | ]
23 | }
24 | ]
25 | }
--------------------------------------------------------------------------------
/common/client/testdata/TestLabelUpdate.json:
--------------------------------------------------------------------------------
1 | {
2 | "networks": [
3 | {
4 | "name": "net1",
5 | "cidr": "100.11.0.0/20",
6 | "block_mask": 30
7 | }
8 | ],
9 | "topologies": [
10 | {
11 | "networks": [ "net1" ],
12 | "map": [
13 | { "name" : "g1", "assignment" : { "rack" : "rack1" }, "groups": [ ] }
14 | ]
15 | }
16 | ]
17 | }
--------------------------------------------------------------------------------
/common/client/testdata/TestListBlocks.json:
--------------------------------------------------------------------------------
1 | {
2 | "networks":[
3 | {
4 | "name":"net1",
5 | "cidr":"10.0.0.0/8",
6 | "block_mask":30
7 | }
8 | ],
9 | "topologies":[
10 | {
11 | "networks":[
12 | "net1"
13 | ],
14 | "map":[
15 | {
16 | "routing":"test",
17 | "groups":[
18 | {
19 | "name":"h1",
20 | "ip":"192.168.99.10"
21 | }
22 | ]
23 | }
24 | ]
25 | }
26 | ]
27 | }
--------------------------------------------------------------------------------
/common/client/testdata/TestMultiNetAllocate.json:
--------------------------------------------------------------------------------
1 | {
2 | "networks":[
3 | {
4 | "name":"net1",
5 | "cidr":"10.0.0.0/8",
6 | "block_mask":30
7 | },
8 | {
9 | "name":"net2",
10 | "cidr":"11.0.0.0/8",
11 | "block_mask":30
12 | }
13 | ],
14 | "topologies":[
15 | {
16 | "networks":[
17 | "net1"
18 | ],
19 | "map":[
20 | {
21 | "groups":[
22 | {
23 | "name":"host1",
24 | "ip":"192.168.99.10"
25 | }
26 | ]
27 | }
28 | ]
29 | },
30 | {
31 | "networks":[
32 | "net2"
33 | ],
34 | "map":[
35 | {
36 | "groups":[
37 | {
38 | "name":"host2",
39 | "ip":"192.168.99.11"
40 | }
41 | ]
42 | }
43 | ]
44 | }
45 | ]
46 | }
47 |
--------------------------------------------------------------------------------
/common/client/testdata/TestNodeAssignment.json:
--------------------------------------------------------------------------------
1 | {
2 | "networks": [
3 | {
4 | "name": "rnet-1",
5 | "cidr": "100.11.0.0/20",
6 | "block_mask": 30
7 | },
8 | {
9 | "name": "rnet-2",
10 | "cidr": "100.22.0.0/20",
11 | "block_mask": 30
12 | }
13 | ],
14 | "topologies": [
15 | {
16 | "networks": [ "rnet-1" ],
17 | "map": [
18 | { "name" : "g1", "assignment" : { "rack" : "rack-1" }, "groups": [ ] }
19 | ]
20 | },
21 | {
22 | "networks": [ "rnet-2" ],
23 | "map": [
24 | { "name" : "g2", "assignment" : { "rack" : "rack-2" }, "groups": [ ] },
25 | { "name" : "g3", "assignment" : { "rack" : "rack-3" }, "groups": [ ] },
26 | { "name" : "g4", "assignment" : { "rack" : "rack-4" }, "groups": [ ] }
27 | ]
28 | }
29 | ]
30 | }
--------------------------------------------------------------------------------
/common/client/testdata/TestOutOfBoundsError.json:
--------------------------------------------------------------------------------
1 | {
2 | "networks":[
3 | {
4 | "name":"net1",
5 | "cidr":"10.0.0.0/8",
6 | "block_mask":30
7 | }
8 | ],
9 | "topologies":[
10 | {
11 | "networks":[
12 | "net1"
13 | ],
14 | "map":[
15 | {
16 | "routing":"foo",
17 | "groups":[{
18 | "name":"host1",
19 | "ip":"192.168.0.1"
20 | }]
21 | }
22 | ]
23 | }
24 | ]
25 | }
--------------------------------------------------------------------------------
/common/client/testdata/TestOverlappingCIDRs.json:
--------------------------------------------------------------------------------
1 | {
2 | "networks": [
3 | {
4 | "name": "rnet-1",
5 | "cidr": "100.11.0.0/20",
6 | "block_mask": 30
7 | },
8 | {
9 | "name": "rnet-2",
10 | "cidr": "100.11.0.0/20",
11 | "block_mask": 30
12 | }
13 | ],
14 | "topologies": [
15 | {
16 | "networks": [ "rnet-1", "rnet-2" ],
17 | "map": [
18 | { "groups": [ ] }
19 | ]
20 | }
21 | ]
22 | }
--------------------------------------------------------------------------------
/common/client/testdata/TestPanic.json:
--------------------------------------------------------------------------------
1 | {
2 | "networks": [
3 | {
4 | "name": "rnet-1",
5 | "cidr": "100.11.0.0/20",
6 | "block_mask": 30
7 | },
8 | {
9 | "name": "rnet-2",
10 | "cidr": "100.22.0.0/20",
11 | "block_mask": 30
12 | }
13 | ],
14 | "topologies": [
15 | {
16 | "networks": [ "rnet-1" ],
17 | "map": [
18 | { "groups": [ ] }
19 | ]
20 | }
21 | ]
22 | }
--------------------------------------------------------------------------------
/common/client/testdata/TestParseMultiHostGroupsWithPrefix.json:
--------------------------------------------------------------------------------
1 | {
2 | "networks":[
3 | {
4 | "name":"vlanA",
5 | "cidr":"10.1.0.0/16",
6 | "block_mask" : 28
7 | }
8 | ],
9 | "topologies":[
10 | {
11 | "networks":[
12 | "vlanA"
13 | ],
14 | "map":[
15 | {
16 | "routing":"block-host-routes, prefix-announce-bgp:peerxxxx",
17 | "groups":[ { "name" : "h1", "ip" : "1.1.1.1" }, { "name" : "h1", "ip" : "1.1.1.1" } ]
18 | },
19 | {
20 | "routing":"block-host-routes, prefix-announce-bgp:peerxxxx",
21 | "groups":[ { "name" : "h1", "ip" : "1.1.1.1" }, { "name" : "h1", "ip" : "1.1.1.1" } ]
22 | },
23 | {
24 | "routing":"block-host-routes, prefix-announce-bgp:peerxxxx",
25 | "groups":[ { "name" : "h1", "ip" : "1.1.1.1" }, { "name" : "h1", "ip" : "1.1.1.1" } ]
26 | },
27 | {
28 | "routing":"block-host-routes, prefix-announce-bgp:peerxxxx",
29 | "groups":[ { "name" : "h1", "ip" : "1.1.1.1" }, { "name" : "h1", "ip" : "1.1.1.1" } ]
30 | }
31 | ]
32 | }
33 | ]
34 | }
--------------------------------------------------------------------------------
/common/client/testdata/TestParsePrefixPerHostA.json:
--------------------------------------------------------------------------------
1 | {
2 | "networks":[
3 | {
4 | "name":"vlanA",
5 | "cidr":"10.1.0.0/16",
6 | "block_mask" : 28
7 | }
8 | ],
9 | "topologies":[
10 | {
11 | "networks":[
12 | "vlanA"
13 | ],
14 | "map":[
15 | {
16 | "routing":"prefix-on-host",
17 | "groups":[
18 | { "name" : "h1", "ip" : "1.1.1.1" }
19 | ]
20 | },
21 | {
22 | "routing":"prefix-on-host",
23 | "groups":[
24 | { "name" : "h1", "ip" : "1.1.1.1" }
25 | ]
26 | },
27 | {
28 | "routing":"prefix-on-host",
29 | "groups":[ { "name" : "h1", "ip" : "1.1.1.1" } ]
30 | },
31 | {
32 | "routing":"prefix-on-host",
33 | "groups":[ { "name" : "h1", "ip" : "1.1.1.1" } ]
34 | }
35 | ]
36 | }
37 | ]
38 | }
--------------------------------------------------------------------------------
/common/client/testdata/TestParsePrefixPerHostB.json:
--------------------------------------------------------------------------------
1 | {
2 | "networks":[
3 | {
4 | "name":"vlanA",
5 | "cidr":"10.1.0.0/16",
6 | "block_mask" : 28
7 | }
8 | ],
9 | "topologies":[
10 | {
11 | "networks":[
12 | "vlanA"
13 | ],
14 | "map":[
15 | {
16 | "routing":"prefix-announce-bgp:peerxxxx",
17 | "groups":[ { "name" : "h1", "ip" : "1.1.1.1" } ]
18 | },
19 | {
20 | "routing":"prefix-announce-bgp:peerxxxx",
21 | "groups":[ { "name" : "h1", "ip" : "1.1.1.1" } ]
22 | },
23 | {
24 | "routing":"prefix-announce-bgp:peerxxxx",
25 | "groups":[ { "name" : "h1", "ip" : "1.1.1.1" } ]
26 | },
27 | {
28 | "routing":"prefix-announce-bgp:peerxxxx",
29 | "groups":[ { "name" : "h1", "ip" : "1.1.1.1" } ]
30 | }
31 | ]
32 | }
33 | ]
34 | }
--------------------------------------------------------------------------------
/common/client/testdata/TestParseSimpleFlatNetworkA.json:
--------------------------------------------------------------------------------
1 | {
2 | "networks":[
3 | {
4 | "name":"vlanA",
5 | "cidr":"10.1.0.0/16",
6 | "block_mask": 28
7 | }
8 | ],
9 | "topologies":[
10 | {
11 | "networks":[
12 | "vlanA"
13 | ],
14 | "map":[
15 | {
16 | "routing":"block-on-host",
17 | "groups":[
18 | { "name" : "h1", "ip" : "1.1.1.1" },
19 | { "name" : "h1", "ip" : "1.1.1.1" },
20 | { "name" : "h1", "ip" : "1.1.1.1" },
21 | { "name" : "h1", "ip" : "1.1.1.1" }
22 | ]
23 | }
24 | ]
25 | }
26 | ]
27 | }
--------------------------------------------------------------------------------
/common/client/testdata/TestParseSimpleFlatNetworkB.json:
--------------------------------------------------------------------------------
1 | {
2 | "networks":[
3 | {
4 | "name":"vlanA",
5 | "cidr":"10.1.0.0/16",
6 | "block_mask": 28
7 |
8 | }
9 | ],
10 | "topologies":[
11 | {
12 | "networks":[
13 | "vlanA"
14 | ],
15 | "map":[
16 | {
17 | "routing":"block-announce-bgp:peerxxxxx",
18 | "groups":[
19 | { "name" : "h1", "ip" : "1.1.1.1" },
20 | { "name" : "h2", "ip" : "1.1.1.1" },
21 | { "name" : "h3", "ip" : "1.1.1.1" },
22 | { "name" : "h4", "ip" : "1.1.1.1" }
23 | ]
24 | }
25 | ]
26 | }
27 | ]
28 | }
--------------------------------------------------------------------------------
/common/client/testdata/TestParseSimpleFlatNetworkC.json:
--------------------------------------------------------------------------------
1 | {
2 | "networks":[
3 | {
4 | "name":"vlanA",
5 | "cidr":"10.1.0.0/16",
6 | "block_mask" : 28
7 | }
8 | ],
9 | "topologies":[
10 | {
11 | "networks":[
12 | "vlanA"
13 | ],
14 | "map":[
15 | {
16 | "routing":"block-on-host,block - announce - bgp: peerxxxxx",
17 | "groups":[
18 | { "name" : "h1", "ip" : "1.1.1.1" },
19 | { "name" : "h1", "ip" : "1.1.1.1" },
20 | { "name" : "h1", "ip" : "1.1.1.1" },
21 | { "name" : "h1", "ip" : "1.1.1.1" }
22 | ]
23 | }
24 | ]
25 | }
26 | ]
27 | }
--------------------------------------------------------------------------------
/common/client/testdata/TestParseVPCRoutingForTwoAZs.json:
--------------------------------------------------------------------------------
1 | {
2 | "networks":[
3 | {
4 | "name":"subnetA",
5 | "cidr":"10.1.0.0/16",
6 | "block_mask" : 28
7 | },
8 | {
9 | "name":"subnetB",
10 | "cidr":"10.2.0.0/16",
11 | "block_mask" : 28
12 | }
13 | ],
14 | "topologies":[
15 | {
16 | "networks":[
17 | "subnetA"
18 | ],
19 | "map":[
20 | {
21 | "routing":"block-host-routes,prefix-announce-vpc",
22 | "groups":[]
23 | },
24 | {
25 | "routing":"block-host-routes,prefix-announce-vpc",
26 | "groups":[]
27 | }
28 | ]
29 | },
30 | {
31 | "networks":[
32 | "subnetB"
33 | ],
34 | "map":[
35 | {
36 | "routing":"block-host-routes,prefix-announce-vpc",
37 | "groups":[]
38 | },
39 | {
40 | "routing":"block-host-routes,prefix-announce-vpc",
41 | "groups":[]
42 | }
43 | ]
44 | }
45 | ]
46 | }
--------------------------------------------------------------------------------
/common/client/testdata/TestPredefinedHosts.json:
--------------------------------------------------------------------------------
1 | {
2 | "networks": [{
3 | "name": "net1",
4 | "cidr": "10.0.0.0/8",
5 | "block_mask" : 28
6 | },
7 | {
8 | "name": "net2",
9 | "cidr": "11.0.0.0/8",
10 | "block_mask" : 28
11 | }
12 | ],
13 | "topologies": [{
14 | "networks": ["net1"],
15 | "map": [{
16 | "cidr": "10.0.0.0/8",
17 | "groups": [{
18 | "cidr": "10.0.0.0/9",
19 | "groups": [
20 | {
21 | "name" : "h1",
22 | "ip": "192.168.99.10"
23 | },
24 | {
25 | "name" : "h2",
26 | "ip": "192.168.99.11"
27 | }
28 | ]
29 | },
30 | {
31 | "cidr": "10.128.0.0/9",
32 | "groups": [{
33 | "name" : "h3",
34 | "ip": "192.168.99.12"
35 | },
36 | {
37 | "name" : "h4",
38 | "ip": "192.168.99.13"
39 | }
40 | ]
41 | }
42 | ]
43 | }]
44 | },
45 | {
46 | "networks": ["net2"],
47 | "map": [{
48 | "cidr": "11.0.0.0/8",
49 | "groups": [{
50 | "name" : "h5",
51 | "ip": "192.168.99.10"
52 | },
53 | {
54 | "name" : "h6",
55 | "ip": "192.168.99.11"
56 | }
57 | ]
58 | }]
59 |
60 | }
61 | ]
62 | }
--------------------------------------------------------------------------------
/common/client/testdata/TestPrefixGenForEmptyGroups.json:
--------------------------------------------------------------------------------
1 | {
2 | "networks": [
3 | {
4 | "name": "net1",
5 | "cidr": "10.0.0.0/16",
6 | "block_mask": 30
7 | }
8 | ],
9 | "topologies": [
10 | {
11 | "networks": [
12 | "net1"
13 | ],
14 | "map": [
15 | {
16 | "groups": [],
17 | "name": "group1"
18 | },
19 | {
20 | "groups": [
21 | {
22 | "groups": [],
23 | "name": "group2a"
24 | },
25 | {
26 | "groups": [],
27 | "name": "group2b"
28 | }
29 | ],
30 | "name": "group2"
31 | }
32 | ]
33 | }
34 | ]
35 | }
36 |
--------------------------------------------------------------------------------
/common/client/testdata/TestRepeatedNetwork.json:
--------------------------------------------------------------------------------
1 | { "networks" : [
2 | { "name" : "net1", "cidr" : "100.11.0.0/20", "block_mask" : 30 },
3 | { "name" : "net2", "cidr" : "100.22.0.0/20", "block_mask" : 30 }
4 | ],
5 | "topologies" : [
6 | {
7 | "networks" : ["net1","net2"],
8 | "map" : [ { "groups" : [] } ]
9 | },
10 | {
11 | "networks" : ["net1","net2"],
12 | "map" : [ { "groups" : [] } ]
13 | }
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/common/client/testdata/TestSegments.json:
--------------------------------------------------------------------------------
1 | {
2 | "networks":[
3 | {
4 | "name":"net1",
5 | "cidr":"10.0.0.0/24",
6 | "block_mask":30
7 | }
8 | ],
9 | "topologies":[
10 | {
11 | "networks":[
12 | "net1"
13 | ],
14 | "map":[
15 | {
16 | "routing":"foo",
17 | "groups": [{
18 | "name":"host1",
19 | "ip":"192.168.0.1"
20 | }]
21 | }
22 | ]
23 | }
24 | ]
25 | }
--------------------------------------------------------------------------------
/common/client/testdata/TestTenants.json:
--------------------------------------------------------------------------------
1 | {
2 | "networks":[
3 | {
4 | "name":"net1",
5 | "cidr":"10.200.0.0/16",
6 | "block_mask":29,
7 | "tenants":[
8 | "tenant1",
9 | "tenant2"
10 | ]
11 | },
12 | {
13 | "name":"net2",
14 | "cidr":"10.220.0.0/16",
15 | "block_mask":28,
16 | "tenants":[
17 | "tenant3"
18 | ]
19 | },
20 | {
21 | "name":"net3",
22 | "cidr":"10.240.0.0/16",
23 | "block_mask":28
24 | }
25 | ],
26 | "topologies":[
27 | {
28 | "networks":[
29 | "net1",
30 | "net2",
31 | "net3"
32 | ],
33 | "map":[
34 | {
35 | "routing":"foo",
36 | "groups": [{
37 | "name":"host1",
38 | "ip":"192.168.0.1"
39 | }]
40 | }
41 | ]
42 | }
43 | ]
44 | }
--------------------------------------------------------------------------------
/common/client/testdata/TestTenantsBug701.json:
--------------------------------------------------------------------------------
1 | {
2 | "networks":[
3 | { "name":"net1", "cidr":"10.200.0.0/26", "block_mask":29 },
4 | { "name":"net2", "cidr":"10.220.0.0/26", "block_mask":29 },
5 | { "name":"net3", "cidr":"10.240.0.0/26", "block_mask":29 }
6 | ],
7 | "topologies":[
8 | {
9 | "networks":[
10 | "net1",
11 | "net2"
12 | ],
13 | "map":[
14 | {
15 | "groups": [
16 | { "name":"host1", "ip":"192.168.0.1" },
17 | { "name":"host2", "ip":"192.168.0.2" },
18 | { "name":"host3", "ip":"192.168.0.3" },
19 | { "name":"host4", "ip":"192.168.0.4" }
20 | ]
21 | }
22 | ]
23 | }
24 | ]
25 | }
26 |
--------------------------------------------------------------------------------
/common/client/testdata/TestTopologyGetOutputKubeadm.json:
--------------------------------------------------------------------------------
1 | {
2 | "networks": [
3 | {
4 | "name": "romana-network",
5 | "cidr": "10.112.0.0/12",
6 | "block_mask": 29,
7 | "tenants": [
8 | "*"
9 | ]
10 | }
11 | ],
12 | "topologies": [
13 | {
14 | "networks": [
15 | "romana-network"
16 | ],
17 | "map": [
18 | {
19 | "groups": [
20 | {
21 | "assignment": {
22 | "beta.kubernetes.io/arch": "amd64",
23 | "beta.kubernetes.io/os": "linux",
24 | "kubernetes.io/hostname": "ip-192-168-99-10",
25 | "node-role.kubernetes.io/master": ""
26 | },
27 | "name": "ip-192-168-99-10",
28 | "ip": "192.168.99.10"
29 | }
30 | ],
31 | "name": "host-group-01",
32 | "cidr": "10.112.0.0/15"
33 | },
34 | {
35 | "name": "host-group-02",
36 | "cidr": "10.114.0.0/15"
37 | },
38 | {
39 | "name": "host-group-03",
40 | "cidr": "10.116.0.0/15"
41 | },
42 | {
43 | "name": "host-group-04",
44 | "cidr": "10.118.0.0/15"
45 | },
46 | {
47 | "name": "host-group-05",
48 | "cidr": "10.120.0.0/15"
49 | },
50 | {
51 | "name": "host-group-06",
52 | "cidr": "10.122.0.0/15"
53 | },
54 | {
55 | "name": "host-group-07",
56 | "cidr": "10.124.0.0/15"
57 | },
58 | {
59 | "name": "host-group-08",
60 | "cidr": "10.126.0.0/15"
61 | }
62 | ]
63 | }
64 | ]
65 | }
66 |
--------------------------------------------------------------------------------
/common/client/testdata/TestUpdateTopology.json:
--------------------------------------------------------------------------------
1 | {
2 | "networks":[
3 | {
4 | "name":"net1",
5 | "cidr":"10.0.0.0/8",
6 | "block_mask":30
7 | }
8 | ],
9 | "topologies":[
10 | {
11 | "networks":[
12 | "net1"
13 | ],
14 | "map":[
15 | {
16 | "routing":"test",
17 | "groups":[
18 | {
19 | "name":"h1",
20 | "ip":"192.168.99.10"
21 | },
22 | {
23 | "name":"h2",
24 | "ip":"192.168.99.11"
25 | }
26 | ]
27 | }
28 | ]
29 | }
30 | ]
31 | }
--------------------------------------------------------------------------------
/common/client/testdata/TestUpdateTopologyInvalidBlockMask.json:
--------------------------------------------------------------------------------
1 | {
2 | "networks": [
3 | {
4 | "name": "net1",
5 | "cidr": "10.0.0.0/24",
6 | "block_mask": 20
7 | }
8 | ],
9 | "topologies": [
10 | {
11 | "networks": [
12 | "net1"
13 | ],
14 | "map": [
15 | {
16 | "groups": []
17 | }
18 | ]
19 | }
20 | ]
21 | }
--------------------------------------------------------------------------------
/common/config.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2016-2017 Pani Networks
2 | // All Rights Reserved.
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may
5 | // not use this file except in compliance with the License. You may obtain
6 | // a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | // License for the specific language governing permissions and limitations
14 | // under the License.
15 |
16 | package common
17 |
18 | // Config is the configuration required for a Romana client library.
19 | // TODO it is here temporarily until circular imports are resolved.
20 | type Config struct {
21 | EtcdEndpoints []string
22 | EtcdPrefix string
23 | InitialTopologyFile *string
24 | Mock bool
25 | }
26 |
--------------------------------------------------------------------------------
/common/defs.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2015-2017 Pani Networks
2 | // All Rights Reserved.
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may
5 | // not use this file except in compliance with the License. You may obtain
6 | // a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | // License for the specific language governing permissions and limitations
14 | // under the License.
15 |
16 | package common
17 |
18 | // Definitions of common structures.
19 |
20 | type FindFlag string
21 |
22 | const (
23 | // Flags to store.Find operation
24 | FindFirst = "findFirst"
25 | FindLast = "findLast"
26 | FindExactlyOne = "findExactlyOne"
27 | FindAll = "findAll"
28 | )
29 |
30 | // Here we only keep type definitions and struct definitions with no behavior.
31 |
32 | // Constants
33 | const (
34 | // For passing in Gorilla Mux context the unmarshalled data
35 | ContextKeyUnmarshalledMap string = "UnmarshalledMap"
36 | // For passing in Gorilla Mux context path variables
37 | ContextKeyQueryVariables string = "QueryVars"
38 | // For passing in Gorilla Mux context the original body data
39 | ContextKeyOriginalBody string = "OriginalBody"
40 | ContextKeyMarshaller string = "Marshaller"
41 | ContextKeyUser string = "User"
42 | ReadWriteTimeoutDelta = 10
43 |
44 | // Name of the query parameter used for request token
45 | RequestTokenQueryParameter = "RequestToken"
46 |
47 | HeaderContentType = "content-type"
48 |
49 | Starting ServiceMessage = "Starting."
50 |
51 | // JSON
52 | TimeoutMessage = "{ \"error\" : \"Timed out\" }"
53 |
54 | // Empty string returned when there is a string return
55 | // but there is an error so no point in returning any
56 | // value.
57 | ErrorNoValue = ""
58 |
59 | // Path for authentication; if this is what is used
60 | // in the request we will not check the token (because
61 | // we are attempting to get a token at this point).
62 | AuthPath = "/auth"
63 |
64 | // Body provided.
65 | HookExecutableBodyArgument = "body"
66 | )
67 |
68 | // LinkResponse structure represents the commonly occurring
69 | // {
70 | // "href" : "https://",
71 | // "rel" : "self"
72 | // }
73 | // part of the response.
74 | type LinkResponse struct {
75 | Href string `json:"href,omitempty"`
76 | Rel string `json:"rel,omitempty"`
77 | }
78 |
79 | // Type definitions
80 | type ServiceMessage string
81 |
82 | // Message to register with the root service the actual
83 | // port a service is listening on.
84 | type PortUpdateMessage struct {
85 | Port uint64 `json:"port"`
86 | }
87 |
88 | // RestServiceInfo describes information about a running
89 | // Romana service.
90 | type RestServiceInfo struct {
91 | // Address being listened on (as host:port)
92 | Address string
93 | // Channel to communicate with the service
94 | Channel chan ServiceMessage
95 | }
96 |
--------------------------------------------------------------------------------
/common/doc.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2016 Pani Networks
2 | // All Rights Reserved.
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may
5 | // not use this file except in compliance with the License. You may obtain
6 | // a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | // License for the specific language governing permissions and limitations
14 | // under the License.
15 |
16 | /*
17 | Package common contains various things common to all Romana services.
18 | */
19 | package common
20 |
--------------------------------------------------------------------------------
/common/log/trace/trace.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2016 Pani Networks
2 | // All Rights Reserved.
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may
5 | // not use this file except in compliance with the License. You may obtain
6 | // a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | // License for the specific language governing permissions and limitations
14 | // under the License.
15 |
16 | package trace
17 |
18 | // These definitions are just helpful conventions for our use of trace
19 | // messages. There is no 'standard' for logging trace levels, but for our
20 | // projects we use them as described below.
21 |
22 | const (
23 | Public int = 1 // Messages on entry of public/exported functions
24 | Private int = 2 // Messages on entry of private/non-exported functions
25 | Inside int = 3 // Messages from inside of functions
26 | )
27 |
--------------------------------------------------------------------------------
/common/rest_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2017 Pani Networks
2 | // All Rights Reserved.
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may
5 | // not use this file except in compliance with the License. You may obtain
6 | // a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | // License for the specific language governing permissions and limitations
14 | // under the License.
15 |
16 | // +build ignore
17 |
18 | package common
19 |
20 | import (
21 | "fmt"
22 | "testing"
23 | "time"
24 |
25 | "github.com/codegangsta/negroni"
26 | "github.com/gorilla/mux"
27 | )
28 |
29 | func TestPanicHandler(t *testing.T) {
30 | var err error
31 | // Create negroni
32 | negroni := negroni.New()
33 | negroni.Use(newPanicRecoveryHandler())
34 |
35 | router := mux.NewRouter().StrictSlash(true)
36 | handlerOk := func(input interface{}, context RestContext) (interface{}, error) {
37 | return "OK", nil
38 | }
39 | route := Route{Handler: handlerOk,
40 | Method: "GET",
41 | Pattern: "/ok",
42 | AuthZChecker: func(ctx RestContext) bool {
43 | return true
44 | },
45 | }
46 | wrappedHandler := wrapHandler(handlerOk, route)
47 | router.
48 | Methods(route.Method).
49 | Path(route.Pattern).
50 | Handler(wrappedHandler)
51 |
52 | handlerPanic := func(input interface{}, context RestContext) (interface{}, error) {
53 | panic("Panic!!!")
54 | }
55 | route = Route{Handler: handlerOk,
56 | Method: "GET",
57 | Pattern: "/panic",
58 | AuthZChecker: func(ctx RestContext) bool {
59 | return true
60 | },
61 | }
62 | wrappedHandler = wrapHandler(handlerPanic, route)
63 | router.
64 | Methods(route.Method).
65 | Path(route.Pattern).
66 | Handler(wrappedHandler)
67 |
68 | negroni.UseHandler(router)
69 |
70 | readWriteDur, _ := time.ParseDuration("10s")
71 | svcInfo, err := RunNegroni(negroni, "localhost:0", readWriteDur)
72 | if err != nil {
73 | t.Errorf("Unexpected error %s", err)
74 | }
75 | t.Log(<-svcInfo.Channel)
76 |
77 | t.Logf("Listening on %s", svcInfo.Address)
78 |
79 | rc, err := NewRestClient(RestClientConfig{
80 | TimeoutMillis: 10000,
81 | })
82 | if err != nil {
83 | t.Errorf("Unexpected error %s", err)
84 | }
85 |
86 | url := fmt.Sprintf("http://%s", svcInfo.Address)
87 | s := ""
88 |
89 | okUrl := fmt.Sprintf("%s/ok", url)
90 | err = rc.Get(okUrl, &s)
91 | if err != nil {
92 | t.Errorf("Unexpected error on %s: %s", okUrl, err)
93 | }
94 | t.Logf("Got %s from %s", s, okUrl)
95 |
96 | panicUrl := fmt.Sprintf("%s/panic", url)
97 | err = rc.Get(panicUrl, &s)
98 | if err == nil {
99 | t.Errorf("Expected error on %s, got %+v", panicUrl, s)
100 | }
101 | switch err := err.(type) {
102 | case HttpError:
103 | if err.StatusCode != 500 {
104 | t.Errorf("Unexpected error on %s: %s", okUrl, err)
105 | }
106 | t.Logf("Got expected error on panic: %+v", s)
107 | default:
108 | t.Errorf("Unexpected error on %s: %s", okUrl, err)
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/common/structures.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2016 Pani Networks
2 | // All Rights Reserved.
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may
5 | // not use this file except in compliance with the License. You may obtain
6 | // a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | // License for the specific language governing permissions and limitations
14 | // under the License.
15 |
16 | package common
17 |
18 | import (
19 | "net"
20 | )
21 |
22 | type IP struct {
23 | ip net.IP
24 | ipv6 bool
25 | }
26 |
27 | func MakeIPv4(a, b, c, d byte) IP {
28 | ip := IP{net.IPv4(a, b, c, d), false}
29 | return ip
30 | }
31 |
32 | func IPv4ToInt(ip net.IP) uint64 {
33 | switch len(ip) {
34 | case 4: // IPv4
35 | return uint64(ip[0])<<24 | uint64(ip[1])<<16 | uint64(ip[2])<<8 | uint64(ip[3])
36 | case 16: // IPv6
37 | return uint64(ip[12])<<24 | uint64(ip[13])<<16 | uint64(ip[14])<<8 | uint64(ip[15])
38 | default:
39 | return 0
40 | }
41 | }
42 |
43 | func IntToIPv4(ipInt uint64) net.IP {
44 | return net.IPv4(byte(ipInt>>24), byte(ipInt>>16), byte(ipInt>>8), byte(ipInt))
45 | }
46 |
--------------------------------------------------------------------------------
/common/testdata/agent_proxy_add_policy.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | body=`echo $1 | cut -d'=' -f2`
3 | echo Posting $body to http://localhost:9630/
4 | echo curl -H 'content-type: application/json' -d @- -X POST http://localhost:9630/
5 | echo $body | curl -H 'content-type: application/json' -d @- -X POST http://localhost:9630/
6 |
--------------------------------------------------------------------------------
/common/testdata/agent_proxy_delete_policy.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | body=`echo $1 | cut -d'=' -f2`
3 | echo $body | curl -H 'content-type: application/json' -d @- -X DELETE http://localhost:9630/
4 |
--------------------------------------------------------------------------------
/common/testdata/demo.rsa:
--------------------------------------------------------------------------------
1 | -----BEGIN RSA PRIVATE KEY-----
2 | MIIEowIBAAKCAQEAn8MGiedFoscQZ3Kx0AcM/O28DFjOsdEM8111Dw1pxMQn+fN7
3 | Bx1zmt4eHyYfGhfk4iQop4p2jNgMZLZGfIgTRADdDR8ibtd0x35HloUV6sCsT2RM
4 | Ww2+otAOMmY3Nt+sTphHchbEVAF3kFg6Wdbw7yHP1IRWRzzES0ZY8RPqi1S1JZ1X
5 | WthrDfOhPIxKhAyN20TrwUrIZ2sFzhAY/SBUvGjSF2rVYfK+LfS+GfWb5YpEjTAI
6 | tOd5MWYt4RmVtruKHc/SlaIUjfR3mDHtFsuZmKBp2pPGHSHlubEK8GXE54fYlo9F
7 | opPG1zGQpwk6vulm1mP4PZCR/jj4BfX6HNg5jQIDAQABAoIBAGAwKaWpyvFgbO37
8 | zjbzp8w4znmYlonU1blI0YvIhZ/kgjUpdS8peksJ5JTYfO1W6YWGQ7vrz8io431X
9 | iUS9IOxWoMCfuDmIG1VpIGztn+6BJr1+I8LGmbqpDYMmq6OsM6/2Wz3Dm10ArR4J
10 | IMiqctFnSBwpuVCWs5CmwDIVzaco24mzdZVReQt/mIr6PDy2EWL62NJAU22+g+ki
11 | vFhQ95meiegkLgIiwFcj322Kzkb4Bovhfk/bJMW3yYJ4tNzIjvqa2yaBk45JRtKK
12 | w/jss6wppU52LhHXUqO3jIY0IPAcbbxwkI35u3tCUl8keputm9uOeltKfqUc0RHN
13 | hjVXf7kCgYEAylJvUXRHlwof25FhgcXtWv0/Idis7EPuPjVssTiOxbx6ppJ9F3gz
14 | iVHkJ8JUGlJvLAvvmxC8VG0ooCf7DLO2F/paA/W2Veey5zuD88wsveV0+RBYoyOQ
15 | 8ah7rpKqM6WsmUGigo3kv9/bmFR3QzVqGt0zxmNz+06scOQS0DZ4BjMCgYEAyiXu
16 | 39Zna4Osgc0lUD43mwfG/K0JhbtvRioob9T8ypS10a7np7w/rVCcvEZBNrf0iOOb
17 | r51VdCWldL40U3Ac3ma3BnNoujm/M5yt0+WtX9BonK+82jt2HcPHzetOwfrbWij8
18 | nK6MlHYIdHCT5uas258EYD331BpUmdOWX2gVgT8CgYAlWfCpJgFtdfYJsB5DOgom
19 | ySgVlI3gFG5wV6aYVSuuf6xZAiRL5+oDyPWJDYw/S8awaAe1JfLO11QvtRHY93h/
20 | lr11i7A8zciufcNPh/sel7Tvoj/q5k8E/ijPQs5fOI/Tv1VEKvP3JFnDFpzp8HuF
21 | OB8t2gB4fb7CHKkIfXXNFwKBgQCgQGYYCK4/60isNecor8oBGq9CzCJKZAaYhv87
22 | eItWRYHF0nRO4stQKCG4l7FHIZm3k0hudbmys81wuQBeTPh1ztO1R3R0fSb4UXL0
23 | EpXY6SylWiPKlWzTt8sDkVEEMkwGM4ve4fkMEG4vcLevSpsqF+uuKDH5cU40DCqf
24 | Q7IYnQKBgCfX8WSqvbdMkK9MQF+p1zFQohajTTN5k2ieZkVoKZFVunArQs3QNSR+
25 | EkXk1BdHehHqSOo4Lu0nbWKBEm9bitxHZBiIlkGjo785jGMaokMbY8P5PM9SaCxd
26 | iwiAOSRpyqw7gGQGCPJ24tLiGwHw0CAZE9kMLaALsTA1Ggrr2gpG
27 | -----END RSA PRIVATE KEY-----
28 |
--------------------------------------------------------------------------------
/common/testdata/demo.rsa.pkcs8:
--------------------------------------------------------------------------------
1 | -----BEGIN PRIVATE KEY-----
2 | MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCfwwaJ50WixxBn
3 | crHQBwz87bwMWM6x0QzzXXUPDWnExCf583sHHXOa3h4fJh8aF+TiJCininaM2Axk
4 | tkZ8iBNEAN0NHyJu13THfkeWhRXqwKxPZExbDb6i0A4yZjc236xOmEdyFsRUAXeQ
5 | WDpZ1vDvIc/UhFZHPMRLRljxE+qLVLUlnVda2GsN86E8jEqEDI3bROvBSshnawXO
6 | EBj9IFS8aNIXatVh8r4t9L4Z9ZvlikSNMAi053kxZi3hGZW2u4odz9KVohSN9HeY
7 | Me0Wy5mYoGnak8YdIeW5sQrwZcTnh9iWj0Wik8bXMZCnCTq+6WbWY/g9kJH+OPgF
8 | 9foc2DmNAgMBAAECggEAYDAppanK8WBs7fvONvOnzDjOeZiWidTVuUjRi8iFn+SC
9 | NSl1Lyl6SwnklNh87VbphYZDu+vPyKjjfVeJRL0g7FagwJ+4OYgbVWkgbO2f7oEm
10 | vX4jwsaZuqkNgyaro6wzr/ZbPcObXQCtHgkgyKpy0WdIHCm5UJazkKbAMhXNpyjb
11 | ibN1lVF5C3+Yivo8PLYRYvrY0kBTbb6D6SK8WFD3mZ6J6CQuAiLAVyPfbYrORvgG
12 | i+F+T9skxbfJgni03MiO+prbJoGTjklG0orD+OyzrCmlTnYuEddSo7eMhjQg8Bxt
13 | vHCQjfm7e0JSXyR6m62b2456W0p+pRzREc2GNVd/uQKBgQDKUm9RdEeXCh/bkWGB
14 | xe1a/T8h2KzsQ+4+NWyxOI7FvHqmkn0XeDOJUeQnwlQaUm8sC++bELxUbSigJ/sM
15 | s7YX+loD9bZV57LnO4PzzCy95XT5EFijI5DxqHuukqozpayZQaKCjeS/39uYVHdD
16 | NWoa3TPGY3P7Tqxw5BLQNngGMwKBgQDKJe7f1mdrg6yBzSVQPjebB8b8rQmFu29G
17 | Kihv1PzKlLXRruenvD+tUJy8RkE2t/SI45uvnVV0JaV0vjRTcBzeZrcGc2i6Ob8z
18 | nK3T5a1f0Gicr7zaO3Ydw8fN607B+ttaKPycroyUdgh0cJPm5qzbnwRgPffUGlSZ
19 | 05ZfaBWBPwKBgCVZ8KkmAW119gmwHkM6CibJKBWUjeAUbnBXpphVK65/rFkCJEvn
20 | 6gPI9YkNjD9LxrBoB7Ul8s7XVC+1Edj3eH+WvXWLsDzNyK59w0+H+x6XtO+iP+rm
21 | TwT+KM9Czl84j9O/VUQq8/ckWcMWnOnwe4U4Hy3aAHh9vsIcqQh9dc0XAoGBAKBA
22 | ZhgIrj/rSKw15yivygEar0LMIkpkBpiG/zt4i1ZFgcXSdE7iy1AoIbiXsUchmbeT
23 | SG51ubKzzXC5AF5M+HXO07VHdHR9JvhRcvQSldjpLKVaI8qVbNO3ywORUQQyTAYz
24 | i97h+QwQbi9wt69KmyoX664oMflxTjQMKp9DshidAoGAJ9fxZKq9t0yQr0xAX6nX
25 | MVCiFqNNM3mTaJ5mRWgpkVW6cCtCzdA1JH4SReTUF0d6EepI6jgu7SdtYoESb1uK
26 | 3EdkGIiWQaOjvzmMYxqiQxtjw/k8z1JoLF2LCIA5JGnKrDuAZAYI8nbi0uIbAfDQ
27 | IBkT2QwtoAuxMDUaCuvaCkY=
28 | -----END PRIVATE KEY-----
29 |
--------------------------------------------------------------------------------
/common/testdata/demo.rsa.pub:
--------------------------------------------------------------------------------
1 | ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCfwwaJ50WixxBncrHQBwz87bwMWM6x0QzzXXUPDWnExCf583sHHXOa3h4fJh8aF+TiJCininaM2AxktkZ8iBNEAN0NHyJu13THfkeWhRXqwKxPZExbDb6i0A4yZjc236xOmEdyFsRUAXeQWDpZ1vDvIc/UhFZHPMRLRljxE+qLVLUlnVda2GsN86E8jEqEDI3bROvBSshnawXOEBj9IFS8aNIXatVh8r4t9L4Z9ZvlikSNMAi053kxZi3hGZW2u4odz9KVohSN9HeYMe0Wy5mYoGnak8YdIeW5sQrwZcTnh9iWj0Wik8bXMZCnCTq+6WbWY/g9kJH+OPgF9foc2DmN grisha@MacBook-Pro-4.local
2 |
--------------------------------------------------------------------------------
/common/testdata/demo.rsa.pub.pkcs8:
--------------------------------------------------------------------------------
1 | -----BEGIN PUBLIC KEY-----
2 | MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAn8MGiedFoscQZ3Kx0AcM
3 | /O28DFjOsdEM8111Dw1pxMQn+fN7Bx1zmt4eHyYfGhfk4iQop4p2jNgMZLZGfIgT
4 | RADdDR8ibtd0x35HloUV6sCsT2RMWw2+otAOMmY3Nt+sTphHchbEVAF3kFg6Wdbw
5 | 7yHP1IRWRzzES0ZY8RPqi1S1JZ1XWthrDfOhPIxKhAyN20TrwUrIZ2sFzhAY/SBU
6 | vGjSF2rVYfK+LfS+GfWb5YpEjTAItOd5MWYt4RmVtruKHc/SlaIUjfR3mDHtFsuZ
7 | mKBp2pPGHSHlubEK8GXE54fYlo9FopPG1zGQpwk6vulm1mP4PZCR/jj4BfX6HNg5
8 | jQIDAQAB
9 | -----END PUBLIC KEY-----
10 |
--------------------------------------------------------------------------------
/common/testdata/romana.auth.yaml:
--------------------------------------------------------------------------------
1 | services:
2 | - service: root
3 | api:
4 | host: localhost
5 | port: 9600
6 | auth_public: ../common/testdata/demo.rsa.pub.pkcs8
7 | config:
8 | auth: yes
9 | auth_private: ../common/testdata/demo.rsa.pkcs8
10 | store:
11 | type: sqlite3
12 | database: /tmp/auth.sqlite3
13 | - service: ipam
14 | api:
15 | host: localhost
16 | port: 9601
17 | auth_public: ../common/testdata/demo.rsa.pub.pkcs8
18 | config:
19 | store:
20 | type: sqlite3
21 | database: /tmp/ipam.sqlite3
22 | - service: tenant
23 | api:
24 | host: localhost
25 | port: 9602
26 | auth_public: ../common/testdata/demo.rsa.pub.pkcs8
27 | config:
28 | store:
29 | type: sqlite3
30 | host: localhost
31 | database: /tmp/tenant.sqlite3
32 | - service: topology
33 | api:
34 | host: localhost
35 | port: 9603
36 | auth_public: ../common/testdata/demo.rsa.pub.pkcs8
37 | config:
38 | store:
39 | type: sqlite3
40 | database: /tmp/topology.sqlite3
41 | datacenter:
42 | ip_version: 4
43 | cidr: 10.0.0.0/8
44 | host_bits: 8
45 | tenant_bits: 4
46 | segment_bits: 4
47 | endpoint_space_bits: 0
48 | endpoint_bits: 8
49 | - service: agent
50 | api:
51 | host: 0.0.0.0
52 | port: 9604
53 | auth_public: ../common/testdata/demo.rsa.pub.pkcs8
54 | config:
55 | lease_file : "/etc/ethers"
56 | wait_for_iface_try : 6
57 | store:
58 | type: sqlite3
59 | database: /var/tmp/agent.sqlite3
60 | - service: policy
61 | api:
62 | host: localhost
63 | port: 9605
64 | auth_public: ../common/testdata/demo.rsa.pub.pkcs8
65 | config:
66 | store:
67 | type: sqlite3
68 | database: /tmp/topology.sqlite3
69 |
--------------------------------------------------------------------------------
/common/testdata/romana.hooks.yaml:
--------------------------------------------------------------------------------
1 | services:
2 | - service: root
3 | api:
4 | host: localhost
5 | hooks:
6 | - executable: ../common/testdata/hook.sh
7 | pattern: /config/{serviceName}
8 | method: GET
9 | when: before
10 | output: /tmp/hook.txt
11 | - executable: ../common/testdata/hook_bad.sh
12 | pattern: /config/{serviceName}/port
13 | method: POST
14 | when: after
15 | output: /tmp/hook_bad.txt
16 | config:
17 | store:
18 | type: sqlite3
19 | database: /var/tmp/auth.sqlite3
20 | - service: ipam
21 | api:
22 | host: localhost
23 | config:
24 | store:
25 | type: sqlite3
26 | database: /var/tmp/ipam.sqlite3
27 | - service: tenant
28 | api:
29 | host: localhost
30 | config:
31 | store:
32 | type: sqlite3
33 | database: /var/tmp/tenant.sqlite3
34 | - service: topology
35 | api:
36 | host: localhost
37 | config:
38 | store:
39 | type: sqlite3
40 | database: /var/tmp/topology.sqlite3
41 | datacenter:
42 | ip_version: 4
43 | cidr: 10.0.0.0/8
44 | host_bits: 8
45 | tenant_bits: 4
46 | segment_bits: 4
47 | endpoint_space_bits: 0
48 | endpoint_bits: 8
49 | - service: agent
50 | api:
51 | host: 0.0.0.0
52 | config:
53 | lease_file : "/etc/ethers"
54 | wait_for_iface_try : 6
--------------------------------------------------------------------------------
/common/testdata/romana.sample.yaml:
--------------------------------------------------------------------------------
1 | services:
2 | - service: root
3 | api:
4 | host: localhost
5 | port: 9600
6 | config:
7 | store:
8 | type: sqlite3
9 | database: /var/tmp/ipam.sqlite3
10 | - service: ipam
11 | api:
12 | host: localhost
13 | port: 9601
14 | config:
15 | store:
16 | endpoints:
17 | - localhost:2379
18 | prefix: /romana/ipam
19 | - service: policy
20 | api:
21 | host: localhost
22 | port: 9602
23 | config:
24 | store:
25 | endpoints:
26 | - localhost:2379
27 | prefix: /romana/policy
28 | - service: agent
29 | api:
30 | host: 0.0.0.0
31 | port: 9603
32 | hooks:
33 | - executable: ../common/testdata/agent_proxy_add_policy.sh
34 | pattern: /policies
35 | method: POST
36 | when: before
37 | output: /var/log/policies-hook.log
38 | - executable: ../common/testdata/agent_proxy_delete_policy.sh
39 | pattern: /policies
40 | method: DELETE
41 | when: before
42 | output: /var/log/policies-hook.log
43 | config:
44 | lease_file : "/etc/ethers"
45 | wait_for_iface_try : 6
46 | store:
47 | type: sqlite3
48 | database: /var/tmp/agent.sqlite3
49 | - service: kubernetesListener
50 | api:
51 | host: 0.0.0.0
52 | port: 9604
53 | config:
54 | kubernetes_url : "http://localhost"
55 | namespace_notification_path: "/api/v1/namespaces/?watch=true"
56 | policy_notification_path_prefix : "/apis/extensions/v1beta1/namespaces/"
57 | policy_notification_path_postfix : "/networkpolicies/?watch=true"
58 | segment_label_name: "tier"
59 |
--------------------------------------------------------------------------------
/common/testdata/romanad.yaml:
--------------------------------------------------------------------------------
1 | romanad:
2 | host: localhost
3 | port: 9600
4 | etcd_prefix: /romana
5 | etcd_endpoints:
6 | - localhost:2379
7 | agent:
8 | host: 0.0.0.0
9 | port: 9604
10 | lease_file : "/etc/ethers"
11 | wait_for_iface_try : 6
12 | kubernetesListener
13 | host: 0.0.0.0
14 | port: 9606
15 | kubernetes_url : "http://localhost"
16 | namespace_notification_path: "/api/v1/namespaces/?watch=true"
17 | policy_notification_path_prefix : "/apis/extensions/v1beta1/namespaces/"
18 | policy_notification_path_postfix : "/networkpolicies/?watch=true"
19 | segment_label_name: "tier"
20 |
21 |
--------------------------------------------------------------------------------
/doc/policy-examples/example-policy-dhcp-host.json:
--------------------------------------------------------------------------------
1 | {
2 | "ingress": [
3 | {
4 | "rules": [
5 | {
6 | "ports": [
7 | 67
8 | ],
9 | "protocol": "UDP"
10 | }
11 | ],
12 | "peers": [
13 | {
14 | "peer": "local"
15 | }
16 | ]
17 | }
18 | ],
19 | "applied_to": [
20 | {
21 | "dest": "host"
22 | }
23 | ],
24 | "name": "pol-dhcp-host",
25 | "direction": "ingress"
26 | }
27 |
--------------------------------------------------------------------------------
/doc/policy-examples/example-policy-dhcp-vm.json:
--------------------------------------------------------------------------------
1 | {
2 | "applied_to": [
3 | {
4 | "dest": "local"
5 | }
6 | ],
7 | "ingress": [
8 | {
9 | "peers": [
10 | {
11 | "peer": "host"
12 | }
13 | ],
14 | "rules": [
15 | {
16 | "ports": [
17 | 68
18 | ],
19 | "protocol": "UDP"
20 | }
21 | ]
22 | }
23 | ],
24 | "name": "pol-dhcp-vm",
25 | "direction": "ingress"
26 | }
27 |
--------------------------------------------------------------------------------
/doc/policy-examples/example-policy-icmp-vm.json:
--------------------------------------------------------------------------------
1 | {
2 | "ingress": [
3 | {
4 | "rules": [
5 | {
6 | "protocol": "ICMP"
7 | }
8 | ],
9 | "peers": [
10 | {
11 | "peer": "host"
12 | }
13 | ]
14 | }
15 | ],
16 | "applied_to": [
17 | {
18 | "dest": "local"
19 | }
20 | ],
21 | "name": "pol-icmp-vm",
22 | "direction": "ingress"
23 | }
24 |
--------------------------------------------------------------------------------
/doc/policy-examples/example-policy-ssh-vm.json:
--------------------------------------------------------------------------------
1 | {
2 | "ingress": [
3 | {
4 | "rules": [
5 | {
6 | "ports": [
7 | 22
8 | ],
9 | "protocol": "TCP"
10 | }
11 | ],
12 | "applied_to": [
13 | {
14 | "dest": "local"
15 | }
16 | ]
17 | }
18 | ],
19 | "peers": [
20 | {
21 | "peer": "host"
22 | }
23 | ],
24 | "name": "pol-ssh-vm",
25 | "direction": "ingress"
26 | }
27 |
--------------------------------------------------------------------------------
/doc/policy-examples/policy-service-agent.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "pa-pol",
3 | "description": "Policy Service <--> Agent policy example.",
4 | "rules": [
5 | {
6 | "ports": [
7 | 80,
8 | 443
9 | ],
10 | "protocol": "TCP"
11 | }
12 | ],
13 | "peers": [
14 | {
15 | "tenant_network_id": 1,
16 | "segment_network_id": 2
17 | }
18 | ],
19 | "direction": "ingress",
20 | "external_id": "external_id_1",
21 | "applied_to": [
22 | {
23 | "tenant_network_id": 1,
24 | "segment_network_id": 1
25 | }
26 | ],
27 | "datacenter": {
28 | "ip_version": 4,
29 | "cidr": "10.0.0.0/8",
30 | "port_bits": 8,
31 | "tenant_bits": 4,
32 | "segment_bits": 4,
33 | "endpoint_space_bits": 0,
34 | "endpoint_bits": 8
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/doc/policy.md:
--------------------------------------------------------------------------------
1 | ### Romana Policy Service and Policy Examples
2 |
3 | #### Sample Romana Policy
4 | Sample Romana polices with various combinations of rules are shown [here](examples/).
5 |
6 | #### Applying New Policy
7 | Romana policies can be applied by calling the Policy API
8 | directly or using the Romana CLI. An example of applying
9 | a policy is shown below:
10 | ```bash
11 | $ cat policy.json
12 | {
13 | "securitypolicies": [{
14 | "name": "policy1",
15 | "description": "sample policy opening ssh, http and https ports",
16 | "direction": "ingress",
17 | "applied_to": [{
18 | "tenant": "demo",
19 | "segment": "default"
20 | }],
21 | "peers": [{
22 | "peer": "any"
23 | }],
24 | "rules": [{
25 | "protocol": "tcp",
26 | "ports": [22, 80, 443]
27 | }]
28 | }]
29 | }
30 |
31 | $ romana policy add policy.json
32 | New Policies Processed:
33 | Id Policy Name Direction Successful Applied?
34 | 1 policy1 ingress true
35 |
36 | $ romana policy list -f json
37 | [{
38 | "direction": "ingress",
39 | "description": "sample policy opening ssh, http and https ports",
40 | "name": "policy1",
41 | "id": 1,
42 | "external_id": "policy1",
43 | "applied_to": [{
44 | "tenant": "demo",
45 | "segment": "default"
46 | }],
47 | "peers": [{
48 | "peer": "any"
49 | }],
50 | "rules": [{
51 | "protocol": "TCP",
52 | "ports": [
53 | 22,
54 | 80,
55 | 443
56 | ]
57 | }]
58 | }]
59 | ```
60 |
--------------------------------------------------------------------------------
/listener/processor.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2016 Pani Networks
2 | // All Rights Reserved.
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may
5 | // not use this file except in compliance with the License. You may obtain
6 | // a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | // License for the specific language governing permissions and limitations
14 | // under the License.
15 |
16 | package listener
17 |
18 | import (
19 | "reflect"
20 | "time"
21 |
22 | "github.com/romana/core/common/log/trace"
23 | log "github.com/romana/rlog"
24 |
25 | "k8s.io/client-go/pkg/api/v1"
26 | "k8s.io/client-go/pkg/apis/extensions/v1beta1"
27 | )
28 |
29 | const (
30 | // TODO make a parameter. Stas.
31 | processorTickTime = 4
32 | )
33 |
34 | // process is a goroutine that consumes resource update events coming from
35 | // Kubernetes and:
36 | // 1. On receiving an added or deleted event:
37 | // i. add it to the queue
38 | // ii. on a timer event, send the events to handleNetworkPolicyEvents and empty the queue
39 | // 2. On receiving a done event, exit the goroutine
40 | func (l *KubeListener) process(in <-chan Event, done chan struct{}) {
41 | log.Debugf("KubeListener: process(): Entered with in %v, done %v", in, done)
42 |
43 | timer := time.NewTicker(processorTickTime * time.Second)
44 | var networkPolicyEvents []Event
45 |
46 | go func() {
47 | for {
48 | select {
49 | case <-timer.C:
50 | if len(networkPolicyEvents) > 0 {
51 | log.Infof("Calling network policy handler for scheduled %d events", len(networkPolicyEvents))
52 | handleNetworkPolicyEvents(networkPolicyEvents, l)
53 | networkPolicyEvents = nil
54 | }
55 | case e := <-in:
56 | log.Debugf("KubeListener: process(): Got %v", e)
57 | switch obj := e.Object.(type) {
58 | case *v1beta1.NetworkPolicy:
59 | log.Tracef(trace.Inside, "Scheduing network policy action, now scheduled %d actions", len(networkPolicyEvents))
60 | networkPolicyEvents = append(networkPolicyEvents, e)
61 | case *v1.Namespace:
62 | log.Tracef(trace.Inside, "Processor received namespace")
63 | handleNamespaceEvent(e, l)
64 | default:
65 | log.Errorf("Processor received an event of unkonwn type %s, ignoring object %s", reflect.TypeOf(obj), obj)
66 | }
67 | case <-done:
68 | log.Debugf("KubeListener: process(): Got done")
69 | timer.Stop()
70 | return
71 | }
72 | }
73 | }()
74 | }
75 |
--------------------------------------------------------------------------------
/listener/resources_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2016 Pani Networks
2 | // All Rights Reserved.
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may
5 | // not use this file except in compliance with the License. You may obtain
6 | // a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | // License for the specific language governing permissions and limitations
14 | // under the License.
15 |
16 | // +build ignore
17 |
18 | package listener
19 |
20 | import (
21 | "testing"
22 |
23 | "github.com/romana/core/common"
24 | "github.com/romana/core/common/client"
25 |
26 | "k8s.io/client-go/pkg/api/v1"
27 | "k8s.io/client-go/pkg/apis/extensions/v1beta1"
28 | )
29 |
30 | func TestSyncNetworkPolicies(t *testing.T) {
31 |
32 | var allRomanaPolicies []common.Policy
33 | var kubePolicies []v1beta1.NetworkPolicy
34 | getAllPoliciesFunc = func(client *client.Client) ([]common.Policy, error) {
35 | return allRomanaPolicies, nil
36 | }
37 |
38 | allRomanaPolicies = []common.Policy{
39 | common.Policy{
40 | Name: "donotdelete",
41 | },
42 | common.Policy{
43 | Name: "kube.default.deleteme",
44 | },
45 | common.Policy{
46 | Name: "kube.default.newPolicy1",
47 | },
48 | }
49 |
50 | kubePolicies = []v1beta1.NetworkPolicy{
51 | v1beta1.NetworkPolicy{
52 | ObjectMeta: v1.ObjectMeta{Name: "newPolicy1"},
53 | },
54 | v1beta1.NetworkPolicy{
55 | ObjectMeta: v1.ObjectMeta{Name: "newPolicy2"},
56 | },
57 | }
58 |
59 | l := &KubeListener{}
60 | newKubePolicies, oldRomanaPolicies, _ := l.syncNetworkPolicies(kubePolicies)
61 |
62 | if len(oldRomanaPolicies) != 1 || len(newKubePolicies) != 1 {
63 | t.Errorf("Received %d newKubePolicies (expect 1) and %d oldRomanaPolicies (expect 1)", len(newKubePolicies), len(oldRomanaPolicies))
64 | }
65 |
66 | if oldRomanaPolicies[0].Name != "kube.default.deleteme" {
67 | t.Errorf("Wrong romana policy scheduled for deletion %s - expected kube.default.deleteme", oldRomanaPolicies[0])
68 | }
69 |
70 | newKubePolicy, ok := newKubePolicies[0].Object.(v1beta1.NetworkPolicy)
71 | if !ok {
72 | t.Error("Failed to cast v1beta1.NetworkPolicy")
73 | }
74 |
75 | if newKubePolicy.ObjectMeta.Name != "newPolicy2" {
76 | t.Errorf("Wrong kube policy scheduled for creation %s - expected newPolicy2", newKubePolicy.ObjectMeta.Name)
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/listener/testdata/any-source.json:
--------------------------------------------------------------------------------
1 | {"id":"kube.tenant-a.pol1.d3122af3-a4cc-11e7-a1ea-068bf013416e","direction":"ingress","applied_to":[{"tenant_id":"tenant-a","segment_id":"backend"}],"ingress":[{"peers":[{"peer":"any"}],"rules":[{"protocol":"tcp","ports":[80]}]}]}
2 |
--------------------------------------------------------------------------------
/listener/testdata/any-source.kube:
--------------------------------------------------------------------------------
1 | {
2 | "apiVersion": "extensions/v1beta1",
3 | "kind": "NetworkPolicy",
4 | "metadata": {
5 | "name": "pol1",
6 | "namespace": "tenant-a"
7 | },
8 | "spec": {
9 | "ingress": [
10 | {
11 | "ports": [
12 | {
13 | "port": 80,
14 | "protocol": "TCP"
15 | }
16 | ]
17 | }
18 | ],
19 | "podSelector": {
20 | "matchLabels": {
21 | "romana.io/segment": "backend"
22 | }
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/listener/testdata/any-source.yml:
--------------------------------------------------------------------------------
1 | apiVersion: extensions/v1beta1
2 | kind: NetworkPolicy
3 | metadata:
4 | name: pol1
5 | namespace: tenant-a
6 | spec:
7 | podSelector:
8 | matchLabels:
9 | romana.io/segment: backend
10 | ingress:
11 | - from:
12 | ports:
13 | - protocol: TCP
14 | port: 80
15 |
--------------------------------------------------------------------------------
/listener/testdata/demo-policy.json:
--------------------------------------------------------------------------------
1 | {"id":"kube.tenant-a.pol1.a8e9618f-ab1d-11e7-a1ea-068bf013416e","direction":"ingress","applied_to":[{"tenant_id":"tenant-a","segment_id":"backend"}],"ingress":[{"peers":[{"tenant_id":"tenant-a","segment_id":"frontend"}],"rules":[{"protocol":"tcp","ports":[80]}]}]}
2 |
--------------------------------------------------------------------------------
/listener/testdata/demo-policy.kube:
--------------------------------------------------------------------------------
1 | {
2 | "apiVersion": "extensions/v1beta1",
3 | "kind": "NetworkPolicy",
4 | "metadata": {
5 | "name": "pol1",
6 | "namespace": "tenant-a"
7 | },
8 | "spec": {
9 | "ingress": [
10 | {
11 | "from": [
12 | {
13 | "podSelector": {
14 | "matchLabels": {
15 | "romana.io/segment": "frontend"
16 | }
17 | }
18 | }
19 | ],
20 | "ports": [
21 | {
22 | "port": 80,
23 | "protocol": "TCP"
24 | }
25 | ]
26 | }
27 | ],
28 | "podSelector": {
29 | "matchLabels": {
30 | "romana.io/segment": "backend"
31 | }
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/listener/testdata/foreing-source-tenant.json:
--------------------------------------------------------------------------------
1 | {"id":"kube.tenant-a.pol1.0ef621c5-a4cd-11e7-a1ea-068bf013416e","direction":"ingress","applied_to":[{"tenant_id":"tenant-a","segment_id":"backend"}],"ingress":[{"peers":[{"tenant_id":"kube-system"}],"rules":[{"protocol":"tcp","ports":[80]}]}]}
2 |
--------------------------------------------------------------------------------
/listener/testdata/foreing-source-tenant.kube:
--------------------------------------------------------------------------------
1 | {
2 | "apiVersion": "extensions/v1beta1",
3 | "kind": "NetworkPolicy",
4 | "metadata": {
5 | "name": "pol1",
6 | "namespace": "tenant-a"
7 | },
8 | "spec": {
9 | "ingress": [
10 | {
11 | "from": [
12 | {
13 | "namespaceSelector": {
14 | "matchLabels": {
15 | "namespace": "kube-system"
16 | }
17 | }
18 | }
19 | ],
20 | "ports": [
21 | {
22 | "port": 80,
23 | "protocol": "TCP"
24 | }
25 | ]
26 | }
27 | ],
28 | "podSelector": {
29 | "matchLabels": {
30 | "romana.io/segment": "backend"
31 | }
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/listener/testdata/foreing-source-tenant.yml:
--------------------------------------------------------------------------------
1 | apiVersion: extensions/v1beta1
2 | kind: NetworkPolicy
3 | metadata:
4 | name: pol1
5 | namespace: tenant-a
6 | spec:
7 | podSelector:
8 | matchLabels:
9 | romana.io/segment: backend
10 | ingress:
11 | - from:
12 | - namespaceSelector:
13 | matchLabels:
14 | namespace: kube-system
15 | ports:
16 | - protocol: TCP
17 | port: 80
18 |
--------------------------------------------------------------------------------
/listener/testdata/no-rules.json:
--------------------------------------------------------------------------------
1 | {"id":"kube.tenant-a.pol1.4bf9aba4-a4cd-11e7-a1ea-068bf013416e","direction":"ingress","applied_to":[{"tenant_id":"tenant-a","segment_id":"backend"}],"ingress":[{"peers":[{"tenant_id":"tenant-a","segment_id":"frontend"}],"rules":[{"protocol":"any"}]}]}
2 |
--------------------------------------------------------------------------------
/listener/testdata/no-rules.kube:
--------------------------------------------------------------------------------
1 | {
2 | "apiVersion": "extensions/v1beta1",
3 | "kind": "NetworkPolicy",
4 | "metadata": {
5 | "name": "pol1",
6 | "namespace": "tenant-a"
7 | },
8 | "spec": {
9 | "ingress": [
10 | {
11 | "from": [
12 | {
13 | "podSelector": {
14 | "matchLabels": {
15 | "romana.io/segment": "frontend"
16 | }
17 | }
18 | }
19 | ]
20 | }
21 | ],
22 | "podSelector": {
23 | "matchLabels": {
24 | "romana.io/segment": "backend"
25 | }
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/listener/testdata/no-rules.yml:
--------------------------------------------------------------------------------
1 | apiVersion: extensions/v1beta1
2 | kind: NetworkPolicy
3 | metadata:
4 | name: pol1
5 | namespace: tenant-a
6 | spec:
7 | podSelector:
8 | matchLabels:
9 | romana.io/segment: backend
10 | ingress:
11 | - from:
12 | - podSelector:
13 | matchLabels:
14 | romana.io/segment: frontend
15 | ports:
16 |
--------------------------------------------------------------------------------
/listener/testdata/no-source-segment.json:
--------------------------------------------------------------------------------
1 | {"id":"kube.tenant-a.pol1.7bcdb586-a4cd-11e7-a1ea-068bf013416e","direction":"ingress","applied_to":[{"tenant_id":"tenant-a","segment_id":"backend"}],"ingress":[{"peers":[{"tenant_id":"tenant-a"}],"rules":[{"protocol":"tcp","ports":[80]}]}]}
2 |
--------------------------------------------------------------------------------
/listener/testdata/no-source-segment.kube:
--------------------------------------------------------------------------------
1 | {
2 | "apiVersion": "extensions/v1beta1",
3 | "kind": "NetworkPolicy",
4 | "metadata": {
5 | "name": "pol1",
6 | "namespace": "tenant-a"
7 | },
8 | "spec": {
9 | "ingress": [
10 | {
11 | "from": [
12 | {
13 | "podSelector": {
14 | "matchLabels": {
15 | "free-range": "tenbucks"
16 | }
17 | }
18 | }
19 | ],
20 | "ports": [
21 | {
22 | "port": 80,
23 | "protocol": "TCP"
24 | }
25 | ]
26 | }
27 | ],
28 | "podSelector": {
29 | "matchLabels": {
30 | "romana.io/segment": "backend"
31 | }
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/listener/testdata/no-source-segment.yml:
--------------------------------------------------------------------------------
1 | apiVersion: extensions/v1beta1
2 | kind: NetworkPolicy
3 | metadata:
4 | name: pol1
5 | namespace: tenant-a
6 | spec:
7 | podSelector:
8 | matchLabels:
9 | romana.io/segment: backend
10 | ingress:
11 | - from:
12 | - podSelector:
13 | matchLabels:
14 | free-range: tenbucks
15 | ports:
16 | - protocol: TCP
17 | port: 80
18 |
--------------------------------------------------------------------------------
/listener/testdata/no-target-segment.json:
--------------------------------------------------------------------------------
1 | {"id":"kube.tenant-a.pol1.a7cac6f1-a4cd-11e7-a1ea-068bf013416e","direction":"ingress","applied_to":[{"tenant_id":"tenant-a"}],"ingress":[{"peers":[{"tenant_id":"tenant-a","segment_id":"frontend"}],"rules":[{"protocol":"tcp","ports":[80]}]}]}
2 |
--------------------------------------------------------------------------------
/listener/testdata/no-target-segment.kube:
--------------------------------------------------------------------------------
1 | {
2 | "apiVersion": "extensions/v1beta1",
3 | "kind": "NetworkPolicy",
4 | "metadata": {
5 | "name": "pol1",
6 | "namespace": "tenant-a"
7 | },
8 | "spec": {
9 | "ingress": [
10 | {
11 | "from": [
12 | {
13 | "podSelector": {
14 | "matchLabels": {
15 | "romana.io/segment": "frontend"
16 | }
17 | }
18 | }
19 | ],
20 | "ports": [
21 | {
22 | "port": 80,
23 | "protocol": "TCP"
24 | }
25 | ]
26 | }
27 | ],
28 | "podSelector": {
29 | "matchLabels": {
30 | "free-range": "tenbucks"
31 | }
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/listener/testdata/no-target-segment.yml:
--------------------------------------------------------------------------------
1 | apiVersion: extensions/v1beta1
2 | kind: NetworkPolicy
3 | metadata:
4 | name: pol1
5 | namespace: tenant-a
6 | spec:
7 | podSelector:
8 | matchLabels:
9 | free-range: tenbucks
10 | ingress:
11 | - from:
12 | - podSelector:
13 | matchLabels:
14 | romana.io/segment: frontend
15 | ports:
16 | - protocol: TCP
17 | port: 80
18 |
--------------------------------------------------------------------------------
/pkg/policytools/doc.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2016 Pani Networks
2 | // All Rights Reserved.
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may
5 | // not use this file except in compliance with the License. You may obtain
6 | // a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | // License for the specific language governing permissions and limitations
14 | // under the License.
15 |
16 | package policytools
17 |
--------------------------------------------------------------------------------
/pkg/policytools/internal/gen/main.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2016 Pani Networks
2 | // All Rights Reserved.
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may
5 | // not use this file except in compliance with the License. You may obtain
6 | // a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | // License for the specific language governing permissions and limitations
14 | // under the License.
15 |
16 | // +build ignore
17 |
18 | package main
19 |
20 | import (
21 | "encoding/csv"
22 | "flag"
23 | "os"
24 | "text/template"
25 | )
26 |
27 | func main() {
28 | templatePath := flag.String("template", "", "template to render")
29 | dataFilePath := flag.String("data", "", "")
30 | outputPath := flag.String("out", "", "")
31 | flag.Parse()
32 |
33 | template, err := template.ParseFiles(*templatePath)
34 | if err != nil {
35 | panic(err)
36 | }
37 |
38 | data, err := os.Open(*dataFilePath)
39 | if err != nil {
40 | panic(err)
41 | }
42 |
43 | r := csv.NewReader(data)
44 | r.Comma = '\t'
45 | r.LazyQuotes = true
46 |
47 | records, err := r.ReadAll()
48 | if err != nil {
49 | panic(err)
50 | }
51 |
52 | out, err := os.OpenFile(*outputPath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644)
53 | if err != nil {
54 | panic(err)
55 | }
56 | defer out.Close()
57 |
58 | err = template.Execute(out, records)
59 | if err != nil {
60 | panic(err)
61 | }
62 |
63 | err = out.Close()
64 | if err != nil {
65 | panic(err)
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/pkg/policytools/iterator.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2016 Pani Networks
2 | // All Rights Reserved.
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may
5 | // not use this file except in compliance with the License. You may obtain
6 | // a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | // License for the specific language governing permissions and limitations
14 | // under the License.
15 |
16 | package policytools
17 |
18 | import (
19 | "fmt"
20 |
21 | "github.com/romana/core/common/api"
22 | )
23 |
24 | // PolicyIterator provides a way to iterate over every combination of a
25 | // target * peer * rule in a list of policies.
26 | type PolicyIterator struct {
27 | policies []api.Policy
28 | policyIdx int
29 | targetIdx int
30 | ingressIdx int
31 | peerIdx int
32 | ruleIdx int
33 | started bool
34 | }
35 |
36 | // New creates a new PolicyIterator.
37 | func NewPolicyIterator(policies []api.Policy) (*PolicyIterator, error) {
38 | if len(policies) == 0 {
39 | return nil, fmt.Errorf("must have non empty policies list")
40 | }
41 |
42 | emptyIngress := func(p api.Policy) bool {
43 | return len(p.Ingress) == 0
44 | }
45 |
46 | emptyRules := func(p api.Policy) bool {
47 | for _, i := range p.Ingress {
48 | if len(i.Rules) == 0 {
49 | return true
50 | }
51 | }
52 | return false
53 | }
54 |
55 | emptyPeers := func(p api.Policy) bool {
56 | for _, i := range p.Ingress {
57 | if len(i.Peers) == 0 {
58 | return true
59 | }
60 | }
61 | return false
62 | }
63 |
64 | emptyTargets := func(p api.Policy) bool {
65 | return len(p.AppliedTo) == 0
66 | }
67 |
68 | for _, p := range policies {
69 | if emptyIngress(p) || emptyTargets(p) || emptyPeers(p) || emptyRules(p) {
70 | return nil, fmt.Errorf("policy %s has .Ingress .AppliedTo .Peers or .Rules field empty", p)
71 | }
72 | }
73 |
74 | return &PolicyIterator{policies: policies}, nil
75 | }
76 |
77 | // Next advances policy iterator to the next combination
78 | // of policy * target * peer * rule. It returns false if iterator
79 | // can not advance any further, otherwise it returs true.
80 | func (i *PolicyIterator) Next() bool {
81 | if !i.started {
82 | i.started = true
83 | return true
84 | }
85 |
86 | policy, _, ingress, _, _ := i.items()
87 |
88 | if i.ruleIdx < len(ingress.Rules)-1 {
89 | i.ruleIdx += 1
90 | return true
91 | }
92 |
93 | if i.peerIdx < len(ingress.Peers)-1 {
94 | i.peerIdx += 1
95 | i.ruleIdx = 0
96 | return true
97 | }
98 |
99 | if i.ingressIdx < len(policy.Ingress)-1 {
100 | i.ingressIdx += 1
101 | i.ruleIdx = 0
102 | i.peerIdx = 0
103 | return true
104 | }
105 |
106 | if i.targetIdx < len(policy.AppliedTo)-1 {
107 | i.targetIdx += 1
108 | i.ingressIdx = 0
109 | i.ruleIdx = 0
110 | i.peerIdx = 0
111 | return true
112 | }
113 |
114 | if i.policyIdx < len(i.policies)-1 {
115 | i.policyIdx += 1
116 | i.targetIdx = 0
117 | i.ingressIdx = 0
118 | i.ruleIdx = 0
119 | i.peerIdx = 0
120 | return true
121 | }
122 |
123 | return false
124 | }
125 |
126 | // Items retrieves current combination of policy * target * peer * rule from iterator.
127 | func (i PolicyIterator) Items() (api.Policy, api.Endpoint, api.Endpoint, api.Rule) {
128 | policy, target, _, peer, rule := i.items()
129 | return policy, target, peer, rule
130 | }
131 |
132 | func (i PolicyIterator) items() (api.Policy, api.Endpoint, api.RomanaIngress, api.Endpoint, api.Rule) {
133 | policy := i.policies[i.policyIdx]
134 | target := policy.AppliedTo[i.targetIdx]
135 | ingress := policy.Ingress[i.ingressIdx]
136 | peer := policy.Ingress[i.ingressIdx].Peers[i.peerIdx]
137 | rule := policy.Ingress[i.ingressIdx].Rules[i.ruleIdx]
138 | return policy, target, ingress, peer, rule
139 | }
140 |
--------------------------------------------------------------------------------
/pkg/policytools/templates/blueprint.go_template:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2016 Pani Networks
2 | // All Rights Reserved.
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may
5 | // not use this file except in compliance with the License. You may obtain
6 | // a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | // License for the specific language governing permissions and limitations
14 | // under the License.
15 |
16 | // This file if generate. Do not edit.
17 |
18 | package policytools
19 |
20 | import (
21 | "github.com/romana/core/agent/firewall"
22 | "github.com/romana/core/common/api"
23 | )
24 |
25 | var Blueprints = map[string]RuleBlueprint{
26 | {{ range . }} {{ $Direction := index . 0 }} {{ $Scheme := index . 1}} {{ $PeerType := index . 2}} {{ $TargetType := index . 3}} {{ $BaseChain := index . 4 }} {{ $TopRuleMatch := index . 5 }} {{ $TopRuleAction := index . 6 }} {{ $SecondBaseChain := index . 7 }} {{ $SecondRuleMatch := index . 8 }} {{ $SecondRuleAction := index . 9 }} {{ $ThirdBaseChain := index . 10 }} {{ $ThirdRuleMatch := index . 11 }} {{ $ThirdRuleAction := index . 12 }} {{ $FourthBaseChain := index . 13 }} {{ $FourthRuleMatch := index . 14 }} {{ $FourthRuleAction := index . 15 }} {{ if (ne $BaseChain "BaseChain") }}
27 | MakeBlueprintKey(
28 | {{ $Direction }},
29 | {{ $Scheme }},
30 | {{ $TargetType }},
31 | {{ $PeerType }},
32 | ): RuleBlueprint{
33 | BaseChain: {{ $BaseChain }},
34 | TopRuleMatch: {{ $TopRuleMatch }},
35 | TopRuleAction: {{ $TopRuleAction }},
36 | SecondBaseChain: {{ $SecondBaseChain }},
37 | SecondRuleMatch: {{ $SecondRuleMatch }},
38 | SecondRuleAction: {{ $SecondRuleAction }},
39 | ThirdBaseChain: {{ $ThirdBaseChain }},
40 | ThirdRuleMatch: {{ $ThirdRuleMatch }},
41 | ThirdRuleAction: {{ $ThirdRuleAction }},
42 | FourthBaseChain: {{ $FourthBaseChain }},
43 | FourthRuleMatch: {{ $FourthRuleMatch }},
44 | FourthRuleAction: "{{ $FourthRuleAction }}",
45 | },
46 | {{ end }} {{ end }}
47 | }
48 |
--------------------------------------------------------------------------------
/routepublisher/publisher/publisher.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2017 Pani Networks
2 | // All Rights Reserved.
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may
5 | // not use this file except in compliance with the License. You may obtain
6 | // a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | // License for the specific language governing permissions and limitations
14 | // under the License.
15 |
16 | // Package defines interface for publishing networks via dynamic routing
17 | // protocols.
18 | package publisher
19 |
20 | import (
21 | "net"
22 | )
23 |
24 | type Config map[string]string
25 |
26 | func (c Config) SetDefault(key, defaultValue string) string {
27 | if configValue, ok := c[key]; ok {
28 | return configValue
29 | }
30 |
31 | return defaultValue
32 | }
33 |
34 | type Interface interface {
35 | // Updates list of networks advertised via routing protocol.
36 | Update([]net.IPNet, map[string]interface{}) error
37 | }
38 |
--------------------------------------------------------------------------------
/server/romanad.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2016-2017 Pani Networks
2 | // All Rights Reserved.
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may
5 | // not use this file except in compliance with the License. You may obtain
6 | // a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | // License for the specific language governing permissions and limitations
14 | // under the License.
15 |
16 | package server
17 |
18 | import (
19 | "github.com/romana/core/common"
20 | "github.com/romana/core/common/api"
21 | "github.com/romana/core/common/client"
22 | )
23 |
24 | type Romanad struct {
25 | Addr string
26 | client *client.Client
27 | }
28 |
29 | func (r *Romanad) GetAddress() string {
30 | return r.Addr
31 | }
32 |
33 | func (r *Romanad) Name() string {
34 | return "romanad"
35 | }
36 |
37 | func (r *Romanad) Initialize(clientConfig common.Config) error {
38 | var err error
39 | r.client, err = client.NewClient(&clientConfig)
40 | if err != nil {
41 | return err
42 | }
43 | return nil
44 | }
45 |
46 | // Routes provided by ipam.
47 | func (r *Romanad) Routes() common.Routes {
48 | routes := common.Routes{
49 | common.Route{
50 | Method: "POST",
51 | Pattern: "/policies",
52 | Handler: r.addPolicy,
53 | MakeMessage: func() interface{} { return &api.Policy{} },
54 | UseRequestToken: false,
55 | },
56 | common.Route{
57 | Method: "DELETE",
58 | Pattern: "/policies",
59 | Handler: r.deletePolicy,
60 | MakeMessage: func() interface{} { return &api.Policy{} },
61 | UseRequestToken: false,
62 | },
63 | common.Route{
64 | Method: "DELETE",
65 | Pattern: "/policies/{policyID}",
66 | Handler: r.deletePolicy,
67 | MakeMessage: nil,
68 | UseRequestToken: false,
69 | },
70 | common.Route{
71 | Method: "GET",
72 | Pattern: "/policies",
73 | Handler: r.listPolicies,
74 | MakeMessage: nil,
75 | UseRequestToken: false,
76 | },
77 | common.Route{
78 | Method: "GET",
79 | Pattern: "/policies/{policyID}",
80 | Handler: r.getPolicy,
81 | MakeMessage: nil,
82 | UseRequestToken: false,
83 | },
84 | common.Route{
85 | Method: "GET",
86 | Pattern: "/networks/{network}/blocks",
87 | Handler: r.listNetworkBlocks,
88 | },
89 | common.Route{
90 | Method: "GET",
91 | Pattern: "/blocks",
92 | Handler: r.listAllBlocks,
93 | },
94 | common.Route{
95 | Method: "POST",
96 | Pattern: "/address",
97 | Handler: r.allocateIP,
98 | MakeMessage: func() interface{} { return &api.IPAMAddressRequest{} },
99 | },
100 | common.Route{
101 | Method: "DELETE",
102 | Pattern: "/address",
103 | Handler: r.deallocateIP,
104 | },
105 | common.Route{
106 | Method: "GET",
107 | Pattern: "/networks",
108 | Handler: r.listNetworks,
109 | },
110 | common.Route{
111 | Method: "GET",
112 | Pattern: "/topology",
113 | Handler: r.getTopology,
114 | },
115 | common.Route{
116 | Method: "POST",
117 | Pattern: "/topology",
118 | Handler: r.updateTopology,
119 | MakeMessage: func() interface{} { return &api.TopologyUpdateRequest{} },
120 | },
121 | common.Route{
122 | Method: "GET",
123 | Pattern: "/hosts",
124 | Handler: r.listHosts,
125 | },
126 | common.Route{
127 | Method: "POST",
128 | Pattern: "/hosts",
129 | Handler: r.addHost,
130 | MakeMessage: func() interface{} { return &api.Host{} },
131 | },
132 | }
133 | return routes
134 | }
135 |
--------------------------------------------------------------------------------
/test/cmd/fuzzer/main.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2016-2017 Pani Networks
2 | // All Rights Reserved.
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may
5 | // not use this file except in compliance with the License. You may obtain
6 | // a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | // License for the specific language governing permissions and limitations
14 | // under the License.
15 |
16 | // Command for running the Kubernetes Listener.
17 | package main
18 |
19 | import (
20 | "flag"
21 | "fmt"
22 | "net"
23 |
24 | "os"
25 | "strings"
26 |
27 | "github.com/romana/core/common"
28 | "github.com/romana/core/common/api"
29 | "github.com/romana/core/common/client"
30 | log "github.com/romana/rlog"
31 | )
32 |
33 | func main() {
34 | var err error
35 | endpointsStr := flag.String("etcd-endpoints", client.DefaultEtcdEndpoints, "Comma-separated list of etcd endpoints.")
36 | prefix := flag.String("etcd-prefix", client.DefaultEtcdPrefix, "Prefix to use for etcd data.")
37 | flag.Parse()
38 | if endpointsStr == nil {
39 | log.Errorf("No etcd endpoints specified")
40 | os.Exit(1)
41 | }
42 | endpoints := strings.Split(*endpointsStr, ",")
43 |
44 | pr := *prefix
45 | if !strings.HasPrefix(pr, "/") {
46 | pr = "/" + pr
47 | }
48 | config := common.Config{EtcdEndpoints: endpoints,
49 | EtcdPrefix: pr,
50 | }
51 | cl, err := client.NewClient(&config)
52 | if err != nil {
53 | panic(err)
54 | }
55 | tags := make(map[string]string)
56 | i := 0
57 | for {
58 | i++
59 | log.Infof("In fuzzer, with %d", i)
60 | ip := net.ParseIP(fmt.Sprintf("10.10.10.%d", i))
61 | tags[fmt.Sprintf("key%d", i)] = fmt.Sprintf("val%d", i)
62 | hostName := fmt.Sprintf("fuzzerhost%d", i)
63 | host := api.Host{IP: ip,
64 | Name: hostName,
65 | Tags: tags,
66 | }
67 | log.Infof("Adding host %s", hostName)
68 | err = cl.IPAM.AddHost(host)
69 | if err != nil {
70 | panic(err)
71 | }
72 | log.Infof("Added host OK")
73 | addr := fmt.Sprintf("addr%d", i)
74 | log.Infof("Trying to allocate IP for %s", addr)
75 | ip, err := cl.IPAM.AllocateIP(addr, hostName, "t1", "s1")
76 | if err != nil {
77 | panic(err)
78 | }
79 | log.Infof("Allocated %s for %s", ip, addr)
80 | if i > 4 {
81 | for j := 1; j <= 4; j++ {
82 | err = cl.IPAM.DeallocateIP(fmt.Sprintf("addr%d", j))
83 | if err != nil {
84 | panic(err)
85 | }
86 | }
87 | break
88 | }
89 | if i > 4 {
90 | break
91 | }
92 |
93 | }
94 |
95 | }
96 |
--------------------------------------------------------------------------------
/test/doc.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2016 Pani Networks
2 | // All Rights Reserved.
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may
5 | // not use this file except in compliance with the License. You may obtain
6 | // a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | // License for the specific language governing permissions and limitations
14 | // under the License.
15 |
16 | // +build ignore
17 |
18 | // Package test for testing purpose.
19 | package test
20 |
--------------------------------------------------------------------------------