istio

Форк
0
/
conversion.go 
218 строк · 6.1 Кб
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

15
package crd
16

17
import (
18
	"bytes"
19
	"encoding/json"
20
	"fmt"
21
	"io"
22
	"reflect"
23

24
	"github.com/hashicorp/go-multierror"
25
	"gopkg.in/yaml.v2"
26
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
27
	kubeyaml "k8s.io/apimachinery/pkg/util/yaml"
28

29
	"istio.io/istio/pkg/config"
30
	"istio.io/istio/pkg/config/schema/collections"
31
	"istio.io/istio/pkg/config/schema/resource"
32
	"istio.io/istio/pkg/log"
33
)
34

35
// FromJSON converts a canonical JSON to a proto message
36
func FromJSON(s resource.Schema, js string) (config.Spec, error) {
37
	c, err := s.NewInstance()
38
	if err != nil {
39
		return nil, err
40
	}
41
	if err = config.ApplyJSON(c, js); err != nil {
42
		return nil, err
43
	}
44
	return c, nil
45
}
46

47
func StatusJSONFromMap(schema resource.Schema, jsonMap map[string]any) (config.Status, error) {
48
	if jsonMap == nil {
49
		return nil, nil
50
	}
51
	js, err := json.Marshal(jsonMap)
52
	if err != nil {
53
		return nil, err
54
	}
55
	status, err := schema.Status()
56
	if err != nil {
57
		return nil, err
58
	}
59
	err = json.Unmarshal(js, status)
60
	if err != nil {
61
		return nil, err
62
	}
63
	return status, nil
64
}
65

66
// FromYAML converts a canonical YAML to a proto message
67
func FromYAML(s resource.Schema, yml string) (config.Spec, error) {
68
	c, err := s.NewInstance()
69
	if err != nil {
70
		return nil, err
71
	}
72
	if err = config.ApplyYAML(c, yml); err != nil {
73
		return nil, err
74
	}
75
	return c, nil
76
}
77

78
// FromJSONMap converts from a generic map to a proto message using canonical JSON encoding
79
// JSON encoding is specified here: https://developers.google.com/protocol-buffers/docs/proto3#json
80
func FromJSONMap(s resource.Schema, data any) (config.Spec, error) {
81
	// Marshal to YAML bytes
82
	str, err := yaml.Marshal(data)
83
	if err != nil {
84
		return nil, err
85
	}
86
	out, err := FromYAML(s, string(str))
87
	if err != nil {
88
		return nil, multierror.Prefix(err, fmt.Sprintf("YAML decoding error: %v", string(str)))
89
	}
90
	return out, nil
91
}
92

93
// ConvertObject converts an IstioObject k8s-style object to the internal configuration model.
94
func ConvertObject(schema resource.Schema, object IstioObject, domain string) (*config.Config, error) {
95
	js, err := json.Marshal(object.GetSpec())
96
	if err != nil {
97
		return nil, err
98
	}
99
	spec, err := FromJSON(schema, string(js))
100
	if err != nil {
101
		return nil, err
102
	}
103
	status, err := StatusJSONFromMap(schema, object.GetStatus())
104
	if err != nil {
105
		log.Errorf("could not get istio status from map %v, err %v", object.GetStatus(), err)
106
	}
107
	meta := object.GetObjectMeta()
108

109
	return &config.Config{
110
		Meta: config.Meta{
111
			GroupVersionKind:  schema.GroupVersionKind(),
112
			Name:              meta.Name,
113
			Namespace:         meta.Namespace,
114
			Domain:            domain,
115
			Labels:            meta.Labels,
116
			Annotations:       meta.Annotations,
117
			ResourceVersion:   meta.ResourceVersion,
118
			CreationTimestamp: meta.CreationTimestamp.Time,
119
		},
120
		Spec:   spec,
121
		Status: status,
122
	}, nil
123
}
124

125
// ConvertConfig translates Istio config to k8s config JSON
126
func ConvertConfig(cfg config.Config) (IstioObject, error) {
127
	spec, err := config.ToMap(cfg.Spec)
128
	if err != nil {
129
		return nil, err
130
	}
131
	status, err := config.ToMap(cfg.Status)
132
	if err != nil {
133
		return nil, err
134
	}
135
	namespace := cfg.Namespace
136
	if namespace == "" {
137
		namespace = metav1.NamespaceDefault
138
	}
139
	return &IstioKind{
140
		TypeMeta: metav1.TypeMeta{
141
			Kind:       cfg.GroupVersionKind.Kind,
142
			APIVersion: cfg.GroupVersionKind.Group + "/" + cfg.GroupVersionKind.Version,
143
		},
144
		ObjectMeta: metav1.ObjectMeta{
145
			Name:              cfg.Name,
146
			Namespace:         namespace,
147
			ResourceVersion:   cfg.ResourceVersion,
148
			Labels:            cfg.Labels,
149
			Annotations:       cfg.Annotations,
150
			CreationTimestamp: metav1.NewTime(cfg.CreationTimestamp),
151
		},
152
		Spec:   spec,
153
		Status: status,
154
	}, nil
155
}
156

157
// TODO - add special cases for type-to-kind and kind-to-type
158
// conversions with initial-isms. Consider adding additional type
159
// information to the abstract model and/or elevating k8s
160
// representation to first-class type to avoid extra conversions.
161

162
func parseInputsImpl(inputs string, withValidate bool) ([]config.Config, []IstioKind, error) {
163
	var varr []config.Config
164
	var others []IstioKind
165
	reader := bytes.NewReader([]byte(inputs))
166
	empty := IstioKind{}
167

168
	// We store configs as a YaML stream; there may be more than one decoder.
169
	yamlDecoder := kubeyaml.NewYAMLOrJSONDecoder(reader, 512*1024)
170
	for {
171
		obj := IstioKind{}
172
		err := yamlDecoder.Decode(&obj)
173
		if err == io.EOF {
174
			break
175
		}
176
		if err != nil {
177
			return nil, nil, fmt.Errorf("cannot parse proto message: %v", err)
178
		}
179
		if reflect.DeepEqual(obj, empty) {
180
			continue
181
		}
182

183
		gvk := obj.GroupVersionKind()
184
		s, exists := collections.PilotGatewayAPI().FindByGroupVersionAliasesKind(resource.FromKubernetesGVK(&gvk))
185
		if !exists {
186
			log.Debugf("unrecognized type %v", obj.Kind)
187
			others = append(others, obj)
188
			continue
189
		}
190

191
		cfg, err := ConvertObject(s, &obj, "")
192
		if err != nil {
193
			return nil, nil, fmt.Errorf("cannot parse proto message for %v: %v", obj.Name, err)
194
		}
195

196
		if withValidate {
197
			if _, err := s.ValidateConfig(*cfg); err != nil {
198
				return nil, nil, fmt.Errorf("configuration is invalid: %v", err)
199
			}
200
		}
201

202
		varr = append(varr, *cfg)
203
	}
204

205
	return varr, others, nil
206
}
207

208
// ParseInputs reads multiple documents from `kubectl` output and checks with
209
// the schema. It also returns the list of unrecognized kinds as the second
210
// response.
211
//
212
// NOTE: This function only decodes a subset of the complete k8s
213
// ObjectMeta as identified by the fields in model.Meta. This
214
// would typically only be a problem if a user dumps an configuration
215
// object with kubectl and then re-ingests it.
216
func ParseInputs(inputs string) ([]config.Config, []IstioKind, error) {
217
	return parseInputsImpl(inputs, true)
218
}
219

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.