File: dns_darwin.go

package info (click to toggle)
golang-github-crc-org-crc 2.34.0%2Bds1-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,548 kB
  • sloc: sh: 398; makefile: 326; javascript: 40
file content (146 lines) | stat: -rw-r--r-- 4,159 bytes parent folder | download
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
}