1 // -*- fill-column: 70 -*- 2 3 // Copyright 2020-2021 Datawire. All rights reserved 4 // 5 // Licensed under the Apache License, Version 2.0 (the "License"); you 6 // may not use this file except in compliance with the License. You 7 // may obtain a copy of the License at 8 // 9 // http://www.apache.org/licenses/LICENSE-2.0 10 // 11 // Unless required by applicable law or agreed to in writing, software 12 // distributed under the License is distributed on an "AS IS" BASIS, 13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 14 // implied. See the License for the specific language governing 15 // permissions and limitations under the License. 16 17 ////////////////////////////////////////////////////////////////////// 18 // 0. Table of Contents ////////////////////////////////////////////// 19 ////////////////////////////////////////////////////////////////////// 20 // 21 // This file deals with common package/apiVersion-level things not 22 // specific to any individual CRD: 23 // 24 // 1. Magic markers: Document the various magic "+marker" comments 25 // that are used in this package, and set up the package-level 26 // markers. 27 // 28 // 2. Package documentation: The `godoc` package-wide documentation. 29 // 30 // 3. Scheme: Set up the Group/Version/SchemeBuilder/AddToScheme for 31 // this apiVersion. 32 // 33 // 4. API design guidelines: Guidelines for additions to this 34 // package. 35 // 36 // Things that are shared between multiple CRDs, but are for 37 // individual CRDs rather than the package/apiVersion as a whole, do 38 // not belong in this file; they belong in `common.go`. 39 40 ////////////////////////////////////////////////////////////////////// 41 // 1. Magic markers ////////////////////////////////////////////////// 42 ////////////////////////////////////////////////////////////////////// 43 // 44 // We use a bunch of magic comments called "+markers" that serve as 45 // input to `controller-gen` and `conversion-gen`. Note that while 46 // `controller-gen` doesn't care about what file these are in, the 47 // older `k8s.io/gengo`-based `conversion-gen` specifically looks for 48 // `doc.go`. Mostly they annotate a type, or a field within a struct. 49 // Just below here, we do the "global" package-level markers; these 50 // package-level markers need to come before the "package" line. 51 // 52 // The type markers of interest are: 53 // 54 // - "+kubebuilder:object:generate=bool" whether to generate 55 // `DeepCopy` and `DeepCopyInto` methods for this type; but we 56 // don't actually set this on types, since we can set it to true 57 // for all types at the package-level. 58 // 59 // - "+kubebuilder:object:root=bool" whether to *also* generate a 60 // `DeepCopyObject` method. It upsets me that controller-gen 61 // doesn't infer this based on the presence of metav1.TypeMeta 62 // inside of the type. 63 // 64 // - "+kubebuilder:subresource:status" whether to add "status" as a 65 // subresource for that type. It upsets me that controller-gen 66 // doesn't infer this based on the presence of a `status` field 67 // inside of the type. 68 // 69 // The field markers of interest are: 70 // 71 // - The "+kubebuilder:validation:*" markers control the OpenAPI v3 72 // validation schema that is generated for this field. ":Optional" 73 // or ":Required" may be applied at the package-level in order to 74 // set the default for all fields. Most of the others can also be 75 // set at the type level. 76 // 77 // Package-level markers: 78 // 79 // The group name to use for the CRDs in the generated YAML: 80 // +groupName=getambassador.io 81 // +versionName=v3alpha1 82 // 83 // By default, mark all types in this package to have DeepCopy methods 84 // generated (so we don't need to specify this for every type): 85 // +kubebuilder:object:generate=true 86 // 87 // By default, mark all fields as optional (so we don't need to 88 // specify this for every optional field, since most fields are 89 // optional; and also because controller-gen's "required-by-default" 90 // mode is broken and always makes everything optional, even if it's 91 // explicitly marked as required): 92 // +kubebuilder:validation:Optional 93 // 94 // Other apiVersions say "+k8s:conversion-gen=…/v3alpha1" to have 95 // conversion-gen help write code to convert to/from this apiVersion 96 // and that one. 97 98 ////////////////////////////////////////////////////////////////////// 99 // 2. Package documentation ////////////////////////////////////////// 100 ////////////////////////////////////////////////////////////////////// 101 102 // Package v3alpha1 contains API Schema definitions for the 103 // getambassador.io v3alpha1 API group 104 package v3alpha1 105 106 ////////////////////////////////////////////////////////////////////// 107 // 3. Scheme ///////////////////////////////////////////////////////// 108 ////////////////////////////////////////////////////////////////////// 109 110 import ( 111 "k8s.io/apimachinery/pkg/runtime/schema" 112 "sigs.k8s.io/controller-runtime/pkg/scheme" 113 ) 114 115 var ( 116 // GroupVersion is group version used to register these objects 117 GroupVersion = schema.GroupVersion{Group: "getambassador.io", Version: "v3alpha1"} 118 119 // SchemeBuilder is used to add go types to the GroupVersionKind scheme 120 SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} 121 122 // AddToScheme adds the types in this group-version to the given scheme. 123 AddToScheme = SchemeBuilder.AddToScheme 124 ) 125 126 ////////////////////////////////////////////////////////////////////// 127 // 4. API design guidelines ////////////////////////////////////////// 128 ////////////////////////////////////////////////////////////////////// 129 // 130 // Ambassador's API has inconsistencies because it has historical 131 // baggage. Not all of Ambassador's existing API (or even most of 132 // it!?) follow these guidelines, but new additions to the API should. 133 // If/when we advance to getambassador.io/v3 and we can break 134 // compatibility, these are things that we should apply everywhere. 135 // 136 // - Prefer `camelCase` to `snake_case` 137 // * Exception: Except for consistency with existing fields in the 138 // same resource, or symmetry with identical fields in another 139 // resource. 140 // * Justification: Kubernetes style is to use camelCase. But 141 // historically Ambassador used snake_case for everything. 142 // 143 // - Give _every_ field a `json:""` struct tag. 144 // * Justification: Marshaling and unmarshaling are key to what we 145 // do, and it's critical to carefully define how it happens. 146 // * Notes: This is not optional. Do it for _every field_. (It's OK 147 // if the tag is literally `json:""` for fields that must never be 148 // exposed during marshaling.) 149 // 150 // - Prefer `*int`, and `*bool`; rather than just `int`, `bool`. 151 // * Justification: The Ambassador API is rooted in Python, where it 152 // is always possible to tell if a given element was present in in 153 // a CRD, or left unset. This is at odds with Go's `omitempty` 154 // specifier, which really means "omit if empty _or if set to the 155 // default (zero) value_". For int in particular, this results in 156 // a value of 0 being omitted, and for many Ambassador fields, 0 157 // is not the correct default value. 158 // 159 // This resulted in a lot of bugs in the 1.10 timeframe, so be 160 // careful going forward. 161 // 162 // - Prefer for object references to not support namespacing 163 // * Exception: If there's a real use-case for it. 164 // * Justification: Most native Kubernetes resources don't support 165 // referencing things in a different namespace. We should be 166 // opinionated and not support it either, unless there's a good 167 // reason to in a specific case. 168 // 169 // - Prefer to use `corev1.LocalObjectReference` or 170 // `corev1.SecretReference` references instead of 171 // `{name}.{namespace}` strings. 172 // * Justification: The `{name}.{namespace}` thing evolved "an 173 // opaque DNS name" in the `service` field of Mappings, and that 174 // was generalized to other things. Outside of the context of 175 // "this is usable as a DNS name to make a request to", it's just 176 // confusing and introduces needless ambiguity. Nothing other 177 // than Ambassador uses that notation. 178 // * Notes: For things that don't support cross-namespace references 179 // (see above), use LocalObjectReference; if you really must 180 // support cross-namespace references, then use SecretReference. 181 // 182 // - Prefer to use `metav1.Duration` fields instead of "_s" or "_ms" 183 // numeric fields. 184 // 185 // - Don't have Ambassador populate anything in the `.spec` or 186 // `.metadata` of something a user might edit, only let Ambassador 187 // set things in the `.status`. 188 // * Exception: If Ambassador 100% owns the resource and a user will 189 // never edit it. 190 // * Notes: I didn't write "Prefer" on this one. Don't violate it. 191 // Just don't do it. Ever. Designing the Host resource in 192 // violation of this was a HUGE mistake and one that I regret very 193 // much. Learn from my mistakes. 194 // * Justification: Having Ambassador-set things in a subresource 195 // from user-set things: 196 // 1. avoids races between the user updating the spec and us 197 // updating the status 198 // 2. allows watt/whatever to only pay attention to 199 // .metadata.generation instead of .metadata.resourceVersion; 200 // avoiding pointless reconfigures. 201 // 3. allows the RBAC to be simpler 202 // 4. avoids the whole class of bugs where we need to make sure 203 // that everything round-trips correctly 204 // 5. provides clarity on which things a user is expected to know 205 // how to fill in 206