123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153 |
- /*
- Copyright 2014 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 config
- import (
- "fmt"
- "reflect"
- "strings"
- "k8s.io/apimachinery/pkg/util/sets"
- clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
- )
- type navigationSteps struct {
- steps []navigationStep
- currentStepIndex int
- }
- type navigationStep struct {
- stepValue string
- stepType reflect.Type
- }
- func newNavigationSteps(path string) (*navigationSteps, error) {
- steps := []navigationStep{}
- individualParts := strings.Split(path, ".")
- currType := reflect.TypeOf(clientcmdapi.Config{})
- currPartIndex := 0
- for currPartIndex < len(individualParts) {
- switch currType.Kind() {
- case reflect.Map:
- // if we're in a map, we need to locate a name. That name may contain dots, so we need to know what tokens are legal for the map's value type
- // for example, we could have a set request like: `set clusters.10.10.12.56.insecure-skip-tls-verify true`. We enter this case with
- // steps representing 10, 10, 12, 56, insecure-skip-tls-verify. The name is "10.10.12.56", so we want to collect all those parts together and
- // store them as a single step. In order to do that, we need to determine what set of tokens is a legal step AFTER the name of the map key
- // This set of reflective code pulls the type of the map values, uses that type to look up the set of legal tags. Those legal tags are used to
- // walk the list of remaining parts until we find a match to a legal tag or the end of the string. That name is used to burn all the used parts.
- mapValueType := currType.Elem().Elem()
- mapValueOptions, err := getPotentialTypeValues(mapValueType)
- if err != nil {
- return nil, err
- }
- nextPart := findNameStep(individualParts[currPartIndex:], sets.StringKeySet(mapValueOptions))
- steps = append(steps, navigationStep{nextPart, mapValueType})
- currPartIndex += len(strings.Split(nextPart, "."))
- currType = mapValueType
- case reflect.Struct:
- nextPart := individualParts[currPartIndex]
- options, err := getPotentialTypeValues(currType)
- if err != nil {
- return nil, err
- }
- fieldType, exists := options[nextPart]
- if !exists {
- return nil, fmt.Errorf("unable to parse %v after %v at %v", path, steps, currType)
- }
- steps = append(steps, navigationStep{nextPart, fieldType})
- currPartIndex += len(strings.Split(nextPart, "."))
- currType = fieldType
- }
- }
- return &navigationSteps{steps, 0}, nil
- }
- func (s *navigationSteps) pop() navigationStep {
- if s.moreStepsRemaining() {
- s.currentStepIndex++
- return s.steps[s.currentStepIndex-1]
- }
- return navigationStep{}
- }
- func (s *navigationSteps) peek() navigationStep {
- if s.moreStepsRemaining() {
- return s.steps[s.currentStepIndex]
- }
- return navigationStep{}
- }
- func (s *navigationSteps) moreStepsRemaining() bool {
- return len(s.steps) > s.currentStepIndex
- }
- // findNameStep takes the list of parts and a set of valid tags that can be used after the name. It then walks the list of parts
- // until it find a valid "next" tag or until it reaches the end of the parts and then builds the name back up out of the individual parts
- func findNameStep(parts []string, typeOptions sets.String) string {
- if len(parts) == 0 {
- return ""
- }
- numberOfPartsInStep := findKnownValue(parts[1:], typeOptions) + 1
- // if we didn't find a known value, then the entire thing must be a name
- if numberOfPartsInStep == 0 {
- numberOfPartsInStep = len(parts)
- }
- nextParts := parts[0:numberOfPartsInStep]
- return strings.Join(nextParts, ".")
- }
- // getPotentialTypeValues takes a type and looks up the tags used to represent its fields when serialized.
- func getPotentialTypeValues(typeValue reflect.Type) (map[string]reflect.Type, error) {
- if typeValue.Kind() == reflect.Ptr {
- typeValue = typeValue.Elem()
- }
- if typeValue.Kind() != reflect.Struct {
- return nil, fmt.Errorf("%v is not of type struct", typeValue)
- }
- ret := make(map[string]reflect.Type)
- for fieldIndex := 0; fieldIndex < typeValue.NumField(); fieldIndex++ {
- fieldType := typeValue.Field(fieldIndex)
- yamlTag := fieldType.Tag.Get("json")
- yamlTagName := strings.Split(yamlTag, ",")[0]
- ret[yamlTagName] = fieldType.Type
- }
- return ret, nil
- }
- func findKnownValue(parts []string, valueOptions sets.String) int {
- for i := range parts {
- if valueOptions.Has(parts[i]) {
- return i
- }
- }
- return -1
- }
|