1// Copyright 2019 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	"sync"
19
20	"github.com/gogo/protobuf/proto"
21
22	"istio.io/api/mesh/v1alpha1"
23
24	"istio.io/istio/galley/pkg/config/scope"
25	"istio.io/istio/galley/pkg/config/source/kube/rt"
26	"istio.io/istio/pkg/config/event"
27	"istio.io/istio/pkg/config/resource"
28	"istio.io/istio/pkg/config/schema/collection"
29	"istio.io/istio/pkg/config/schema/collections"
30)
31
32// InMemorySource is an event.InMemorySource implementation for meshconfig. When the mesh config is first set, add & fullsync events
33// will be published. Otherwise a reset event will be sent.
34type InMemorySource struct {
35	mu      sync.Mutex
36	current proto.Message
37
38	handlers event.Handler
39
40	synced  bool
41	started bool
42}
43
44var _ event.Source = &InMemorySource{}
45
46// NewInmemoryMeshCfg returns a new in-memory source of MeshConfig.
47func NewInmemoryMeshCfg() *InMemorySource {
48	return &InMemorySource{
49		current: DefaultMeshConfig(),
50	}
51}
52
53// NewInmemoryMeshNetworks returns a new-inmemory source of MeshNetworks.
54func NewInmemoryMeshNetworks() *InMemorySource {
55	return &InMemorySource{
56		current: DefaultMeshNetworks(),
57	}
58}
59
60// Dispatch implements event.Dispatcher
61func (s *InMemorySource) Dispatch(handler event.Handler) {
62	s.mu.Lock()
63	defer s.mu.Unlock()
64	s.handlers = event.CombineHandlers(s.handlers, handler)
65}
66
67// Start implements event.InMemorySource
68func (s *InMemorySource) Start() {
69	s.mu.Lock()
70	defer s.mu.Unlock()
71
72	if s.started {
73		// Already started
74		return
75	}
76	s.started = true
77
78	if s.synced {
79		s.send(event.Added)
80		s.send(event.FullSync)
81	}
82}
83
84// Stop implements event.InMemorySource
85func (s *InMemorySource) Stop() {
86	s.mu.Lock()
87	defer s.mu.Unlock()
88	s.started = false
89}
90
91// Set new meshconfig
92func (s *InMemorySource) Set(cfg proto.Message) {
93	s.mu.Lock()
94	defer s.mu.Unlock()
95
96	cfg = proto.Clone(cfg)
97	s.current = cfg
98
99	if s.started {
100		if !s.synced {
101			s.send(event.Added)
102			s.send(event.FullSync)
103		} else {
104			s.send(event.Reset)
105		}
106	}
107
108	s.synced = true
109}
110
111// IsSynced indicates that the InMemorySource has been given a Mesh config at least once.
112func (s *InMemorySource) IsSynced() bool {
113	s.mu.Lock()
114	defer s.mu.Unlock()
115	return s.synced
116}
117
118func (s *InMemorySource) send(k event.Kind) {
119	// must be called under lock
120	var c collection.Schema
121	var n resource.FullName
122	switch t := s.current.(type) {
123	case *v1alpha1.MeshConfig:
124		n = MeshConfigResourceName
125		c = collections.IstioMeshV1Alpha1MeshConfig
126	case *v1alpha1.MeshNetworks:
127		n = MeshNetworksResourceName
128		c = collections.IstioMeshV1Alpha1MeshNetworks
129	default:
130		scope.Processing.Errorf("Unsupported type: %T", t)
131	}
132
133	e := event.Event{
134		Kind:   k,
135		Source: c,
136	}
137
138	o := rt.Origin{
139		FullName:   n,
140		Collection: c.Name(),
141		Kind:       c.Resource().Kind(),
142	}
143
144	switch k {
145	case event.Added, event.Updated:
146		e.Resource = &resource.Instance{
147			Metadata: resource.Metadata{
148				FullName: n,
149				Schema:   c.Resource(),
150			},
151			Message: proto.Clone(s.current),
152			Origin:  &o,
153		}
154	}
155
156	s.handlers.Handle(e)
157}
158