navigation_step_parser.go 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. /*
  2. Copyright 2014 The Kubernetes 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
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package config
  14. import (
  15. "fmt"
  16. "reflect"
  17. "strings"
  18. "k8s.io/apimachinery/pkg/util/sets"
  19. clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
  20. )
  21. type navigationSteps struct {
  22. steps []navigationStep
  23. currentStepIndex int
  24. }
  25. type navigationStep struct {
  26. stepValue string
  27. stepType reflect.Type
  28. }
  29. func newNavigationSteps(path string) (*navigationSteps, error) {
  30. steps := []navigationStep{}
  31. individualParts := strings.Split(path, ".")
  32. currType := reflect.TypeOf(clientcmdapi.Config{})
  33. currPartIndex := 0
  34. for currPartIndex < len(individualParts) {
  35. switch currType.Kind() {
  36. case reflect.Map:
  37. // 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
  38. // 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
  39. // 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
  40. // 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
  41. // 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
  42. // 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.
  43. mapValueType := currType.Elem().Elem()
  44. mapValueOptions, err := getPotentialTypeValues(mapValueType)
  45. if err != nil {
  46. return nil, err
  47. }
  48. nextPart := findNameStep(individualParts[currPartIndex:], sets.StringKeySet(mapValueOptions))
  49. steps = append(steps, navigationStep{nextPart, mapValueType})
  50. currPartIndex += len(strings.Split(nextPart, "."))
  51. currType = mapValueType
  52. case reflect.Struct:
  53. nextPart := individualParts[currPartIndex]
  54. options, err := getPotentialTypeValues(currType)
  55. if err != nil {
  56. return nil, err
  57. }
  58. fieldType, exists := options[nextPart]
  59. if !exists {
  60. return nil, fmt.Errorf("unable to parse %v after %v at %v", path, steps, currType)
  61. }
  62. steps = append(steps, navigationStep{nextPart, fieldType})
  63. currPartIndex += len(strings.Split(nextPart, "."))
  64. currType = fieldType
  65. }
  66. }
  67. return &navigationSteps{steps, 0}, nil
  68. }
  69. func (s *navigationSteps) pop() navigationStep {
  70. if s.moreStepsRemaining() {
  71. s.currentStepIndex++
  72. return s.steps[s.currentStepIndex-1]
  73. }
  74. return navigationStep{}
  75. }
  76. func (s *navigationSteps) peek() navigationStep {
  77. if s.moreStepsRemaining() {
  78. return s.steps[s.currentStepIndex]
  79. }
  80. return navigationStep{}
  81. }
  82. func (s *navigationSteps) moreStepsRemaining() bool {
  83. return len(s.steps) > s.currentStepIndex
  84. }
  85. // 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
  86. // 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
  87. func findNameStep(parts []string, typeOptions sets.String) string {
  88. if len(parts) == 0 {
  89. return ""
  90. }
  91. numberOfPartsInStep := findKnownValue(parts[1:], typeOptions) + 1
  92. // if we didn't find a known value, then the entire thing must be a name
  93. if numberOfPartsInStep == 0 {
  94. numberOfPartsInStep = len(parts)
  95. }
  96. nextParts := parts[0:numberOfPartsInStep]
  97. return strings.Join(nextParts, ".")
  98. }
  99. // getPotentialTypeValues takes a type and looks up the tags used to represent its fields when serialized.
  100. func getPotentialTypeValues(typeValue reflect.Type) (map[string]reflect.Type, error) {
  101. if typeValue.Kind() == reflect.Ptr {
  102. typeValue = typeValue.Elem()
  103. }
  104. if typeValue.Kind() != reflect.Struct {
  105. return nil, fmt.Errorf("%v is not of type struct", typeValue)
  106. }
  107. ret := make(map[string]reflect.Type)
  108. for fieldIndex := 0; fieldIndex < typeValue.NumField(); fieldIndex++ {
  109. fieldType := typeValue.Field(fieldIndex)
  110. yamlTag := fieldType.Tag.Get("json")
  111. yamlTagName := strings.Split(yamlTag, ",")[0]
  112. ret[yamlTagName] = fieldType.Type
  113. }
  114. return ret, nil
  115. }
  116. func findKnownValue(parts []string, valueOptions sets.String) int {
  117. for i := range parts {
  118. if valueOptions.Has(parts[i]) {
  119. return i
  120. }
  121. }
  122. return -1
  123. }