generate.go 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. /*
  2. Copyright 2018 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 generate
  14. import (
  15. "fmt"
  16. "reflect"
  17. "strconv"
  18. "strings"
  19. "github.com/spf13/cobra"
  20. "github.com/spf13/pflag"
  21. "k8s.io/apimachinery/pkg/runtime"
  22. utilerrors "k8s.io/apimachinery/pkg/util/errors"
  23. )
  24. // GeneratorFunc returns the generators for the provided command
  25. type GeneratorFunc func(cmdName string) map[string]Generator
  26. // GeneratorParam is a parameter for a generator
  27. // TODO: facilitate structured json generator input schemes
  28. type GeneratorParam struct {
  29. Name string
  30. Required bool
  31. }
  32. // Generator is an interface for things that can generate API objects from input
  33. // parameters. One example is the "expose" generator that is capable of exposing
  34. // new replication controllers and services, among other things.
  35. type Generator interface {
  36. // Generate creates an API object given a set of parameters
  37. Generate(params map[string]interface{}) (runtime.Object, error)
  38. // ParamNames returns the list of parameters that this generator uses
  39. ParamNames() []GeneratorParam
  40. }
  41. // StructuredGenerator is an interface for things that can generate API objects not using parameter injection
  42. type StructuredGenerator interface {
  43. // StructuredGenerator creates an API object using pre-configured parameters
  44. StructuredGenerate() (runtime.Object, error)
  45. }
  46. func IsZero(i interface{}) bool {
  47. if i == nil {
  48. return true
  49. }
  50. return reflect.DeepEqual(i, reflect.Zero(reflect.TypeOf(i)).Interface())
  51. }
  52. // ValidateParams ensures that all required params are present in the params map
  53. func ValidateParams(paramSpec []GeneratorParam, params map[string]interface{}) error {
  54. allErrs := []error{}
  55. for ix := range paramSpec {
  56. if paramSpec[ix].Required {
  57. value, found := params[paramSpec[ix].Name]
  58. if !found || IsZero(value) {
  59. allErrs = append(allErrs, fmt.Errorf("Parameter: %s is required", paramSpec[ix].Name))
  60. }
  61. }
  62. }
  63. return utilerrors.NewAggregate(allErrs)
  64. }
  65. // AnnotateFlags annotates all flags that are used by generators.
  66. func AnnotateFlags(cmd *cobra.Command, generators map[string]Generator) {
  67. // Iterate over all generators and mark any flags used by them.
  68. for name, generator := range generators {
  69. generatorParams := map[string]struct{}{}
  70. for _, param := range generator.ParamNames() {
  71. generatorParams[param.Name] = struct{}{}
  72. }
  73. cmd.Flags().VisitAll(func(flag *pflag.Flag) {
  74. if _, found := generatorParams[flag.Name]; !found {
  75. // This flag is not used by the current generator
  76. // so skip it.
  77. return
  78. }
  79. if flag.Annotations == nil {
  80. flag.Annotations = map[string][]string{}
  81. }
  82. if annotations := flag.Annotations["generator"]; annotations == nil {
  83. flag.Annotations["generator"] = []string{}
  84. }
  85. flag.Annotations["generator"] = append(flag.Annotations["generator"], name)
  86. })
  87. }
  88. }
  89. // EnsureFlagsValid ensures that no invalid flags are being used against a
  90. func EnsureFlagsValid(cmd *cobra.Command, generators map[string]Generator, generatorInUse string) error {
  91. AnnotateFlags(cmd, generators)
  92. allErrs := []error{}
  93. cmd.Flags().VisitAll(func(flag *pflag.Flag) {
  94. // If the flag hasn't changed, don't validate it.
  95. if !flag.Changed {
  96. return
  97. }
  98. // Look into the flag annotations for the generators that can use it.
  99. if annotations := flag.Annotations["generator"]; len(annotations) > 0 {
  100. annotationMap := map[string]struct{}{}
  101. for _, ann := range annotations {
  102. annotationMap[ann] = struct{}{}
  103. }
  104. // If the current generator is not annotated, then this flag shouldn't
  105. // be used with it.
  106. if _, found := annotationMap[generatorInUse]; !found {
  107. allErrs = append(allErrs, fmt.Errorf("cannot use --%s with --generator=%s", flag.Name, generatorInUse))
  108. }
  109. }
  110. })
  111. return utilerrors.NewAggregate(allErrs)
  112. }
  113. // MakeParams is a utility that creates generator parameters from a command line
  114. func MakeParams(cmd *cobra.Command, params []GeneratorParam) map[string]interface{} {
  115. result := map[string]interface{}{}
  116. for ix := range params {
  117. f := cmd.Flags().Lookup(params[ix].Name)
  118. if f != nil {
  119. result[params[ix].Name] = f.Value.String()
  120. }
  121. }
  122. return result
  123. }
  124. func MakeProtocols(protocols map[string]string) string {
  125. out := []string{}
  126. for key, value := range protocols {
  127. out = append(out, fmt.Sprintf("%s/%s", key, value))
  128. }
  129. return strings.Join(out, ",")
  130. }
  131. func ParseProtocols(protocols interface{}) (map[string]string, error) {
  132. protocolsString, isString := protocols.(string)
  133. if !isString {
  134. return nil, fmt.Errorf("expected string, found %v", protocols)
  135. }
  136. if len(protocolsString) == 0 {
  137. return nil, fmt.Errorf("no protocols passed")
  138. }
  139. portProtocolMap := map[string]string{}
  140. protocolsSlice := strings.Split(protocolsString, ",")
  141. for ix := range protocolsSlice {
  142. portProtocol := strings.Split(protocolsSlice[ix], "/")
  143. if len(portProtocol) != 2 {
  144. return nil, fmt.Errorf("unexpected port protocol mapping: %s", protocolsSlice[ix])
  145. }
  146. if len(portProtocol[0]) == 0 {
  147. return nil, fmt.Errorf("unexpected empty port")
  148. }
  149. if len(portProtocol[1]) == 0 {
  150. return nil, fmt.Errorf("unexpected empty protocol")
  151. }
  152. portProtocolMap[portProtocol[0]] = portProtocol[1]
  153. }
  154. return portProtocolMap, nil
  155. }
  156. func MakeLabels(labels map[string]string) string {
  157. out := []string{}
  158. for key, value := range labels {
  159. out = append(out, fmt.Sprintf("%s=%s", key, value))
  160. }
  161. return strings.Join(out, ",")
  162. }
  163. // ParseLabels turns a string representation of a label set into a map[string]string
  164. func ParseLabels(labelSpec interface{}) (map[string]string, error) {
  165. labelString, isString := labelSpec.(string)
  166. if !isString {
  167. return nil, fmt.Errorf("expected string, found %v", labelSpec)
  168. }
  169. if len(labelString) == 0 {
  170. return nil, fmt.Errorf("no label spec passed")
  171. }
  172. labels := map[string]string{}
  173. labelSpecs := strings.Split(labelString, ",")
  174. for ix := range labelSpecs {
  175. labelSpec := strings.Split(labelSpecs[ix], "=")
  176. if len(labelSpec) != 2 {
  177. return nil, fmt.Errorf("unexpected label spec: %s", labelSpecs[ix])
  178. }
  179. if len(labelSpec[0]) == 0 {
  180. return nil, fmt.Errorf("unexpected empty label key")
  181. }
  182. labels[labelSpec[0]] = labelSpec[1]
  183. }
  184. return labels, nil
  185. }
  186. func GetBool(params map[string]string, key string, defValue bool) (bool, error) {
  187. if val, found := params[key]; !found {
  188. return defValue, nil
  189. } else {
  190. return strconv.ParseBool(val)
  191. }
  192. }