Merge pull request #8 from mayuresh82/vip_config
Add ability to specify vip parameters
This commit is contained in:
13
Dockerfile
13
Dockerfile
@@ -3,18 +3,19 @@ RUN apk update && \
|
||||
apk upgrade && \
|
||||
apk add --no-cache git && \
|
||||
apk add make
|
||||
RUN mkdir -p /opt/gocast
|
||||
RUN mkdir -p /go/src/github.com/mayuresh82
|
||||
RUN cd /go/src/github.com/mayuresh82 && \
|
||||
git clone https://github.com/mayuresh82/gocast
|
||||
|
||||
RUN mkdir -p /go/src/github.com/mayuresh82/gocast
|
||||
|
||||
COPY . /go/src/github.com/mayuresh82/gocast
|
||||
|
||||
WORKDIR /go/src/github.com/mayuresh82/gocast
|
||||
|
||||
RUN make
|
||||
RUN cp gocast /opt/gocast/
|
||||
|
||||
FROM alpine:latest
|
||||
RUN apk --no-cache add ca-certificates bash iptables netcat-openbsd sudo
|
||||
WORKDIR /root/
|
||||
COPY --from=builder /opt/gocast/gocast .
|
||||
COPY --from=builder /go/src/github.com/mayuresh82/gocast .
|
||||
|
||||
EXPOSE 8080/tcp
|
||||
|
||||
|
||||
@@ -24,4 +24,7 @@ bgp:
|
||||
apps:
|
||||
- name: app1
|
||||
vip: 1.1.1.1/32
|
||||
vip_config:
|
||||
# additional per VIP BGP communities
|
||||
bgp_communities: [ aaaa:bbbb ]
|
||||
monitor: port:tcp:5000
|
||||
|
||||
@@ -26,11 +26,18 @@ type BgpConfig struct {
|
||||
Origin string
|
||||
}
|
||||
|
||||
type VipConfig struct {
|
||||
// per VIP BGP communities to announce. This is in addition to the
|
||||
// global config
|
||||
BgpCommunities []string `yaml:"bgp_communities"`
|
||||
}
|
||||
|
||||
type AppConfig struct {
|
||||
Name string
|
||||
Vip string
|
||||
Monitors []string
|
||||
Nats []string
|
||||
Name string
|
||||
Vip string
|
||||
VipConfig VipConfig `yaml:"vip_config"`
|
||||
Monitors []string
|
||||
Nats []string
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/mayuresh82/gocast/config"
|
||||
)
|
||||
|
||||
type MonitorType int
|
||||
@@ -50,11 +51,12 @@ func (m Monitors) Contains(elem *Monitor) bool {
|
||||
}
|
||||
|
||||
type App struct {
|
||||
Name string
|
||||
Vip *net.IPNet
|
||||
Monitors Monitors
|
||||
Nats []string
|
||||
Source string
|
||||
Name string
|
||||
Vip *Route
|
||||
VipConfig config.VipConfig
|
||||
Monitors Monitors
|
||||
Nats []string
|
||||
Source string
|
||||
}
|
||||
|
||||
func (a *App) Equal(other *App) bool {
|
||||
@@ -66,10 +68,15 @@ func (a *App) Equal(other *App) bool {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return a.Name == other.Name && a.Vip.String() == other.Vip.String()
|
||||
return a.Name == other.Name && a.Vip.Net.String() == other.Vip.Net.String()
|
||||
}
|
||||
|
||||
func NewApp(appName, vip string, monitors []string, nats []string, source string) (*App, error) {
|
||||
func (a *App) String() string {
|
||||
return fmt.Sprintf("Name: %s, Vip: %s, VipConf: %v, Monitors: %v, Nats: %v, Source: %s",
|
||||
a.Name, a.Vip.Net.String(), a.VipConfig, a.Monitors, a.Nats, a.Source)
|
||||
}
|
||||
|
||||
func NewApp(appName, vip string, vipConfig config.VipConfig, monitors []string, nats []string, source string) (*App, error) {
|
||||
if appName == "" {
|
||||
return nil, fmt.Errorf("Invalid app name")
|
||||
}
|
||||
@@ -78,7 +85,8 @@ func NewApp(appName, vip string, monitors []string, nats []string, source string
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Invalid VIP specified, need ip/mask")
|
||||
}
|
||||
app.Vip = ipnet
|
||||
app.Vip = &Route{Net: ipnet, Communities: vipConfig.BgpCommunities}
|
||||
app.VipConfig = vipConfig
|
||||
for _, m := range monitors {
|
||||
// valid monitor formats:
|
||||
// "port:tcp:123" , "exec:/local/check.sh", "consul"
|
||||
|
||||
@@ -3,32 +3,36 @@ package controller
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/mayuresh82/gocast/config"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestAppParsing(t *testing.T) {
|
||||
a := assert.New(t)
|
||||
app1, err := NewApp("app1", "1.1.1.1/32", []string{"port:tcp:123"}, []string{}, "")
|
||||
app1, err := NewApp("app1", "1.1.1.1/32", config.VipConfig{}, []string{"port:tcp:123"}, []string{}, "")
|
||||
a.Nil(err)
|
||||
app2, err := NewApp("app1", "1.1.1.1/32", []string{"port:tcp:123"}, []string{}, "")
|
||||
app2, err := NewApp("app1", "1.1.1.1/32", config.VipConfig{BgpCommunities: []string{"111:222"}}, []string{"port:tcp:123"}, []string{}, "")
|
||||
a.Nil(err)
|
||||
app3, err := NewApp("app3", "2.2.2.2/32", []string{"exec:/bin/testme"}, []string{}, "")
|
||||
app3, err := NewApp("app3", "2.2.2.2/32", config.VipConfig{}, []string{"exec:/bin/testme"}, []string{}, "")
|
||||
a.Nil(err)
|
||||
|
||||
a.Equal("1.1.1.1/32", app1.Vip.String())
|
||||
a.Equal("1.1.1.1/32", app1.Vip.Net.String())
|
||||
a.Equal(Monitor_PORT, app1.Monitors[0].Type)
|
||||
a.Equal("123", app1.Monitors[0].Port)
|
||||
a.Equal("tcp", app1.Monitors[0].Protocol)
|
||||
a.Equal(config.VipConfig{}, app1.VipConfig)
|
||||
|
||||
a.Equal(true, app1.Equal(app2))
|
||||
|
||||
a.Equal("111:222", app2.Vip.Communities[0])
|
||||
|
||||
a.Equal(Monitor_EXEC, app3.Monitors[0].Type)
|
||||
a.Equal("/bin/testme", app3.Monitors[0].Cmd)
|
||||
|
||||
// test errors
|
||||
_, err = NewApp("app4", "4.4.4.4", []string{}, []string{}, "")
|
||||
_, err = NewApp("app4", "4.4.4.4", config.VipConfig{}, []string{}, []string{}, "")
|
||||
a.NotNil(err)
|
||||
|
||||
_, err = NewApp("app4", "4.4.4.4/32", []string{"port:abcd::1023"}, []string{}, "")
|
||||
_, err = NewApp("app4", "4.4.4.4/32", config.VipConfig{}, []string{"port:abcd::1023"}, []string{}, "")
|
||||
a.NotNil(err)
|
||||
}
|
||||
|
||||
@@ -14,6 +14,11 @@ import (
|
||||
gobgp "github.com/osrg/gobgp/pkg/server"
|
||||
)
|
||||
|
||||
type Route struct {
|
||||
Net *net.IPNet
|
||||
Communities []string
|
||||
}
|
||||
|
||||
type Controller struct {
|
||||
peerAS int
|
||||
localIP, peerIP net.IP
|
||||
@@ -90,14 +95,14 @@ func (c *Controller) AddPeer(peer string) error {
|
||||
return c.s.AddPeer(context.Background(), &api.AddPeerRequest{Peer: n})
|
||||
}
|
||||
|
||||
func (c *Controller) getApiPath(route *net.IPNet) *api.Path {
|
||||
func (c *Controller) getApiPath(route *Route) *api.Path {
|
||||
afi := api.Family_AFI_IP
|
||||
if route.IP.To4() == nil {
|
||||
if route.Net.IP.To4() == nil {
|
||||
afi = api.Family_AFI_IP6
|
||||
}
|
||||
prefixlen, _ := route.Mask.Size()
|
||||
prefixlen, _ := route.Net.Mask.Size()
|
||||
nlri, _ := ptypes.MarshalAny(&api.IPAddressPrefix{
|
||||
Prefix: route.IP.String(),
|
||||
Prefix: route.Net.IP.String(),
|
||||
PrefixLen: uint32(prefixlen),
|
||||
})
|
||||
a1, _ := ptypes.MarshalAny(&api.OriginAttribute{
|
||||
@@ -107,7 +112,7 @@ func (c *Controller) getApiPath(route *net.IPNet) *api.Path {
|
||||
NextHop: c.localIP.String(),
|
||||
})
|
||||
var communities []uint32
|
||||
for _, comm := range c.communities {
|
||||
for _, comm := range append(c.communities, route.Communities...) {
|
||||
communities = append(communities, convertCommunity(comm))
|
||||
}
|
||||
a3, _ := ptypes.MarshalAny(&api.CommunitiesAttribute{
|
||||
@@ -121,7 +126,7 @@ func (c *Controller) getApiPath(route *net.IPNet) *api.Path {
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Controller) Announce(route *net.IPNet) error {
|
||||
func (c *Controller) Announce(route *Route) error {
|
||||
var found bool
|
||||
err := c.s.ListPeer(context.Background(), &api.ListPeerRequest{}, func(p *api.Peer) {
|
||||
if p.Conf.NeighborAddress == c.peerIP.String() {
|
||||
@@ -140,7 +145,7 @@ func (c *Controller) Announce(route *net.IPNet) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Controller) Withdraw(route *net.IPNet) error {
|
||||
func (c *Controller) Withdraw(route *Route) error {
|
||||
return c.s.DeletePath(context.Background(), &api.DeletePathRequest{Path: c.getApiPath(route)})
|
||||
}
|
||||
|
||||
|
||||
@@ -93,7 +93,8 @@ func TestBgpNew(t *testing.T) {
|
||||
a.FailNow(err.Error())
|
||||
}
|
||||
_, ipnet, _ := net.ParseCIDR("20.30.40.0/24")
|
||||
if err := ctrl.Announce(ipnet); err != nil {
|
||||
r := &Route{Net: ipnet}
|
||||
if err := ctrl.Announce(r); err != nil {
|
||||
a.FailNow(err.Error())
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/mayuresh82/gocast/config"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -85,6 +86,7 @@ func (c *ConsulMon) queryServices() ([]*App, error) {
|
||||
monitors []string
|
||||
nats []string
|
||||
)
|
||||
var vipConf config.VipConfig
|
||||
for _, tag := range service.Tags {
|
||||
// try to find the requires tags. Only vip is mandatory
|
||||
parts := strings.Split(tag, "=")
|
||||
@@ -94,6 +96,8 @@ func (c *ConsulMon) queryServices() ([]*App, error) {
|
||||
switch parts[0] {
|
||||
case "gocast_vip":
|
||||
vip = parts[1]
|
||||
case "gocast_vip_communities":
|
||||
vipConf.BgpCommunities = strings.Split(parts[1], ",")
|
||||
case "gocast_monitor":
|
||||
monitors = append(monitors, parts[1])
|
||||
case "gocast_nat":
|
||||
@@ -104,7 +108,7 @@ func (c *ConsulMon) queryServices() ([]*App, error) {
|
||||
glog.Errorf("No vip Tag found in matched service :%s", service.Service)
|
||||
continue
|
||||
}
|
||||
app, err := NewApp(service.Service, vip, monitors, nats, "consul")
|
||||
app, err := NewApp(service.Service, vip, vipConf, monitors, nats, "consul")
|
||||
if err != nil {
|
||||
glog.Errorf("Unable to add consul app: %v", err)
|
||||
continue
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/mayuresh82/gocast/config"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@@ -15,7 +16,7 @@ var mockConsulData = map[string]string{
|
||||
"ID": "test-app-1",
|
||||
"Service": "test-service",
|
||||
"Tags": [
|
||||
"enable_gocast", "gocast_vip=1.1.1.1/32", "gocast_monitor=consul"
|
||||
"enable_gocast", "gocast_vip=1.1.1.1/32", "gocast_monitor=consul", "gocast_vip_communities=111:222,333:444"
|
||||
]
|
||||
}
|
||||
}}`,
|
||||
@@ -103,7 +104,9 @@ func TestQueryServices(t *testing.T) {
|
||||
a.FailNow(err.Error())
|
||||
}
|
||||
a.Equal(1, len(apps))
|
||||
app, _ := NewApp("test-service", "1.1.1.1/32", []string{"consul"}, []string{}, "consul")
|
||||
a.Equal([]string{"111:222", "333:444"}, apps[0].Vip.Communities)
|
||||
|
||||
app, _ := NewApp("test-service", "1.1.1.1/32", config.VipConfig{}, []string{"consul"}, []string{}, "consul")
|
||||
a.True(app.Equal(apps[0]))
|
||||
|
||||
// test no match
|
||||
|
||||
@@ -100,7 +100,7 @@ func NewMonitor(config *c.Config) *MonitorMgr {
|
||||
mon.config = config
|
||||
// add apps defined in config
|
||||
for _, a := range config.Apps {
|
||||
app, err := NewApp(a.Name, a.Vip, a.Monitors, a.Nats, "config")
|
||||
app, err := NewApp(a.Name, a.Vip, a.VipConfig, a.Monitors, a.Nats, "config")
|
||||
if err != nil {
|
||||
glog.Errorf("Failed to add configured app %s: %v", a.Name, err)
|
||||
continue
|
||||
@@ -159,8 +159,8 @@ func (m *MonitorMgr) Add(app *App) {
|
||||
glog.V(2).Infof("App %s already exists", app.Name)
|
||||
return
|
||||
}
|
||||
if appMon.app.Vip.String() == app.Vip.String() && appMon.app.Name != app.Name {
|
||||
glog.Errorf("Error: Vip %s is already being announced by app: %s", app.Vip.String(), appMon.app.Name)
|
||||
if appMon.app.Vip.Net.String() == app.Vip.Net.String() && appMon.app.Name != app.Name {
|
||||
glog.Errorf("Error: Vip %s is already being announced by app: %s", app.Vip.Net.String(), appMon.app.Name)
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -168,7 +168,7 @@ func (m *MonitorMgr) Add(app *App) {
|
||||
appMon := &appMon{app: app, done: make(chan bool)}
|
||||
m.monitors[app.Name] = appMon
|
||||
go m.runLoop(appMon)
|
||||
glog.Infof("Registered a new app: %v", app)
|
||||
glog.Infof("Registered a new app: %v", app.String())
|
||||
}
|
||||
|
||||
// Remove removes an app from monitor manager, stops BGP
|
||||
@@ -183,7 +183,7 @@ func (m *MonitorMgr) Remove(appName string) {
|
||||
glog.Errorf("Failed to withdraw route: %v", err)
|
||||
}
|
||||
}
|
||||
if err := deleteLoopback(a.app.Vip); err != nil {
|
||||
if err := deleteLoopback(a.app.Vip.Net); err != nil {
|
||||
glog.Errorf("Failed to remove app: %s: %v", a.app.Name, err)
|
||||
}
|
||||
for _, nat := range a.app.Nats {
|
||||
@@ -191,7 +191,7 @@ func (m *MonitorMgr) Remove(appName string) {
|
||||
if len(parts) != 2 {
|
||||
continue
|
||||
}
|
||||
if err := natRule("D", a.app.Vip.IP, m.ctrl.localIP, parts[0], parts[1]); err != nil {
|
||||
if err := natRule("D", a.app.Vip.Net.IP, m.ctrl.localIP, parts[0], parts[1]); err != nil {
|
||||
glog.Errorf("Failed to remove app: %s: %v", a.app.Name, err)
|
||||
}
|
||||
}
|
||||
@@ -228,7 +228,7 @@ func (m *MonitorMgr) checkCond(am *appMon) error {
|
||||
if m.runMonitors(app) {
|
||||
glog.V(2).Infof("All Monitors for app: %s succeeded", app.Name)
|
||||
if !am.announced {
|
||||
if err := addLoopback(app.Name, app.Vip); err != nil {
|
||||
if err := addLoopback(app.Name, app.Vip.Net); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, nat := range app.Nats {
|
||||
@@ -236,7 +236,7 @@ func (m *MonitorMgr) checkCond(am *appMon) error {
|
||||
if len(parts) != 2 {
|
||||
continue
|
||||
}
|
||||
if err := natRule("A", app.Vip.IP, m.ctrl.localIP, parts[0], parts[1]); err != nil {
|
||||
if err := natRule("A", app.Vip.Net.IP, m.ctrl.localIP, parts[0], parts[1]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -294,13 +294,13 @@ func (m *MonitorMgr) CloseAll() {
|
||||
if am.checkOn {
|
||||
am.done <- true
|
||||
}
|
||||
deleteLoopback(am.app.Vip)
|
||||
deleteLoopback(am.app.Vip.Net)
|
||||
for _, nat := range am.app.Nats {
|
||||
parts := strings.Split(nat, ":")
|
||||
if len(parts) != 2 {
|
||||
continue
|
||||
}
|
||||
natRule("D", am.app.Vip.IP, m.ctrl.localIP, parts[0], parts[1])
|
||||
natRule("D", am.app.Vip.Net.IP, m.ctrl.localIP, parts[0], parts[1])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,9 +4,12 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/golang/glog"
|
||||
"github.com/mayuresh82/gocast/controller"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/mayuresh82/gocast/config"
|
||||
"github.com/mayuresh82/gocast/controller"
|
||||
)
|
||||
|
||||
// Server is the main entrypoint into the app and serves app requests
|
||||
@@ -46,7 +49,11 @@ func (s *Server) Serve(ctx context.Context) {
|
||||
|
||||
func (s *Server) registerHandler(w http.ResponseWriter, r *http.Request) {
|
||||
queries := r.URL.Query()
|
||||
app, err := controller.NewApp(queries["name"][0], queries["vip"][0], queries["monitor"], queries["nat"], "http")
|
||||
var vipConf config.VipConfig
|
||||
if vipComm, ok := queries["vip_communities"]; ok {
|
||||
vipConf.BgpCommunities = strings.Split(vipComm[0], ",")
|
||||
}
|
||||
app, err := controller.NewApp(queries["name"][0], queries["vip"][0], vipConf, queries["monitor"], queries["nat"], "http")
|
||||
if err != nil {
|
||||
http.Error(w, fmt.Sprintf("Invalid request: %v", err), http.StatusBadRequest)
|
||||
return
|
||||
|
||||
Reference in New Issue
Block a user