/* Copyright 2017 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package parse import ( "fmt" "reflect" "k8s.io/kube-openapi/pkg/util/proto" "k8s.io/kubectl/pkg/apply" "k8s.io/kubectl/pkg/util/openapi" ) // Factory creates an Element by combining object values from recorded, local and remote sources with // the metadata from an openapi schema. type Factory struct { // Resources contains the openapi field metadata for the object models Resources openapi.Resources } // CreateElement returns an Element by collating the recorded, local and remote field values func (b *Factory) CreateElement(recorded, local, remote map[string]interface{}) (apply.Element, error) { // Create an Item from the 3 values. Use empty name for field visitor := &ElementBuildingVisitor{b.Resources} gvk, err := getCommonGroupVersionKind(recorded, local, remote) if err != nil { return nil, err } // Get the openapi object metadata s := visitor.resources.LookupResource(gvk) oapiKind, err := getKind(s) if err != nil { return nil, err } data := apply.NewRawElementData(recorded, local, remote) fieldName := "" item, err := visitor.getItem(oapiKind, fieldName, data) if err != nil { return nil, err } // Collate each field of the item into a combined Element return item.CreateElement(visitor) } // getItem returns the appropriate Item based on the underlying type of the arguments func (v *ElementBuildingVisitor) getItem(s proto.Schema, name string, data apply.RawElementData) (Item, error) { kind, err := getType(data.GetRecorded(), data.GetLocal(), data.GetRemote()) if err != nil { return nil, err } if kind == nil { // All of the items values are nil. return &emptyItem{Name: name}, nil } // Create an item matching the type switch kind.Kind() { case reflect.Bool, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64, reflect.String: p, err := getPrimitive(s) if err != nil { return nil, fmt.Errorf("expected openapi Primitive, was %T for %v (%v)", s, kind, err) } return &primitiveItem{name, p, data}, nil case reflect.Array, reflect.Slice: a, err := getArray(s) if err != nil { return nil, fmt.Errorf("expected openapi Array, was %T for %v (%v)", s, kind, err) } return &listItem{ Name: name, Array: a, ListElementData: apply.ListElementData{ RawElementData: data, }, }, nil case reflect.Map: if k, err := getKind(s); err == nil { return &typeItem{ Name: name, Type: k, MapElementData: apply.MapElementData{ RawElementData: data, }, }, nil } // If it looks like a map, and no openapi type is found, default to mapItem m, err := getMap(s) if err != nil { return nil, fmt.Errorf("expected openapi Kind or Map, was %T for %v (%v)", s, kind, err) } return &mapItem{ Name: name, Map: m, MapElementData: apply.MapElementData{ RawElementData: data, }, }, nil } return nil, fmt.Errorf("unsupported type %v", kind) }