1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
|
package dns
import (
"bytes"
"context"
"fmt"
"path/filepath"
"strings"
"text/template"
"time"
crcerrors "github.com/crc-org/crc/v2/pkg/crc/errors"
"github.com/crc-org/crc/v2/pkg/crc/logging"
"github.com/crc-org/crc/v2/pkg/crc/network"
"github.com/crc-org/crc/v2/pkg/crc/services"
crcos "github.com/crc-org/crc/v2/pkg/os"
"github.com/pkg/errors"
)
const (
resolverFileTemplate = `port {{ .Port }}
domain {{ .Domain }}
nameserver {{ .IP }}
search_order {{ .SearchOrder }}`
)
type resolverFileValues struct {
Port int
Domain string
IP string
SearchOrder int
}
func runPostStartForOS(serviceConfig services.ServicePostStartConfig) error {
// Update /etc/hosts file for host
if err := addOpenShiftHosts(serviceConfig); err != nil {
return err
}
if serviceConfig.NetworkMode == network.UserNetworkingMode {
return nil
}
// Write resolver config to host
needRestart, err := createResolverFile(serviceConfig.IP, serviceConfig.BundleMetadata.ClusterInfo.BaseDomain,
serviceConfig.BundleMetadata.ClusterInfo.BaseDomain)
if err != nil {
return err
}
if needRestart {
// Restart the Network on mac
logging.Infof("Restarting the host network")
if err := restartNetwork(); err != nil {
return errors.Wrap(err, "Restarting the host network failed")
}
// Wait for the Network to come up but in the case of error, log it to error info.
// If we make it as fatal call then in offline use case for mac is
// always going to be broken.
if err := waitForNetwork(); err != nil {
logging.Error(err)
}
} else {
logging.Infof("Network restart not needed")
}
return nil
}
func createResolverFile(instanceIP string, domain string, filename string) (bool, error) {
var resolverFile bytes.Buffer
values := resolverFileValues{
Port: dnsServicePort,
Domain: domain,
IP: instanceIP,
SearchOrder: 1,
}
t, err := template.New("resolver").Parse(resolverFileTemplate)
if err != nil {
return false, err
}
err = t.Execute(&resolverFile, values)
if err != nil {
return false, err
}
path := filepath.Join("/", "etc", "resolver", filename)
return crcos.WriteFileIfContentChanged(path, resolverFile.Bytes(), 0644)
}
// restartNetwork is required to update the resolver file on OSx.
func restartNetwork() error {
// https://medium.com/@kumar_pravin/network-restart-on-mac-os-using-shell-script-ab19ba6e6e99
netDeviceList, _, err := crcos.RunWithDefaultLocale("networksetup", "-listallnetworkservices")
netDeviceList = strings.TrimSpace(netDeviceList)
if err != nil {
return err
}
for _, netdevice := range strings.Split(netDeviceList, "\n")[1:] {
time.Sleep(1 * time.Second)
stdout, stderr, _ := crcos.RunWithDefaultLocale("networksetup", "-setnetworkserviceenabled", netdevice, "off")
logging.Debugf("Disabling the %s Device (stdout: %s), (stderr: %s)", netdevice, stdout, stderr)
stdout, stderr, err = crcos.RunWithDefaultLocale("networksetup", "-setnetworkserviceenabled", netdevice, "on")
logging.Debugf("Enabling the %s Device (stdout: %s), (stderr: %s)", netdevice, stdout, stderr)
if err != nil {
return fmt.Errorf("%s: %v", stderr, err)
}
}
return nil
}
func checkNetworkConnectivity() error {
hostResolv, err := network.GetResolvValuesFromHost()
if err != nil {
logging.Debugf("Unable to read resolv.conf: %v", err)
return err
}
for _, ns := range hostResolv.NameServers {
_, _, err := crcos.RunWithDefaultLocale("ping", "-c4", ns.IPAddress)
if err != nil {
continue
}
logging.Debugf("Successfully pinged %s, network is up", ns.IPAddress)
return nil
}
return fmt.Errorf("Could not ping any nameservers")
}
// Wait for Network wait till the network is up, since it is required to resolve external dnsquery
func waitForNetwork() error {
retriableConnectivityCheck := func() error {
err := checkNetworkConnectivity()
if err != nil {
return &crcerrors.RetriableError{Err: err}
}
return nil
}
if err := crcerrors.Retry(context.Background(), 15*time.Second, retriableConnectivityCheck, time.Second); err != nil {
return fmt.Errorf("Host is not connected to internet")
}
return nil
}
|