Пример #1
0
// updateState updates the state of an IP for a vserver based on the state of
// that IP's services.
func (v *vserver) updateState(ip seesaw.IP) {
	// A vserver anycast IP is healthy if *all* services for that IP are healthy.
	// A vserver unicast IP is healthy if *any* services for that IP are healthy.
	var healthy bool
	for _, s := range v.services {
		if !s.ip.Equal(ip) {
			continue
		}
		healthy = s.healthy
		if !healthy && seesaw.IsAnycast(ip.IP()) {
			break
		}
		if healthy && !seesaw.IsAnycast(ip.IP()) {
			break
		}
	}

	if v.active[ip] == healthy {
		v.updateServices(ip)
		return
	}
	switch {
	case !healthy && v.active[ip]:
		v.down(ip)
	case healthy && !v.active[ip]:
		v.up(ip)
	}
}
Пример #2
0
// down takes down an IP address for a vserver, then takes down all services
// for that IP address.
func (v *vserver) down(ip seesaw.IP) {
	ncc := v.engine.ncc
	if err := ncc.Dial(); err != nil {
		log.Fatalf("%v: failed to connect to NCC: %v", v, err)
	}
	defer ncc.Close()

	// If this is an anycast VIP, withdraw the BGP route.
	nip := ip.IP()
	if seesaw.IsAnycast(nip) {
		if v.engine.config.AnycastEnabled {
			log.Infof("%v: withdrawing BGP route for %v", v, ip)
			if err := ncc.BGPWithdrawVIP(nip); err != nil {
				log.Fatalf("%v: failed to withdraw VIP %v: %v", v, ip, err)
			}
		}
		vip := seesaw.NewVIP(nip, nil)
		if err := v.engine.lbInterface.DeleteVIP(vip); err != nil {
			log.Fatalf("%v: failed to remove VIP %v: %v", v, ip, err)
		}
		if err := v.engine.lbInterface.DeleteVserver(v.lbVservers[ip], ip.AF()); err != nil {
			log.Fatalf("%v: failed to delete Vserver: %v", v, err)
		}
	}
	// TODO(jsing): Should we delay while the BGP routes propagate?

	delete(v.active, ip)
	v.updateServices(ip)
	log.Infof("%v: VIP %v down", v, ip)
}
Пример #3
0
// up brings up all healthy services for an IP address for a vserver, then
// brings up the IP address.
func (v *vserver) up(ip seesaw.IP) {
	ncc := v.engine.ncc
	if err := ncc.Dial(); err != nil {
		log.Fatalf("%v: failed to connect to NCC: %v", v, err)
	}
	defer ncc.Close()

	v.active[ip] = true
	v.updateServices(ip)

	// If this is an anycast VIP, start advertising a BGP route.
	nip := ip.IP()
	if seesaw.IsAnycast(nip) {
		// TODO(jsing): Create an LBVserver that only encapsulates
		// the necessary state, rather than storing a full vserver
		// snapshot.
		lbVserver := v.snapshot()
		lbVserver.Services = nil
		lbVserver.Warnings = nil
		if err := v.engine.lbInterface.AddVserver(lbVserver, ip.AF()); err != nil {
			log.Fatalf("%v: failed to add Vserver: %v", v, err)
		}
		v.lbVservers[ip] = lbVserver

		vip := seesaw.NewVIP(nip, nil)
		if err := v.engine.lbInterface.AddVIP(vip); err != nil {
			log.Fatalf("%v: failed to add VIP %v: %v", v, ip, err)
		}
		// TODO(angusc): Filter out anycast VIPs for non-anycast clusters further
		// upstream.
		if v.engine.config.AnycastEnabled {
			log.Infof("%v: advertising BGP route for %v", v, ip)
			if err := ncc.BGPAdvertiseVIP(nip); err != nil {
				log.Fatalf("%v: failed to advertise VIP %v: %v", v, ip, err)
			}
		} else {
			log.Warningf("%v: %v is an anycast VIP, but anycast is not enabled", v, ip)
		}
	}

	log.Infof("%v: VIP %v up", v, ip)
}
Пример #4
0
func main() {
	flag.Parse()

	cfg, err := conf.ReadConfigFile(*configFile)
	if err != nil {
		log.Exitf("Failed to read configuration file: %v", err)
	}
	clusterName := cfgOpt(cfg, "cluster", "name")
	if clusterName == "" {
		log.Exit("Unable to get cluster name")
	}

	anycastEnabled := config.DefaultEngineConfig().AnycastEnabled
	if opt := cfgOpt(cfg, "cluster", "anycast_enabled"); opt != "" {
		if anycastEnabled, err = cfg.GetBool("cluster", "anycast_enabled"); err != nil {
			log.Exitf("Unable to parse cluster anycast_enabled: %v", err)
		}
	}
	clusterVIPv4, err := cfgIP(cfg, "cluster", "vip_ipv4")
	if err != nil {
		log.Exitf("Unable to get cluster vip_ipv4: %v", err)
	}
	clusterVIPv6, err := cfgIP(cfg, "cluster", "vip_ipv6")
	if err != nil {
		log.Exitf("Unable to get cluster vip_ipv6: %v", err)
	}
	nodeIPv4, err := cfgIP(cfg, "cluster", "node_ipv4")
	if err != nil {
		log.Exitf("Unable to get cluster node_ipv4: %v", err)
	}
	nodeIPv6, err := cfgIP(cfg, "cluster", "node_ipv6")
	if err != nil {
		log.Exitf("Unable to get cluster node_ipv6: %v", err)
	}
	peerIPv4, err := cfgIP(cfg, "cluster", "peer_ipv4")
	if err != nil {
		log.Exitf("Unable to get cluster peer_ipv4: %v", err)
	}
	peerIPv6, err := cfgIP(cfg, "cluster", "peer_ipv6")
	if err != nil {
		log.Exitf("Unable to get cluster peer_ipv6: %v", err)
	}

	// The default VRID may be overridden via the config file.
	vrid := config.DefaultEngineConfig().VRID
	if cfg.HasOption("cluster", "vrid") {
		id, err := cfg.GetInt("cluster", "vrid")
		if err != nil {
			log.Exitf("Unable to get VRID: %v", err)
		}
		if id < 1 || id > 255 {
			log.Exitf("Invalid VRID %d - must be between 1 and 255 inclusive", id)
		}
		vrid = uint8(id)
	}

	// Optional primary, secondary and tertiary configuration servers.
	configServers := make([]string, 0)
	for _, level := range []string{"primary", "secondary", "tertiary"} {
		if server := cfgOpt(cfg, "config_server", level); server != "" {
			configServers = append(configServers, server)
		}
	}
	if len(configServers) == 0 {
		configServers = config.DefaultEngineConfig().ConfigServers
	}

	nodeInterface := config.DefaultEngineConfig().NodeInterface
	if opt := cfgOpt(cfg, "interface", "node"); opt != "" {
		nodeInterface = opt
	}
	lbInterface := config.DefaultEngineConfig().LBInterface
	if opt := cfgOpt(cfg, "interface", "lb"); opt != "" {
		lbInterface = opt
	}

	// Additional anycast addresses.
	serviceAnycastIPv4 := config.DefaultEngineConfig().ServiceAnycastIPv4
	serviceAnycastIPv6 := config.DefaultEngineConfig().ServiceAnycastIPv6
	if cfg.HasSection("extra_service_anycast") {
		opts, err := cfg.GetOptions("extra_service_anycast")
		if err != nil {
			log.Exitf("Unable to get extra_serivce_anycast options: %v", err)
		}
		for _, opt := range opts {
			ip, err := cfgIP(cfg, "extra_service_anycast", opt)
			if err != nil {
				log.Exitf("Unable to get extra_service_anycast option %q: %v", opt, err)
			}
			if !seesaw.IsAnycast(ip) {
				log.Exitf("%q is not an anycast address", ip)
			}
			if ip.To4() != nil {
				serviceAnycastIPv4 = append(serviceAnycastIPv4, ip)
			} else {
				serviceAnycastIPv6 = append(serviceAnycastIPv6, ip)
			}
		}
	}

	// Override some of the defaults.
	engineCfg := config.DefaultEngineConfig()
	engineCfg.AnycastEnabled = anycastEnabled
	engineCfg.ConfigFile = *configFile
	engineCfg.ConfigServers = configServers
	engineCfg.ClusterFile = *clusterFile
	engineCfg.ClusterName = clusterName
	engineCfg.ClusterVIP.IPv4Addr = clusterVIPv4
	engineCfg.ClusterVIP.IPv6Addr = clusterVIPv6
	engineCfg.LBInterface = lbInterface
	engineCfg.NCCSocket = *nccSocket
	engineCfg.Node.IPv4Addr = nodeIPv4
	engineCfg.Node.IPv6Addr = nodeIPv6
	engineCfg.NodeInterface = nodeInterface
	engineCfg.Peer.IPv4Addr = peerIPv4
	engineCfg.Peer.IPv6Addr = peerIPv6
	engineCfg.ServiceAnycastIPv4 = serviceAnycastIPv4
	engineCfg.ServiceAnycastIPv6 = serviceAnycastIPv6
	engineCfg.SocketPath = *socketPath
	engineCfg.VRID = vrid

	// Gentlemen, start your engines...
	engine := engine.NewEngine(&engineCfg)
	server.ShutdownHandler(engine)
	server.ServerRunDirectory("engine", 0, 0)
	// TODO(jsing): Drop privileges before starting engine.
	engine.Run()
}
Пример #5
0
func TestReIPVserver(t *testing.T) {
	e := newTestEngine()
	vserver := newTestVserver(e)
	lbIF := e.lbInterface.(*dummyLBInterface)
	clusterName := "au-syd"
	serviceName := "dns.resolver@au-syd"
	tests := []struct {
		desc    string
		file    string
		health  healthcheck.State
		wantIPs []string
	}{
		{desc: "initial", file: "re-ip/config_1.pb", wantIPs: []string{"192.168.36.1"}},
		{desc: "re-ip unicast", file: "re-ip/config_2.pb", wantIPs: []string{"192.168.36.5"}},
		{desc: "unicast unhealthy", health: healthcheck.StateUnhealthy, wantIPs: []string{"192.168.36.5"}},
		{desc: "unicast disabled", file: "re-ip/config_3.pb", wantIPs: nil},
		{desc: "re-ip anycast (unhealthy)", file: "re-ip/config_4.pb", wantIPs: nil},
		{desc: "anycast health", health: healthcheck.StateHealthy, wantIPs: []string{"192.168.255.1"}},
		{desc: "anycast disabled", file: "re-ip/config_5.pb", wantIPs: nil},
		{desc: "back to unicast", file: "re-ip/config_1.pb", wantIPs: []string{"192.168.36.1"}},
	}
	for _, tc := range tests {
		log.Infof("Applying config: %s", tc.desc)
		if tc.file != "" {
			if err := applyConfig(vserver, tc.file, clusterName, serviceName); err != nil {
				t.Fatalf("Failed to apply configuration: %v", err)
			}
		}

		if tc.health != healthcheck.StateUnknown {
			for k := range vserver.checks {
				n := &checkNotification{
					key:         k,
					description: fmt.Sprintf("forced health=%v for step %q", tc.health, tc.desc),
					status:      healthcheck.Status{State: tc.health},
				}
				vserver.handleCheckNotification(n)
			}
		}

		wantVIPs := make(map[string]bool)
		for _, s := range tc.wantIPs {
			wantVIPs[s] = true
		}

		if seesaw.IsAnycast(vserver.config.IPv4Addr) {
			// vserver.vips only tracks unicast VIPs today, so we can only check that
			// there are none.
			if len(vserver.vips) > 0 {
				t.Errorf("vserver(%q).vips = %v, wanted no unicast vips", tc.desc, vserver.vips)
			}
		} else {
			gotVIPs := make(map[string]bool)
			for v := range vserver.vips {
				gotVIPs[v.IP.String()] = true
			}
			if diff := pretty.Compare(wantVIPs, gotVIPs); diff != "" {
				t.Errorf("vserver(%q).vips unexpected (-want +got):\n%s", tc.desc, diff)
			}
		}

		gotLBVS := make(map[string]bool)
		for v := range vserver.lbVservers {
			gotLBVS[v.String()] = true
		}
		if diff := pretty.Compare(wantVIPs, gotLBVS); diff != "" {
			t.Errorf("vserver(%q).lbVservers (-want +got):\n%s", tc.desc, diff)
		}

		gotIFIPs := make(map[string]bool)
		for v := range lbIF.vips {
			gotIFIPs[v.IP.String()] = true
		}
		if diff := pretty.Compare(wantVIPs, gotIFIPs); diff != "" {
			t.Errorf("After %s, unexpected IPs on the LB interface (-want +got):\n%s", tc.desc, diff)
		}
	}
}