elements.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374
  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 schema
  14. import (
  15. "sync"
  16. )
  17. // Schema is a list of named types.
  18. //
  19. // Schema types are indexed in a map before the first search so this type
  20. // should be considered immutable.
  21. type Schema struct {
  22. Types []TypeDef `yaml:"types,omitempty"`
  23. once sync.Once
  24. m map[string]TypeDef
  25. lock sync.Mutex
  26. // Cached results of resolving type references to atoms. Only stores
  27. // type references which require fields of Atom to be overriden.
  28. resolvedTypes map[TypeRef]Atom
  29. }
  30. // A TypeSpecifier references a particular type in a schema.
  31. type TypeSpecifier struct {
  32. Type TypeRef `yaml:"type,omitempty"`
  33. Schema Schema `yaml:"schema,omitempty"`
  34. }
  35. // TypeDef represents a named type in a schema.
  36. type TypeDef struct {
  37. // Top level types should be named. Every type must have a unique name.
  38. Name string `yaml:"name,omitempty"`
  39. Atom `yaml:"atom,omitempty,inline"`
  40. }
  41. // TypeRef either refers to a named type or declares an inlined type.
  42. type TypeRef struct {
  43. // Either the name or one member of Atom should be set.
  44. NamedType *string `yaml:"namedType,omitempty"`
  45. Inlined Atom `yaml:",inline,omitempty"`
  46. // If this reference refers to a map-type or list-type, this field overrides
  47. // the `ElementRelationship` of the referred type when resolved.
  48. // If this field is nil, then it has no effect.
  49. // See `Map` and `List` for more information about `ElementRelationship`
  50. ElementRelationship *ElementRelationship `yaml:"elementRelationship,omitempty"`
  51. }
  52. // Atom represents the smallest possible pieces of the type system.
  53. // Each set field in the Atom represents a possible type for the object.
  54. // If none of the fields are set, any object will fail validation against the atom.
  55. type Atom struct {
  56. *Scalar `yaml:"scalar,omitempty"`
  57. *List `yaml:"list,omitempty"`
  58. *Map `yaml:"map,omitempty"`
  59. }
  60. // Scalar (AKA "primitive") represents a type which has a single value which is
  61. // either numeric, string, or boolean.
  62. //
  63. // TODO: split numeric into float/int? Something even more fine-grained?
  64. type Scalar string
  65. const (
  66. Numeric = Scalar("numeric")
  67. String = Scalar("string")
  68. Boolean = Scalar("boolean")
  69. )
  70. // ElementRelationship is an enum of the different possible relationships
  71. // between the elements of container types (maps, lists).
  72. type ElementRelationship string
  73. const (
  74. // Associative only applies to lists (see the documentation there).
  75. Associative = ElementRelationship("associative")
  76. // Atomic makes container types (lists, maps) behave
  77. // as scalars / leaf fields
  78. Atomic = ElementRelationship("atomic")
  79. // Separable means the items of the container type have no particular
  80. // relationship (default behavior for maps).
  81. Separable = ElementRelationship("separable")
  82. )
  83. // Map is a key-value pair. Its default semantics are the same as an
  84. // associative list, but:
  85. // - It is serialized differently:
  86. // map: {"k": {"value": "v"}}
  87. // list: [{"key": "k", "value": "v"}]
  88. // - Keys must be string typed.
  89. // - Keys can't have multiple components.
  90. //
  91. // Optionally, maps may be atomic (for example, imagine representing an RGB
  92. // color value--it doesn't make sense to have different actors own the R and G
  93. // values).
  94. //
  95. // Maps may also represent a type which is composed of a number of different fields.
  96. // Each field has a name and a type.
  97. //
  98. // Fields are indexed in a map before the first search so this type
  99. // should be considered immutable.
  100. type Map struct {
  101. // Each struct field appears exactly once in this list. The order in
  102. // this list defines the canonical field ordering.
  103. Fields []StructField `yaml:"fields,omitempty"`
  104. // A Union is a grouping of fields with special rules. It may refer to
  105. // one or more fields in the above list. A given field from the above
  106. // list may be referenced in exactly 0 or 1 places in the below list.
  107. // One can have multiple unions in the same struct, but the fields can't
  108. // overlap between unions.
  109. Unions []Union `yaml:"unions,omitempty"`
  110. // ElementType is the type of the structs's unknown fields.
  111. ElementType TypeRef `yaml:"elementType,omitempty"`
  112. // ElementRelationship states the relationship between the map's items.
  113. // * `separable` (or unset) implies that each element is 100% independent.
  114. // * `atomic` implies that all elements depend on each other, and this
  115. // is effectively a scalar / leaf field; it doesn't make sense for
  116. // separate actors to set the elements. Example: an RGB color struct;
  117. // it would never make sense to "own" only one component of the
  118. // color.
  119. // The default behavior for maps is `separable`; it's permitted to
  120. // leave this unset to get the default behavior.
  121. ElementRelationship ElementRelationship `yaml:"elementRelationship,omitempty"`
  122. once sync.Once
  123. m map[string]StructField
  124. }
  125. // FindField is a convenience function that returns the referenced StructField,
  126. // if it exists, or (nil, false) if it doesn't.
  127. func (m *Map) FindField(name string) (StructField, bool) {
  128. m.once.Do(func() {
  129. m.m = make(map[string]StructField, len(m.Fields))
  130. for _, field := range m.Fields {
  131. m.m[field.Name] = field
  132. }
  133. })
  134. sf, ok := m.m[name]
  135. return sf, ok
  136. }
  137. // CopyInto this instance of Map into the other
  138. // If other is nil this method does nothing.
  139. // If other is already initialized, overwrites it with this instance
  140. // Warning: Not thread safe
  141. func (m *Map) CopyInto(dst *Map) {
  142. if dst == nil {
  143. return
  144. }
  145. // Map type is considered immutable so sharing references
  146. dst.Fields = m.Fields
  147. dst.ElementType = m.ElementType
  148. dst.Unions = m.Unions
  149. dst.ElementRelationship = m.ElementRelationship
  150. if m.m != nil {
  151. // If cache is non-nil then the once token had been consumed.
  152. // Must reset token and use it again to ensure same semantics.
  153. dst.once = sync.Once{}
  154. dst.once.Do(func() {
  155. dst.m = m.m
  156. })
  157. }
  158. }
  159. // UnionFields are mapping between the fields that are part of the union and
  160. // their discriminated value. The discriminated value has to be set, and
  161. // should not conflict with other discriminated value in the list.
  162. type UnionField struct {
  163. // FieldName is the name of the field that is part of the union. This
  164. // is the serialized form of the field.
  165. FieldName string `yaml:"fieldName"`
  166. // Discriminatorvalue is the value of the discriminator to
  167. // select that field. If the union doesn't have a discriminator,
  168. // this field is ignored.
  169. DiscriminatorValue string `yaml:"discriminatorValue"`
  170. }
  171. // Union, or oneof, means that only one of multiple fields of a structure can be
  172. // set at a time. Setting the discriminator helps clearing oher fields:
  173. // - If discriminator changed to non-nil, and a new field has been added
  174. // that doesn't match, an error is returned,
  175. // - If discriminator hasn't changed and two fields or more are set, an
  176. // error is returned,
  177. // - If discriminator changed to non-nil, all other fields but the
  178. // discriminated one will be cleared,
  179. // - Otherwise, If only one field is left, update discriminator to that value.
  180. type Union struct {
  181. // Discriminator, if present, is the name of the field that
  182. // discriminates fields in the union. The mapping between the value of
  183. // the discriminator and the field is done by using the Fields list
  184. // below.
  185. Discriminator *string `yaml:"discriminator,omitempty"`
  186. // DeduceInvalidDiscriminator indicates if the discriminator
  187. // should be updated automatically based on the fields set. This
  188. // typically defaults to false since we don't want to deduce by
  189. // default (the behavior exists to maintain compatibility on
  190. // existing types and shouldn't be used for new types).
  191. DeduceInvalidDiscriminator bool `yaml:"deduceInvalidDiscriminator,omitempty"`
  192. // This is the list of fields that belong to this union. All the
  193. // fields present in here have to be part of the parent
  194. // structure. Discriminator (if oneOf has one), is NOT included in
  195. // this list. The value for field is how we map the name of the field
  196. // to actual value for discriminator.
  197. Fields []UnionField `yaml:"fields,omitempty"`
  198. }
  199. // StructField pairs a field name with a field type.
  200. type StructField struct {
  201. // Name is the field name.
  202. Name string `yaml:"name,omitempty"`
  203. // Type is the field type.
  204. Type TypeRef `yaml:"type,omitempty"`
  205. // Default value for the field, nil if not present.
  206. Default interface{} `yaml:"default,omitempty"`
  207. }
  208. // List represents a type which contains a zero or more elements, all of the
  209. // same subtype. Lists may be either associative: each element is more or less
  210. // independent and could be managed by separate entities in the system; or
  211. // atomic, where the elements are heavily dependent on each other: it is not
  212. // sensible to change one element without considering the ramifications on all
  213. // the other elements.
  214. type List struct {
  215. // ElementType is the type of the list's elements.
  216. ElementType TypeRef `yaml:"elementType,omitempty"`
  217. // ElementRelationship states the relationship between the list's elements
  218. // and must have one of these values:
  219. // * `atomic`: the list is treated as a single entity, like a scalar.
  220. // * `associative`:
  221. // - If the list element is a scalar, the list is treated as a set.
  222. // - If the list element is a map, the list is treated as a map.
  223. // There is no default for this value for lists; all schemas must
  224. // explicitly state the element relationship for all lists.
  225. ElementRelationship ElementRelationship `yaml:"elementRelationship,omitempty"`
  226. // Iff ElementRelationship is `associative`, and the element type is
  227. // map, then Keys must have non-zero length, and it lists the fields
  228. // of the element's map type which are to be used as the keys of the
  229. // list.
  230. //
  231. // TODO: change this to "non-atomic struct" above and make the code reflect this.
  232. //
  233. // Each key must refer to a single field name (no nesting, not JSONPath).
  234. Keys []string `yaml:"keys,omitempty"`
  235. }
  236. // FindNamedType is a convenience function that returns the referenced TypeDef,
  237. // if it exists, or (nil, false) if it doesn't.
  238. func (s *Schema) FindNamedType(name string) (TypeDef, bool) {
  239. s.once.Do(func() {
  240. s.m = make(map[string]TypeDef, len(s.Types))
  241. for _, t := range s.Types {
  242. s.m[t.Name] = t
  243. }
  244. })
  245. t, ok := s.m[name]
  246. return t, ok
  247. }
  248. func (s *Schema) resolveNoOverrides(tr TypeRef) (Atom, bool) {
  249. result := Atom{}
  250. if tr.NamedType != nil {
  251. t, ok := s.FindNamedType(*tr.NamedType)
  252. if !ok {
  253. return Atom{}, false
  254. }
  255. result = t.Atom
  256. } else {
  257. result = tr.Inlined
  258. }
  259. return result, true
  260. }
  261. // Resolve is a convenience function which returns the atom referenced, whether
  262. // it is inline or named. Returns (Atom{}, false) if the type can't be resolved.
  263. //
  264. // This allows callers to not care about the difference between a (possibly
  265. // inlined) reference and a definition.
  266. func (s *Schema) Resolve(tr TypeRef) (Atom, bool) {
  267. // If this is a plain reference with no overrides, just return the type
  268. if tr.ElementRelationship == nil {
  269. return s.resolveNoOverrides(tr)
  270. }
  271. s.lock.Lock()
  272. defer s.lock.Unlock()
  273. if s.resolvedTypes == nil {
  274. s.resolvedTypes = make(map[TypeRef]Atom)
  275. }
  276. var result Atom
  277. var exists bool
  278. // Return cached result if available
  279. // If not, calculate result and cache it
  280. if result, exists = s.resolvedTypes[tr]; !exists {
  281. if result, exists = s.resolveNoOverrides(tr); exists {
  282. // Allow field-level electives to override the referred type's modifiers
  283. switch {
  284. case result.Map != nil:
  285. mapCopy := Map{}
  286. result.Map.CopyInto(&mapCopy)
  287. mapCopy.ElementRelationship = *tr.ElementRelationship
  288. result.Map = &mapCopy
  289. case result.List != nil:
  290. listCopy := *result.List
  291. listCopy.ElementRelationship = *tr.ElementRelationship
  292. result.List = &listCopy
  293. case result.Scalar != nil:
  294. return Atom{}, false
  295. default:
  296. return Atom{}, false
  297. }
  298. } else {
  299. return Atom{}, false
  300. }
  301. // Save result. If it is nil, that is also recorded as not existing.
  302. s.resolvedTypes[tr] = result
  303. }
  304. return result, true
  305. }
  306. // Clones this instance of Schema into the other
  307. // If other is nil this method does nothing.
  308. // If other is already initialized, overwrites it with this instance
  309. // Warning: Not thread safe
  310. func (s *Schema) CopyInto(dst *Schema) {
  311. if dst == nil {
  312. return
  313. }
  314. // Schema type is considered immutable so sharing references
  315. dst.Types = s.Types
  316. if s.m != nil {
  317. // If cache is non-nil then the once token had been consumed.
  318. // Must reset token and use it again to ensure same semantics.
  319. dst.once = sync.Once{}
  320. dst.once.Do(func() {
  321. dst.m = s.m
  322. })
  323. }
  324. }