cli-utils.go 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. // Copyright 2018
  2. //
  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. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  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.
  14. package crdvalidation
  15. import (
  16. "encoding/json"
  17. "flag"
  18. "os"
  19. "strings"
  20. "github.com/ghodss/yaml"
  21. extensionsobj "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
  22. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  23. "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
  24. )
  25. // Config stores the user configuration input
  26. type Config struct {
  27. SpecDefinitionName string
  28. EnableValidation bool
  29. OutputFormat string
  30. Labels Labels
  31. Annotations Labels
  32. ResourceScope string
  33. Group string
  34. Kind string
  35. Version string
  36. Plural string
  37. SpecReplicasPath string
  38. StatusReplicasPath string
  39. LabelSelectorPath string
  40. Categories []string
  41. ShortNames []string
  42. GetOpenAPIDefinitions GetAPIDefinitions
  43. }
  44. type Labels struct {
  45. LabelsString string
  46. LabelsMap map[string]string
  47. }
  48. // Implement the flag.Value interface
  49. func (labels *Labels) String() string {
  50. return labels.LabelsString
  51. }
  52. // Merge labels create a new map with labels merged.
  53. func (labels *Labels) Merge(otherLabels map[string]string) map[string]string {
  54. mergedLabels := map[string]string{}
  55. for key, value := range otherLabels {
  56. mergedLabels[key] = value
  57. }
  58. for key, value := range labels.LabelsMap {
  59. mergedLabels[key] = value
  60. }
  61. return mergedLabels
  62. }
  63. // Implement the flag.Set interface
  64. func (labels *Labels) Set(value string) error {
  65. m := map[string]string{}
  66. if value != "" {
  67. splited := strings.Split(value, ",")
  68. for _, pair := range splited {
  69. sp := strings.Split(pair, "=")
  70. m[sp[0]] = sp[1]
  71. }
  72. }
  73. (*labels).LabelsMap = m
  74. (*labels).LabelsString = value
  75. return nil
  76. }
  77. func NewCustomResourceDefinition(config Config) *extensionsobj.CustomResourceDefinition {
  78. crd := &extensionsobj.CustomResourceDefinition{
  79. ObjectMeta: metav1.ObjectMeta{
  80. Name: config.Plural + "." + config.Group,
  81. Labels: config.Labels.LabelsMap,
  82. Annotations: config.Annotations.LabelsMap,
  83. },
  84. TypeMeta: CustomResourceDefinitionTypeMeta,
  85. Spec: extensionsobj.CustomResourceDefinitionSpec{
  86. Group: config.Group,
  87. Version: config.Version,
  88. Scope: extensionsobj.ResourceScope(config.ResourceScope),
  89. Names: extensionsobj.CustomResourceDefinitionNames{
  90. Plural: config.Plural,
  91. Kind: config.Kind,
  92. Categories: config.Categories,
  93. ShortNames: config.ShortNames,
  94. },
  95. Subresources: &extensionsobj.CustomResourceSubresources{
  96. Status: &extensionsobj.CustomResourceSubresourceStatus {
  97. },
  98. Scale: &extensionsobj.CustomResourceSubresourceScale {
  99. SpecReplicasPath: config.SpecReplicasPath,
  100. StatusReplicasPath: config.StatusReplicasPath,
  101. LabelSelectorPath: &config.LabelSelectorPath,
  102. },
  103. },
  104. },
  105. }
  106. if config.SpecDefinitionName != "" && config.EnableValidation == true {
  107. crd.Spec.Validation = GetCustomResourceValidation(config.SpecDefinitionName, config.GetOpenAPIDefinitions)
  108. }
  109. return crd
  110. }
  111. func MarshallCrd(crd *extensionsobj.CustomResourceDefinition, outputFormat string) error {
  112. jsonBytes, err := json.Marshal(crd)
  113. if err != nil {
  114. return err
  115. }
  116. var r unstructured.Unstructured
  117. if err := json.Unmarshal(jsonBytes, &r.Object); err != nil {
  118. return err
  119. }
  120. unstructured.RemoveNestedField(r.Object, "status")
  121. jsonBytes, err = json.MarshalIndent(r.Object, "", " ")
  122. if err != nil {
  123. return err
  124. }
  125. if outputFormat == "json" {
  126. _, err = os.Stdout.Write(jsonBytes)
  127. if err != nil {
  128. return err
  129. }
  130. } else {
  131. yamlBytes, err := yaml.JSONToYAML(jsonBytes)
  132. if err != nil {
  133. return err
  134. }
  135. _, err = os.Stdout.Write([]byte("---\n"))
  136. if err != nil {
  137. return err
  138. }
  139. _, err = os.Stdout.Write(yamlBytes)
  140. if err != nil {
  141. return err
  142. }
  143. }
  144. return nil
  145. }
  146. // InitFlags prepares command line flags parser
  147. func InitFlags(cfg *Config, flagset *flag.FlagSet) *flag.FlagSet {
  148. flagset.Var(&cfg.Labels, "labels", "Labels")
  149. flagset.Var(&cfg.Annotations, "annotations", "Annotations")
  150. flagset.BoolVar(&cfg.EnableValidation, "with-validation", true, "Add CRD validation field, default: true")
  151. flagset.StringVar(&cfg.Group, "apigroup", "custom.example.com", "CRD api group")
  152. flagset.StringVar(&cfg.SpecDefinitionName, "spec-name", "", "CRD spec definition name")
  153. flagset.StringVar(&cfg.OutputFormat, "output", "yaml", "output format: json|yaml")
  154. flagset.StringVar(&cfg.Kind, "kind", "", "CRD Kind")
  155. flagset.StringVar(&cfg.ResourceScope, "scope", string(extensionsobj.NamespaceScoped), "CRD scope: 'Namespaced' | 'Cluster'. Default: Namespaced")
  156. flagset.StringVar(&cfg.Version, "version", "v1", "CRD version, default: 'v1'")
  157. flagset.StringVar(&cfg.Plural, "plural", "", "CRD plural name")
  158. flagset.StringVar(&cfg.SpecReplicasPath, "spec-replicas-path", ".spec.replicas", "CRD spec replicas path")
  159. flagset.StringVar(&cfg.StatusReplicasPath, "status-replicas-path", ".status.replicas", "CRD status replicas path")
  160. flagset.StringVar(&cfg.LabelSelectorPath, "label-selector-path", ".status.labelSelector", "CRD label selector path")
  161. return flagset
  162. }