Skip to content

Commit abbd257

Browse files
create the object graph for move
1 parent 401bd4e commit abbd257

File tree

4 files changed

+851
-2
lines changed

4 files changed

+851
-2
lines changed
Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
/*
2+
Copyright 2020 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package cluster
18+
19+
import (
20+
"fmt"
21+
22+
"github.com/pkg/errors"
23+
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
24+
apierrors "k8s.io/apimachinery/pkg/api/errors"
25+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
26+
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
27+
"k8s.io/apimachinery/pkg/types"
28+
clusterctlv1 "sigs.k8s.io/cluster-api/cmd/clusterctl/api/v1alpha3"
29+
"sigs.k8s.io/controller-runtime/pkg/client"
30+
)
31+
32+
// objectReference defines a reference to a Kubernetes object that is visited during the discovery phase for the move operation.
33+
type objectReference struct {
34+
APIVersion string
35+
Kind string
36+
Namespace string
37+
Name string
38+
UID types.UID
39+
}
40+
41+
func (s objectReference) String() string {
42+
return fmt.Sprintf("[%s/%s, namespace: %s, name: %s, uid: %s]", s.APIVersion, s.Kind, s.Namespace, s.Name, s.UID)
43+
}
44+
45+
type empty struct{}
46+
47+
// node defines a node in the Kubernetes object graph that is visited during the discovery phase for the move operation.
48+
type node struct {
49+
identity objectReference
50+
51+
// dependents contains the list of nodes that are owned by the current node.
52+
// Nb. This is the reverse of metadata.OwnerRef.
53+
dependents map[*node]empty
54+
55+
// virtual records if this node was discovered indirectly, e.g. by processing an OwnerRef, but not yet observed as a concrete object.
56+
virtual bool
57+
}
58+
59+
// markObserved marks the fact that a node was observed as a concrete object.
60+
func (n *node) markObserved() {
61+
n.virtual = false
62+
}
63+
64+
func (n *node) addDependent(dependent *node) {
65+
n.dependents[dependent] = empty{}
66+
}
67+
68+
// objectGraph manages the Kubernetes object graph that is generated during the discovery phase for the move operation.
69+
type objectGraph struct {
70+
proxy Proxy
71+
uidToNode map[types.UID]*node
72+
}
73+
74+
func newObjectGraph(proxy Proxy) *objectGraph {
75+
return &objectGraph{
76+
proxy: proxy,
77+
uidToNode: map[types.UID]*node{},
78+
}
79+
}
80+
81+
// addObj adds a Kubernetes object to the object graph that is generated during the move discovery phase.
82+
// During add, OwnerReferences are processed in order to create the dependency graph.
83+
func (o *objectGraph) addObj(obj *unstructured.Unstructured) {
84+
// adds the node to the Graph
85+
newNode := o.objToNode(obj)
86+
87+
// process OwnerReferences; if the owner object already exists, update the list of the owner object's dependents; otherwise
88+
// create a virtual node as a placeholder for the owner objects.
89+
for _, owner := range obj.GetOwnerReferences() {
90+
ownerNode, ok := o.uidToNode[owner.UID]
91+
if !ok {
92+
ownerNode = o.ownerToVirtualNode(owner, newNode.identity.Namespace)
93+
}
94+
95+
ownerNode.addDependent(newNode)
96+
}
97+
}
98+
99+
// ownerToVirtualNode creates a virtual node as a placeholder for the Kubernetes owner object received in input.
100+
// The virtual node will be eventually converted to an actual node when the node will be visited during discovery.
101+
func (o *objectGraph) ownerToVirtualNode(owner metav1.OwnerReference, namespace string) *node {
102+
ownerNode := &node{
103+
identity: objectReference{
104+
APIVersion: owner.APIVersion,
105+
Kind: owner.Kind,
106+
Name: owner.Name,
107+
UID: owner.UID,
108+
Namespace: namespace,
109+
},
110+
dependents: make(map[*node]empty),
111+
virtual: true,
112+
}
113+
114+
o.uidToNode[ownerNode.identity.UID] = ownerNode
115+
return ownerNode
116+
}
117+
118+
// objToNode creates a node for the Kubernetes object received in input.
119+
// If the node corresponding to the Kubernetes object already exists as a virtual node detected when processing OwnerReferences,
120+
// the node is marked as Observed.
121+
func (o *objectGraph) objToNode(obj *unstructured.Unstructured) *node {
122+
existingNode, found := o.uidToNode[obj.GetUID()]
123+
if found {
124+
existingNode.markObserved()
125+
return existingNode
126+
}
127+
128+
newNode := &node{
129+
identity: objectReference{
130+
APIVersion: obj.GetAPIVersion(),
131+
Kind: obj.GetKind(),
132+
UID: obj.GetUID(),
133+
Name: obj.GetName(),
134+
Namespace: obj.GetNamespace(),
135+
},
136+
dependents: make(map[*node]empty),
137+
virtual: false,
138+
}
139+
140+
o.uidToNode[newNode.identity.UID] = newNode
141+
return newNode
142+
}
143+
144+
// getDiscoveryTypes returns the list of TypeMeta to be considered for the the move discovery phase.
145+
// This list includes all the types defines by the CRDs installed by clusterctl and the ConfigMap/Secret core types.
146+
func (o *objectGraph) getDiscoveryTypes() ([]metav1.TypeMeta, error) {
147+
discoveredTypes := []metav1.TypeMeta{}
148+
149+
c, err := o.proxy.NewClient()
150+
if err != nil {
151+
return nil, err
152+
}
153+
154+
crdList := &apiextensionsv1.CustomResourceDefinitionList{}
155+
if err := c.List(ctx, crdList, client.MatchingLabels{clusterctlv1.ClusterctlLabelName: ""}); err != nil {
156+
return nil, errors.Wrap(err, "failed to get the list of CRDs required for the move discovery phase")
157+
}
158+
159+
for _, crd := range crdList.Items {
160+
for _, version := range crd.Spec.Versions {
161+
if !version.Storage {
162+
continue
163+
}
164+
165+
discoveredTypes = append(discoveredTypes, metav1.TypeMeta{
166+
Kind: crd.Spec.Names.Kind,
167+
APIVersion: metav1.GroupVersion{
168+
Group: crd.Spec.Group,
169+
Version: version.Name,
170+
}.String(),
171+
})
172+
}
173+
}
174+
175+
discoveredTypes = append(discoveredTypes, metav1.TypeMeta{Kind: "Secret", APIVersion: "v1"})
176+
discoveredTypes = append(discoveredTypes, metav1.TypeMeta{Kind: "ConfigMap", APIVersion: "v1"})
177+
178+
return discoveredTypes, nil
179+
}
180+
181+
// Discovery reads all the Kubernetes objects existing in a namespace for the types received in input, and then adds
182+
// everything to the objects graph.
183+
func (o *objectGraph) Discovery(namespace string, types []metav1.TypeMeta) error {
184+
c, err := o.proxy.NewClient()
185+
if err != nil {
186+
return err
187+
}
188+
189+
selectors := []client.ListOption{
190+
client.InNamespace(namespace),
191+
}
192+
193+
for _, typeMeta := range types {
194+
objList := new(unstructured.UnstructuredList)
195+
objList.SetAPIVersion(typeMeta.APIVersion)
196+
objList.SetKind(typeMeta.Kind)
197+
198+
if err := c.List(ctx, objList, selectors...); err != nil {
199+
if apierrors.IsNotFound(err) {
200+
continue
201+
}
202+
return errors.Wrapf(err, "failed to list %q resources", objList.GroupVersionKind())
203+
}
204+
205+
for i := range objList.Items {
206+
obj := objList.Items[i]
207+
o.addObj(&obj)
208+
}
209+
}
210+
211+
return nil
212+
}

0 commit comments

Comments
 (0)