github.com/Diggs/controller-tools@v0.4.2/pkg/crd/markers/crd.go (about) 1 /* 2 Copyright 2019 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package markers 18 19 import ( 20 "fmt" 21 22 apiext "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" 23 24 "github.com/Diggs/controller-tools/pkg/markers" 25 ) 26 27 // CRDMarkers lists all markers that directly modify the CRD (not validation 28 // schemas). 29 var CRDMarkers = []*definitionWithHelp{ 30 // TODO(directxman12): more detailed help 31 must(markers.MakeDefinition("kubebuilder:subresource:status", markers.DescribesType, SubresourceStatus{})). 32 WithHelp(SubresourceStatus{}.Help()), 33 34 must(markers.MakeDefinition("kubebuilder:subresource:scale", markers.DescribesType, SubresourceScale{})). 35 WithHelp(SubresourceScale{}.Help()), 36 37 must(markers.MakeDefinition("kubebuilder:printcolumn", markers.DescribesType, PrintColumn{})). 38 WithHelp(PrintColumn{}.Help()), 39 40 must(markers.MakeDefinition("kubebuilder:resource", markers.DescribesType, Resource{})). 41 WithHelp(Resource{}.Help()), 42 43 must(markers.MakeDefinition("kubebuilder:storageversion", markers.DescribesType, StorageVersion{})). 44 WithHelp(StorageVersion{}.Help()), 45 46 must(markers.MakeDefinition("kubebuilder:skipversion", markers.DescribesType, SkipVersion{})). 47 WithHelp(SkipVersion{}.Help()), 48 49 must(markers.MakeDefinition("kubebuilder:unservedversion", markers.DescribesType, UnservedVersion{})). 50 WithHelp(UnservedVersion{}.Help()), 51 } 52 53 // TODO: categories and singular used to be annotations types 54 // TODO: doc 55 56 func init() { 57 AllDefinitions = append(AllDefinitions, CRDMarkers...) 58 } 59 60 // +controllertools:marker:generateHelp:category=CRD 61 62 // SubresourceStatus enables the "/status" subresource on a CRD. 63 type SubresourceStatus struct{} 64 65 func (s SubresourceStatus) ApplyToCRD(crd *apiext.CustomResourceDefinitionSpec, version string) error { 66 var subresources *apiext.CustomResourceSubresources 67 for i := range crd.Versions { 68 ver := &crd.Versions[i] 69 if ver.Name != version { 70 continue 71 } 72 if ver.Subresources == nil { 73 ver.Subresources = &apiext.CustomResourceSubresources{} 74 } 75 subresources = ver.Subresources 76 break 77 } 78 if subresources == nil { 79 return fmt.Errorf("status subresource applied to version %q not in CRD", version) 80 } 81 subresources.Status = &apiext.CustomResourceSubresourceStatus{} 82 return nil 83 } 84 85 // +controllertools:marker:generateHelp:category=CRD 86 87 // SubresourceScale enables the "/scale" subresource on a CRD. 88 type SubresourceScale struct { 89 // marker names are leftover legacy cruft 90 91 // SpecPath specifies the jsonpath to the replicas field for the scale's spec. 92 SpecPath string `marker:"specpath"` 93 94 // StatusPath specifies the jsonpath to the replicas field for the scale's status. 95 StatusPath string `marker:"statuspath"` 96 97 // SelectorPath specifies the jsonpath to the pod label selector field for the scale's status. 98 // 99 // The selector field must be the *string* form (serialized form) of a selector. 100 // Setting a pod label selector is necessary for your type to work with the HorizontalPodAutoscaler. 101 SelectorPath *string `marker:"selectorpath"` 102 } 103 104 func (s SubresourceScale) ApplyToCRD(crd *apiext.CustomResourceDefinitionSpec, version string) error { 105 var subresources *apiext.CustomResourceSubresources 106 for i := range crd.Versions { 107 ver := &crd.Versions[i] 108 if ver.Name != version { 109 continue 110 } 111 if ver.Subresources == nil { 112 ver.Subresources = &apiext.CustomResourceSubresources{} 113 } 114 subresources = ver.Subresources 115 break 116 } 117 if subresources == nil { 118 return fmt.Errorf("scale subresource applied to version %q not in CRD", version) 119 } 120 subresources.Scale = &apiext.CustomResourceSubresourceScale{ 121 SpecReplicasPath: s.SpecPath, 122 StatusReplicasPath: s.StatusPath, 123 LabelSelectorPath: s.SelectorPath, 124 } 125 return nil 126 } 127 128 // +controllertools:marker:generateHelp:category=CRD 129 130 // StorageVersion marks this version as the "storage version" for the CRD for conversion. 131 // 132 // When conversion is enabled for a CRD (i.e. it's not a trivial-versions/single-version CRD), 133 // one version is set as the "storage version" to be stored in etcd. Attempting to store any 134 // other version will result in conversion to the storage version via a conversion webhook. 135 type StorageVersion struct{} 136 137 func (s StorageVersion) ApplyToCRD(crd *apiext.CustomResourceDefinitionSpec, version string) error { 138 if version == "" { 139 // single-version, do nothing 140 return nil 141 } 142 // multi-version 143 for i := range crd.Versions { 144 ver := &crd.Versions[i] 145 if ver.Name != version { 146 continue 147 } 148 ver.Storage = true 149 break 150 } 151 return nil 152 } 153 154 // +controllertools:marker:generateHelp:category=CRD 155 156 // SkipVersion removes the particular version of the CRD from the CRDs spec. 157 // 158 // This is useful if you need to skip generating and listing version entries 159 // for 'internal' resource versions, which typically exist if using the 160 // Kubernetes upstream conversion-gen tool. 161 type SkipVersion struct{} 162 163 func (s SkipVersion) ApplyToCRD(crd *apiext.CustomResourceDefinitionSpec, version string) error { 164 if version == "" { 165 // single-version, this is an invalid state 166 return fmt.Errorf("cannot skip a version if there is only a single version") 167 } 168 var versions []apiext.CustomResourceDefinitionVersion 169 // multi-version 170 for i := range crd.Versions { 171 ver := crd.Versions[i] 172 if ver.Name == version { 173 // skip the skipped version 174 continue 175 } 176 versions = append(versions, ver) 177 } 178 crd.Versions = versions 179 return nil 180 } 181 182 // +controllertools:marker:generateHelp:category=CRD 183 184 // PrintColumn adds a column to "kubectl get" output for this CRD. 185 type PrintColumn struct { 186 // Name specifies the name of the column. 187 Name string 188 189 // Type indicates the type of the column. 190 // 191 // It may be any OpenAPI data type listed at 192 // https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#data-types. 193 Type string 194 195 // JSONPath specifies the jsonpath expression used to extract the value of the column. 196 JSONPath string `marker:"JSONPath"` // legacy cruft 197 198 // Description specifies the help/description for this column. 199 Description string `marker:",optional"` 200 201 // Format specifies the format of the column. 202 // 203 // It may be any OpenAPI data format corresponding to the type, listed at 204 // https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#data-types. 205 Format string `marker:",optional"` 206 207 // Priority indicates how important it is that this column be displayed. 208 // 209 // Lower priority (*higher* numbered) columns will be hidden if the terminal 210 // width is too small. 211 Priority int32 `marker:",optional"` 212 } 213 214 func (s PrintColumn) ApplyToCRD(crd *apiext.CustomResourceDefinitionSpec, version string) error { 215 var columns *[]apiext.CustomResourceColumnDefinition 216 for i := range crd.Versions { 217 ver := &crd.Versions[i] 218 if ver.Name != version { 219 continue 220 } 221 if ver.Subresources == nil { 222 ver.Subresources = &apiext.CustomResourceSubresources{} 223 } 224 columns = &ver.AdditionalPrinterColumns 225 break 226 } 227 if columns == nil { 228 return fmt.Errorf("printer columns applied to version %q not in CRD", version) 229 } 230 231 *columns = append(*columns, apiext.CustomResourceColumnDefinition{ 232 Name: s.Name, 233 Type: s.Type, 234 JSONPath: s.JSONPath, 235 Description: s.Description, 236 Format: s.Format, 237 Priority: s.Priority, 238 }) 239 240 return nil 241 } 242 243 // +controllertools:marker:generateHelp:category=CRD 244 245 // Resource configures naming and scope for a CRD. 246 type Resource struct { 247 // Path specifies the plural "resource" for this CRD. 248 // 249 // It generally corresponds to a plural, lower-cased version of the Kind. 250 // See https://book.kubebuilder.io/cronjob-tutorial/gvks.html. 251 Path string `marker:",optional"` 252 253 // ShortName specifies aliases for this CRD. 254 // 255 // Short names are often used when people have work with your resource 256 // over and over again. For instance, "rs" for "replicaset" or 257 // "crd" for customresourcedefinition. 258 ShortName []string `marker:",optional"` 259 260 // Categories specifies which group aliases this resource is part of. 261 // 262 // Group aliases are used to work with groups of resources at once. 263 // The most common one is "all" which covers about a third of the base 264 // resources in Kubernetes, and is generally used for "user-facing" resources. 265 Categories []string `marker:",optional"` 266 267 // Singular overrides the singular form of your resource. 268 // 269 // The singular form is otherwise defaulted off the plural (path). 270 Singular string `marker:",optional"` 271 272 // Scope overrides the scope of the CRD (Cluster vs Namespaced). 273 // 274 // Scope defaults to "Namespaced". Cluster-scoped ("Cluster") resources 275 // don't exist in namespaces. 276 Scope string `marker:",optional"` 277 } 278 279 func (s Resource) ApplyToCRD(crd *apiext.CustomResourceDefinitionSpec, version string) error { 280 if s.Path != "" { 281 crd.Names.Plural = s.Path 282 } 283 if s.Singular != "" { 284 crd.Names.Singular = s.Singular 285 } 286 crd.Names.ShortNames = s.ShortName 287 crd.Names.Categories = s.Categories 288 289 switch s.Scope { 290 case "": 291 crd.Scope = apiext.NamespaceScoped 292 default: 293 crd.Scope = apiext.ResourceScope(s.Scope) 294 } 295 296 return nil 297 } 298 299 // +controllertools:marker:generateHelp:category=CRD 300 301 // UnservedVersion does not serve this version. 302 // 303 // This is useful if you need to drop support for a version in favor of a newer version. 304 type UnservedVersion struct{} 305 306 func (s UnservedVersion) ApplyToCRD(crd *apiext.CustomResourceDefinitionSpec, version string) error { 307 for i := range crd.Versions { 308 ver := &crd.Versions[i] 309 if ver.Name != version { 310 continue 311 } 312 ver.Served = false 313 break 314 } 315 return nil 316 } 317 318 // NB(directxman12): singular was historically distinct, so we keep it here for backwards compat