1// Copyright Istio Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package mesh
16
17import (
18	"fmt"
19	"reflect"
20	"sync"
21	"sync/atomic"
22	"unsafe"
23
24	"istio.io/istio/pkg/util/gogoprotomarshal"
25
26	meshconfig "istio.io/api/mesh/v1alpha1"
27	"istio.io/pkg/filewatcher"
28	"istio.io/pkg/log"
29)
30
31// NetworksHolder is a holder of a mesh networks configuration.
32type NetworksHolder interface {
33	Networks() *meshconfig.MeshNetworks
34}
35
36// NetworkWatcher watches changes to the mesh networks config.
37type NetworksWatcher interface {
38	NetworksHolder
39
40	AddNetworksHandler(func())
41}
42
43var _ NetworksWatcher = &networksWatcher{}
44
45type networksWatcher struct {
46	mutex    sync.Mutex
47	handlers []func()
48	networks *meshconfig.MeshNetworks
49}
50
51// NewFixedNetworksWatcher creates a new NetworksWatcher that always returns the given config.
52// It will never fire any events, since the config never changes.
53func NewFixedNetworksWatcher(networks *meshconfig.MeshNetworks) NetworksWatcher {
54	return &networksWatcher{
55		networks: networks,
56	}
57}
58
59// NewNetworksWatcher creates a new watcher for changes to the given networks config file.
60func NewNetworksWatcher(fileWatcher filewatcher.FileWatcher, filename string) (NetworksWatcher, error) {
61	meshNetworks, err := ReadMeshNetworks(filename)
62	if err != nil {
63		return nil, fmt.Errorf("failed to read mesh networks configuration from %q: %v", filename, err)
64	}
65
66	ResolveHostsInNetworksConfig(meshNetworks)
67	networksdump, _ := gogoprotomarshal.ToJSONWithIndent(meshNetworks, "   ")
68	log.Infof("mesh networks configuration: %s", networksdump)
69
70	w := &networksWatcher{
71		networks: meshNetworks,
72	}
73
74	// Watch the networks config file for changes and reload if it got modified
75	addFileWatcher(fileWatcher, filename, func() {
76		// Reload the config file
77		meshNetworks, err := ReadMeshNetworks(filename)
78		if err != nil {
79			log.Warnf("failed to read mesh networks configuration from %q", filename)
80			return
81		}
82
83		var handlers []func()
84
85		w.mutex.Lock()
86		if !reflect.DeepEqual(meshNetworks, w.networks) {
87			ResolveHostsInNetworksConfig(meshNetworks)
88			networksdump, _ := gogoprotomarshal.ToJSONWithIndent(meshNetworks, "    ")
89			log.Infof("mesh networks configuration updated to: %s", networksdump)
90
91			// Store the new config.
92			atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&w.networks)), unsafe.Pointer(meshNetworks))
93			handlers = append([]func(){}, w.handlers...)
94		}
95		w.mutex.Unlock()
96
97		// Notify the handlers of the change.
98		for _, h := range handlers {
99			h()
100		}
101	})
102	return w, nil
103}
104
105// Config returns the latest network configuration for the mesh.
106func (w *networksWatcher) Networks() *meshconfig.MeshNetworks {
107	return (*meshconfig.MeshNetworks)(atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&w.networks))))
108}
109
110// AddMeshHandler registers a callback handler for changes to the mesh network config.
111func (w *networksWatcher) AddNetworksHandler(h func()) {
112	w.mutex.Lock()
113	defer w.mutex.Unlock()
114	w.handlers = append(w.handlers, h)
115}
116