Andrew Rynhard ca35b85300 refactor: improve installation reliability
This change aims to make installations more unified and reliable. It
introduces the concept of a mountpoint manager that is capable of
mounting, unmounting, and moving a set of mountpoints in the correct
order.

Signed-off-by: Andrew Rynhard <andrew@andrewrynhard.com>
2019-08-01 11:44:40 -07:00

146 lines
4.1 KiB
Go

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package network
import (
"context"
"fmt"
"net"
"os"
"strings"
"time"
"github.com/talos-systems/talos/internal/pkg/constants"
"github.com/talos-systems/talos/internal/pkg/kernel"
"github.com/talos-systems/dhcp/dhcpv4"
"github.com/talos-systems/dhcp/dhcpv4/client4"
"github.com/talos-systems/dhcp/netboot"
"golang.org/x/sys/unix"
)
// DHCPd runs the dhclient process with a certain frequency to maintain a fresh
// dhcp lease
func (service *Service) DHCPd(ctx context.Context, ifname string) {
var oldLifetime int
var lifetime int
var err error
service.logger.Printf("setting up DHCP on interface %s", ifname)
for {
service.logger.Println("obtaining DHCP lease")
lifetime, err = service.Dhclient(ctx, ifname)
if err != nil {
service.logger.Printf("failed to obtain dhcp lease for %s: %+v", ifname, err)
// Attempt to renew on a shorter interval to not lose network connectivity
lifetime = oldLifetime / 2
}
oldLifetime = lifetime
select {
case <-time.After((time.Duration(lifetime / 2)) * time.Second):
case <-ctx.Done():
return
}
}
}
// Dhclient handles the enture DHCP client interaction from a request to setting
// the received address on the interface
func (service *Service) Dhclient(ctx context.Context, ifname string) (int, error) {
// TODO: Figure out how we want to pass around ntp servers
modifiers := []dhcpv4.Modifier{
dhcpv4.WithRequestedOptions(
dhcpv4.OptionHostName,
dhcpv4.OptionClasslessStaticRouteOption,
dhcpv4.OptionDNSDomainSearchList,
dhcpv4.OptionNTPServers,
),
}
// Send hostname in Option 12 if we have it
if hostname, err := os.Hostname(); err != nil {
modifiers = append(modifiers, dhcpv4.WithOption(dhcpv4.OptHostName(hostname)))
}
var err error
var netconf *netboot.NetConf
// make dhcp request
if netconf, err = service.dhclient4(ctx, ifname, modifiers...); err != nil {
return 0, err
}
// verify a single address is returned
if len(netconf.Addresses) != 1 {
return 0, fmt.Errorf("expected 1 address in DHCP response for %s, got %d - %+v", ifname, len(netconf.Addresses), netconf.Addresses)
}
return netconf.Addresses[0].ValidLifetime, netboot.ConfigureInterface(ifname, netconf)
}
// nolint: gocyclo
func (service *Service) dhclient4(ctx context.Context, ifname string, modifiers ...dhcpv4.Modifier) (*netboot.NetConf, error) {
attempts := 10
client := client4.NewClient()
var (
conv []*dhcpv4.DHCPv4
err error
)
for attempt := 0; attempt < attempts; attempt++ {
service.logger.Printf("requesting DHCP lease: attempt %d of %d", attempt+1, attempts)
conv, err = client.Exchange(ifname, modifiers...)
if err != nil && attempt < attempts {
service.logger.Printf("failed to request DHCP lease: %v", err)
select {
case <-time.After(time.Duration(attempt) * time.Second):
case <-ctx.Done():
return nil, ctx.Err()
}
continue
}
break
}
for _, m := range conv {
if m.OpCode == dhcpv4.OpcodeBootReply && m.MessageType() == dhcpv4.MessageTypeOffer {
if m.YourIPAddr != nil {
service.logger.Printf("using IP address %s", m.YourIPAddr.String())
}
hostname := m.YourIPAddr.String()
if m.HostName() != "" {
hostname = m.HostName()
}
// Ignore DHCP-offered hostname if the kernel parameter is set
var kernHostname *string
if kernHostname = kernel.ProcCmdline().Get(constants.KernelParamHostname).First(); kernHostname != nil {
hostname = *kernHostname
}
// Truncate hostname to be betta
// Allow IP addrs to be valid hostnames for the time being
if ok := net.ParseIP(hostname); ok == nil {
// Pull out the first part of a potential FQDN
hostname = strings.Split(hostname, ".")[0]
}
service.logger.Printf("using hostname: %s", hostname)
if err = unix.Sethostname([]byte(hostname)); err != nil {
return nil, err
}
break
}
}
netconf, _, err := netboot.ConversationToNetconfv4(conv)
if err != nil {
return nil, err
}
return netconf, err
}