1// Copyright 2020 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 helmreconciler
16
17import (
18	"io"
19	"strings"
20
21	jsonpatch "github.com/evanphx/json-patch"
22	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
23	"k8s.io/apimachinery/pkg/runtime"
24
25	"istio.io/istio/operator/pkg/name"
26	"istio.io/pkg/log"
27)
28
29const (
30	// MetadataNamespace is the namespace for mesh metadata (labels, annotations)
31	MetadataNamespace = "install.operator.istio.io"
32	// OwningResourceName represents the name of the owner to which the resource relates
33	OwningResourceName = MetadataNamespace + "/owning-resource"
34	// OwningResourceNamespace represents the namespace of the owner to which the resource relates
35	OwningResourceNamespace = MetadataNamespace + "/owning-resource-namespace"
36	// operatorLabelStr indicates Istio operator is managing this resource.
37	operatorLabelStr = name.OperatorAPINamespace + "/managed"
38	// operatorReconcileStr indicates that the operator will reconcile the resource.
39	operatorReconcileStr = "Reconcile"
40	// IstioComponentLabelStr indicates which Istio component a resource belongs to.
41	IstioComponentLabelStr = name.OperatorAPINamespace + "/component"
42	// istioVersionLabelStr indicates the Istio version of the installation.
43	istioVersionLabelStr = name.OperatorAPINamespace + "/version"
44)
45
46var (
47	scope = log.RegisterScope("installer", "installer", 0)
48)
49
50func init() {
51	// Tree representation and wait channels are an inversion of ComponentDependencies and are constructed from it.
52	buildInstallTree()
53	for _, parent := range ComponentDependencies {
54		for _, child := range parent {
55			DependencyWaitCh[child] = make(chan struct{}, 1)
56		}
57	}
58}
59
60// ComponentTree represents a tree of component dependencies.
61type ComponentTree map[name.ComponentName]interface{}
62type componentNameToListMap map[name.ComponentName][]name.ComponentName
63
64var (
65	// ComponentDependencies is a tree of component dependencies. The semantics are ComponentDependencies[cname] gives
66	// the subtree of components that must wait for cname to be installed before starting installation themselves.
67	ComponentDependencies = componentNameToListMap{
68		name.PilotComponentName: {
69			name.PolicyComponentName,
70			name.TelemetryComponentName,
71			name.CNIComponentName,
72			name.IngressComponentName,
73			name.EgressComponentName,
74			name.AddonComponentName,
75		},
76		name.IstioBaseComponentName: {
77			name.PilotComponentName,
78		},
79	}
80
81	// InstallTree is a top down hierarchy tree of dependencies where children must wait for the parent to complete
82	// before starting installation.
83	InstallTree = make(ComponentTree)
84	// DependencyWaitCh is a map of signaling channels. A parent with children ch1...chN will signal
85	// DependencyWaitCh[ch1]...DependencyWaitCh[chN] when it's completely installed.
86	DependencyWaitCh = make(map[name.ComponentName]chan struct{})
87)
88
89// buildInstallTree builds a tree from buildInstallTree where parents are the root of each subtree.
90func buildInstallTree() {
91	// Starting with root, recursively insert each first level child into each node.
92	insertChildrenRecursive(name.IstioBaseComponentName, InstallTree, ComponentDependencies)
93}
94
95func insertChildrenRecursive(componentName name.ComponentName, tree ComponentTree, children componentNameToListMap) {
96	tree[componentName] = make(ComponentTree)
97	for _, child := range children[componentName] {
98		insertChildrenRecursive(child, tree[componentName].(ComponentTree), children)
99	}
100}
101
102// InstallTreeString returns a string representation of the dependency tree.
103func InstallTreeString() string {
104	var sb strings.Builder
105	buildInstallTreeString(name.IstioBaseComponentName, "", &sb)
106	return sb.String()
107}
108
109func buildInstallTreeString(componentName name.ComponentName, prefix string, sb io.StringWriter) {
110	_, _ = sb.WriteString(prefix + string(componentName) + "\n")
111	if _, ok := InstallTree[componentName].(ComponentTree); !ok {
112		return
113	}
114	for k := range InstallTree[componentName].(ComponentTree) {
115		buildInstallTreeString(k, prefix+"  ", sb)
116	}
117}
118
119// applyOverlay applies an overlay using JSON patch strategy over the current Object in place.
120func applyOverlay(current, overlay runtime.Object) error {
121	cj, err := runtime.Encode(unstructured.UnstructuredJSONScheme, current)
122	if err != nil {
123		return err
124	}
125	uj, err := runtime.Encode(unstructured.UnstructuredJSONScheme, overlay)
126	if err != nil {
127		return err
128	}
129	merged, err := jsonpatch.MergePatch(cj, uj)
130	if err != nil {
131		return err
132	}
133	return runtime.DecodeInto(unstructured.UnstructuredJSONScheme, merged, current)
134}
135