1
// Copyright Istio Authors
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
7
// http://www.apache.org/licenses/LICENSE-2.0
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.
24
"github.com/hashicorp/go-multierror"
26
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
27
kubeyaml "k8s.io/apimachinery/pkg/util/yaml"
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"
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()
41
if err = config.ApplyJSON(c, js); err != nil {
47
func StatusJSONFromMap(schema resource.Schema, jsonMap map[string]any) (config.Status, error) {
51
js, err := json.Marshal(jsonMap)
55
status, err := schema.Status()
59
err = json.Unmarshal(js, status)
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()
72
if err = config.ApplyYAML(c, yml); err != nil {
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)
86
out, err := FromYAML(s, string(str))
88
return nil, multierror.Prefix(err, fmt.Sprintf("YAML decoding error: %v", string(str)))
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())
99
spec, err := FromJSON(schema, string(js))
103
status, err := StatusJSONFromMap(schema, object.GetStatus())
105
log.Errorf("could not get istio status from map %v, err %v", object.GetStatus(), err)
107
meta := object.GetObjectMeta()
109
return &config.Config{
111
GroupVersionKind: schema.GroupVersionKind(),
113
Namespace: meta.Namespace,
116
Annotations: meta.Annotations,
117
ResourceVersion: meta.ResourceVersion,
118
CreationTimestamp: meta.CreationTimestamp.Time,
125
// ConvertConfig translates Istio config to k8s config JSON
126
func ConvertConfig(cfg config.Config) (IstioObject, error) {
127
spec, err := config.ToMap(cfg.Spec)
131
status, err := config.ToMap(cfg.Status)
135
namespace := cfg.Namespace
137
namespace = metav1.NamespaceDefault
140
TypeMeta: metav1.TypeMeta{
141
Kind: cfg.GroupVersionKind.Kind,
142
APIVersion: cfg.GroupVersionKind.Group + "/" + cfg.GroupVersionKind.Version,
144
ObjectMeta: metav1.ObjectMeta{
146
Namespace: namespace,
147
ResourceVersion: cfg.ResourceVersion,
149
Annotations: cfg.Annotations,
150
CreationTimestamp: metav1.NewTime(cfg.CreationTimestamp),
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.
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))
168
// We store configs as a YaML stream; there may be more than one decoder.
169
yamlDecoder := kubeyaml.NewYAMLOrJSONDecoder(reader, 512*1024)
172
err := yamlDecoder.Decode(&obj)
177
return nil, nil, fmt.Errorf("cannot parse proto message: %v", err)
179
if reflect.DeepEqual(obj, empty) {
183
gvk := obj.GroupVersionKind()
184
s, exists := collections.PilotGatewayAPI().FindByGroupVersionAliasesKind(resource.FromKubernetesGVK(&gvk))
186
log.Debugf("unrecognized type %v", obj.Kind)
187
others = append(others, obj)
191
cfg, err := ConvertObject(s, &obj, "")
193
return nil, nil, fmt.Errorf("cannot parse proto message for %v: %v", obj.Name, err)
197
if _, err := s.ValidateConfig(*cfg); err != nil {
198
return nil, nil, fmt.Errorf("configuration is invalid: %v", err)
202
varr = append(varr, *cfg)
205
return varr, others, nil
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
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)