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 rt
16
17import (
18	"context"
19	"encoding/json"
20	"fmt"
21
22	"github.com/gogo/protobuf/proto"
23	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
24	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
25	"k8s.io/apimachinery/pkg/runtime"
26	"k8s.io/apimachinery/pkg/watch"
27	"k8s.io/client-go/tools/cache"
28
29	"istio.io/istio/galley/pkg/config/util/pb"
30	"istio.io/istio/pkg/config/schema/resource"
31)
32
33func (p *Provider) getDynamicAdapter(r resource.Schema) *Adapter {
34	return &Adapter{
35		extractObject: func(o interface{}) metav1.Object {
36			res, ok := o.(*unstructured.Unstructured)
37			if !ok {
38				return nil
39			}
40			return res
41		},
42
43		extractResource: func(o interface{}) (proto.Message, error) {
44			u, ok := o.(*unstructured.Unstructured)
45			if !ok {
46				return nil, fmt.Errorf("extractResource: not unstructured: %v", o)
47			}
48
49			pr := r.MustNewProtoInstance()
50			if err := pb.UnmarshalData(pr, u.Object["spec"]); err != nil {
51				return nil, err
52			}
53
54			return pr, nil
55		},
56
57		newInformer: func() (cache.SharedIndexInformer, error) {
58			d, err := p.GetDynamicResourceInterface(r)
59			if err != nil {
60				return nil, err
61			}
62
63			return cache.NewSharedIndexInformer(
64				&cache.ListWatch{
65					ListFunc: func(options metav1.ListOptions) (runtime.Object, error) {
66						return d.List(context.TODO(), options)
67					},
68					WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) {
69						options.Watch = true
70						return d.Watch(context.TODO(), options)
71					},
72				},
73				&unstructured.Unstructured{},
74				p.resyncPeriod,
75				cache.Indexers{}), nil
76		},
77
78		parseJSON: func(data []byte) (interface{}, error) {
79			u := &unstructured.Unstructured{}
80			if err := json.Unmarshal(data, u); err != nil {
81				return nil, fmt.Errorf("failed marshaling into unstructured: %v", err)
82			}
83
84			if empty(u) {
85				return nil, nil
86			}
87
88			return u, nil
89		},
90		getStatus: func(o interface{}) interface{} {
91			u, ok := o.(*unstructured.Unstructured)
92			if !ok {
93				return nil
94			}
95			return u.Object["status"]
96		},
97		isEqual:   resourceVersionsMatch,
98		isBuiltIn: false,
99	}
100}
101
102// Check if the parsed resource is empty
103func empty(r *unstructured.Unstructured) bool {
104	if r.Object == nil || len(r.Object) == 0 {
105		return true
106	}
107	return false
108}
109