diff --git a/blacksmith.go b/blacksmith.go index 35056cc..556aefd 100644 --- a/blacksmith.go +++ b/blacksmith.go @@ -39,13 +39,13 @@ var ( listenIFFlag = flag.String("if", "0.0.0.0", "Interface name for DHCP and PXE to listen on") workspacePathFlag = flag.String("workspace", "/workspace", workspacePathHelp) etcdFlag = flag.String("etcd", "", "Etcd endpoints") - etcdDirFlag = flag.String("etcd-dir", "blacksmith", "The etcd directory prefix") + clusterNameFlag = flag.String("cluster-name", "blacksmith", "The name of this cluster. Will be used as etcd path prefixes.") leaseStartFlag = flag.String("lease-start", "", "Begining of lease starting IP") leaseRangeFlag = flag.Int("lease-range", 0, "Lease range") leaseSubnetFlag = flag.String("lease-subnet", "", "Subnet of specified lease") leaseRouterFlag = flag.String("router", "", "Default router that assigned to DHCP clients") - leaseDNSFlag = flag.String("dns", "", "Default DNS that assigned to DHCP clients") + leaseDNSFlag = flag.String("dns", "8.8.8.8", "comma separated IPs which will be used as default nameservers for skydns.") version string commit string @@ -106,7 +106,7 @@ func main() { } // etcd config - if etcdFlag == nil || etcdDirFlag == nil { + if etcdFlag == nil || clusterNameFlag == nil { fmt.Fprint(os.Stderr, "\nPlease specify the etcd endpoints\n") os.Exit(1) } @@ -116,7 +116,7 @@ func main() { if *listenIFFlag != "" { dhcpIF, err = net.InterfaceByName(*listenIFFlag) if err != nil { - fmt.Fprint(os.Stderr, "\nError while trying to get the interface: %s\n") + fmt.Fprint(os.Stderr, "\nError while trying to get the interface") os.Exit(1) } } else { @@ -126,7 +126,7 @@ func main() { serverIP, err := interfaceIP(dhcpIF) if err != nil { - fmt.Fprint(os.Stderr, "\nError while trying to get the ip from the interface: %s\n") + fmt.Fprint(os.Stderr, "\nError while trying to get the ip from the interface") os.Exit(1) } @@ -146,7 +146,7 @@ func main() { leaseRange := *leaseRangeFlag leaseSubnet := net.ParseIP(*leaseSubnetFlag) leaseRouter := net.ParseIP(*leaseRouterFlag) - leaseDNS := net.ParseIP(*leaseDNSFlag) + dnsIPStrings := strings.Split(*leaseDNSFlag, ",") if leaseStart == nil { fmt.Fprint(os.Stderr, "\nPlease specify the lease start ip\n") @@ -164,7 +164,7 @@ func main() { fmt.Fprint(os.Stderr, "\nPlease specify the IP address of network router\n") os.Exit(1) } - if leaseDNS == nil { + if len(dnsIPStrings) == 0 { fmt.Fprint(os.Stderr, "\nPlease specify an DNS server\n") os.Exit(1) } @@ -183,7 +183,8 @@ func main() { } kapi := etcd.NewKeysAPI(etcdClient) - etcdDataSource, err := datasource.NewEtcdDataSource(kapi, etcdClient, leaseStart, leaseRange, *etcdDirFlag, *workspacePathFlag) + etcdDataSource, err := datasource.NewEtcdDataSource(kapi, etcdClient, leaseStart, + leaseRange, *clusterNameFlag, *workspacePathFlag, serverIP, dnsIPStrings) if err != nil { fmt.Fprintf(os.Stderr, "\nCouldn't create runtime configuration: %s\n", err) os.Exit(1) @@ -244,7 +245,7 @@ func main() { ServerIP: serverIP, RouterAddr: leaseRouter, SubnetMask: leaseSubnet, - DNSAddr: leaseDNS, + DNSAddr: net.ParseIP(dnsIPStrings[0]), }, etcdDataSource) log.Fatalf("\nError while serving dhcp: %s\n", err) }() diff --git a/datasource/etcd_datasource.go b/datasource/etcd_datasource.go index 39c36ac..77eada2 100644 --- a/datasource/etcd_datasource.go +++ b/datasource/etcd_datasource.go @@ -36,12 +36,13 @@ type EtcdDataSource struct { client etcd.Client leaseStart net.IP leaseRange int - etcdDir string + clusterName string workspacePath string initialCoreOSVersion string dhcpAssignLock *sync.Mutex dhcpDataLock *sync.Mutex instancesEtcdDir string // HA + serverIP net.IP } // WorkspacePath is self explanatory @@ -137,6 +138,11 @@ func (ds *EtcdDataSource) CreateMachine(mac net.HardwareAddr, ip net.IP) (Machin defer cancel3() ds.keysAPI.Set(ctx3, ds.prefixify("machines/"+machine.Name()+"/_first_seen"), strconv.FormatInt(time.Now().UnixNano(), 10), &etcd.SetOptions{}) + + ctx4, cancel4 := context.WithTimeout(context.Background(), 3*time.Second) + defer cancel4() + ds.keysAPI.Set(ctx4, "skydns/"+ds.clusterName+"/"+machine.Name(), fmt.Sprintf(`{"host":"%s"}`, ip.String()), nil) + machine.CheckIn() machine.SetFlag("state", "unknown") return machine, true @@ -163,7 +169,7 @@ func (ds *EtcdDataSource) CoreOSVersion() (string, error) { } func (ds *EtcdDataSource) prefixify(key string) string { - return path.Join(ds.etcdDir, key) + return path.Join(ds.clusterName, key) } // Get parses the etcd key and returns it's value @@ -407,6 +413,30 @@ func (ds *EtcdDataSource) LeaseRange() int { return ds.leaseRange } +func ipStringToBytes(ip string) []byte { + return net.ParseIP(ip).To4() +} + +// DNSAddresses returns the ip addresses of the present skydns servers in the +// network, marshalled as specified in rfc2132 (option 6) +// part of DHCPDataSource ineterface implementation +func (ds *EtcdDataSource) DNSAddresses() ([]byte, error) { + ret := make([]byte, 0) + + ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) + defer cancel() + response, err := ds.keysAPI.Get(ctx, ds.prefixify(instancesEtcdDir), &etcd.GetOptions{Recursive: false}) + if err != nil { + return ret, err + } + for _, ent := range response.Node.Nodes { + ipString := ent.Value + + ret = append(ret, ipStringToBytes(ipString)...) + } + return ret, nil +} + func (ds *EtcdDataSource) lockDHCPAssign() { ds.dhcpAssignLock.Lock() } @@ -519,7 +549,7 @@ func (ds *EtcdDataSource) Request(nic string, currentIP net.IP) (net.IP, error) // NewEtcdDataSource gives blacksmith the ability to use an etcd endpoint as // a MasterDataSource func NewEtcdDataSource(kapi etcd.KeysAPI, client etcd.Client, leaseStart net.IP, - leaseRange int, etcdDir, workspacePath string) (MasterDataSource, error) { + leaseRange int, clusterName, workspacePath string, serverIP net.IP, defaultNameServers []string) (MasterDataSource, error) { data, err := ioutil.ReadFile(filepath.Join(workspacePath, "initial.yaml")) if err != nil { @@ -540,7 +570,7 @@ func NewEtcdDataSource(kapi etcd.KeysAPI, client etcd.Client, leaseStart net.IP, instance := &EtcdDataSource{ keysAPI: kapi, client: client, - etcdDir: etcdDir, + clusterName: clusterName, leaseStart: leaseStart, leaseRange: leaseRange, workspacePath: workspacePath, @@ -548,6 +578,7 @@ func NewEtcdDataSource(kapi etcd.KeysAPI, client etcd.Client, leaseStart net.IP, dhcpAssignLock: &sync.Mutex{}, dhcpDataLock: &sync.Mutex{}, instancesEtcdDir: invalidEtcdKey, + serverIP: serverIP, } _, err = instance.CoreOSVersion() @@ -561,7 +592,7 @@ func NewEtcdDataSource(kapi etcd.KeysAPI, client etcd.Client, leaseStart net.IP, if err != nil { return nil, fmt.Errorf("Error while initializing etcd tree: %s", err) } - fmt.Printf("Initialized etcd tree (%s)", etcdDir) + fmt.Printf("Initialized etcd tree (%s)", clusterName) } else { return nil, fmt.Errorf("Error while checking GetCoreOSVersion: %s", err) } @@ -571,5 +602,24 @@ func NewEtcdDataSource(kapi etcd.KeysAPI, client etcd.Client, leaseStart net.IP, defer cancel() instance.keysAPI.Set(ctx, instance.prefixify("machines"), "", &etcd.SetOptions{Dir: true}) + ctx2, cancel2 := context.WithTimeout(context.Background(), 3*time.Second) + defer cancel2() + instance.keysAPI.Set(ctx2, "skydns", "", &etcd.SetOptions{Dir: true}) + + ctx3, cancel3 := context.WithTimeout(context.Background(), 3*time.Second) + defer cancel3() + instance.keysAPI.Set(ctx3, "skydns/"+instance.clusterName, "", &etcd.SetOptions{Dir: true}) + quoteEnclosedNameservers := make([]string, 0) + for _, v := range defaultNameServers { + quoteEnclosedNameservers = append(quoteEnclosedNameservers, fmt.Sprintf(`"%s"`, v)) + } + commaSeparatedQouteEnclosedNameservers := strings.Join(quoteEnclosedNameservers, ",") + + skydnsconfig := fmt.Sprintf(`{"dns_addr":"%s:53","nameservers":[%s],"domain":"%s."}`, + instance.serverIP.String(), commaSeparatedQouteEnclosedNameservers, clusterName) + ctx4, cancel4 := context.WithTimeout(context.Background(), 3*time.Second) + defer cancel4() + instance.keysAPI.Set(ctx4, "skydns/config", skydnsconfig, nil) + return instance, nil } diff --git a/datasource/hacluster.go b/datasource/hacluster.go index 771d07d..f6648c6 100644 --- a/datasource/hacluster.go +++ b/datasource/hacluster.go @@ -25,7 +25,7 @@ func (ds *EtcdDataSource) registerOnEtcd() error { masterOrderOption := etcd.CreateInOrderOptions{ TTL: MasterTtlTime, } - resp, err := ds.keysAPI.CreateInOrder(ctx, ds.prefixify(instancesEtcdDir), "", &masterOrderOption) + resp, err := ds.keysAPI.CreateInOrder(ctx, ds.prefixify(instancesEtcdDir), ds.serverIP.String(), &masterOrderOption) if err != nil { return err } @@ -41,7 +41,7 @@ func (ds *EtcdDataSource) etcdHeartbeat() error { PrevExist: etcd.PrevExist, TTL: MasterTtlTime, } - _, err := ds.keysAPI.Set(ctx, ds.instancesEtcdDir, "", &masterSetOption) + _, err := ds.keysAPI.Set(ctx, ds.instancesEtcdDir, ds.serverIP.String(), &masterSetOption) return err } diff --git a/datasource/structure.go b/datasource/structure.go index aad5cf8..9478b10 100644 --- a/datasource/structure.go +++ b/datasource/structure.go @@ -86,6 +86,12 @@ type DHCPDataSource interface { // Request is how to client requests to use the Ip address Request(nic string, currentIP net.IP) (net.IP, error) + + // DNSAddresses returns addresses of the dns servers present in the network which + // can answer "what is the ip address of nodeX ?" + // a byte slice is returned to be used as option 6 (rfc2132) in a dhcp Request + // reply packet + DNSAddresses() ([]byte, error) } // RestServer defines the interface that a rest server has to implement to work diff --git a/dhcp/server.go b/dhcp/server.go index a7e632c..db1b121 100644 --- a/dhcp/server.go +++ b/dhcp/server.go @@ -149,7 +149,10 @@ func (h *DHCPHandler) ServeDHCP(p dhcp4.Packet, msgType dhcp4.MessageType, optio logging.Log("DHCP", "dhcp request - CHADDR %s - Requested IP %s - ACCEPTED", p.CHAddr().String(), requestedIP.String()) } packet.AddOption(12, []byte("node"+macAddress)) // host name option - + dns, err := h.datasource.DNSAddresses() + if err == nil { + packet.AddOption(6, dns) + } return packet case dhcp4.Release, dhcp4.Decline: