1/* 2Copyright 2015 The Kubernetes Authors. 3 4Licensed under the Apache License, Version 2.0 (the "License"); 5you may not use this file except in compliance with the License. 6You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10Unless required by applicable law or agreed to in writing, software 11distributed under the License is distributed on an "AS IS" BASIS, 12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13See the License for the specific language governing permissions and 14limitations under the License. 15*/ 16 17package fake 18 19import ( 20 "io" 21 "path/filepath" 22 "strings" 23 24 "k8s.io/gengo/generator" 25 "k8s.io/gengo/namer" 26 "k8s.io/gengo/types" 27 28 "k8s.io/code-generator/cmd/client-gen/generators/util" 29 "k8s.io/code-generator/cmd/client-gen/path" 30) 31 32// genFakeForType produces a file for each top-level type. 33type genFakeForType struct { 34 generator.DefaultGen 35 outputPackage string 36 group string 37 version string 38 groupGoName string 39 inputPackage string 40 typeToMatch *types.Type 41 imports namer.ImportTracker 42} 43 44var _ generator.Generator = &genFakeForType{} 45 46// Filter ignores all but one type because we're making a single file per type. 47func (g *genFakeForType) Filter(c *generator.Context, t *types.Type) bool { return t == g.typeToMatch } 48 49func (g *genFakeForType) Namers(c *generator.Context) namer.NameSystems { 50 return namer.NameSystems{ 51 "raw": namer.NewRawNamer(g.outputPackage, g.imports), 52 } 53} 54 55func (g *genFakeForType) Imports(c *generator.Context) (imports []string) { 56 return g.imports.ImportLines() 57} 58 59// Ideally, we'd like genStatus to return true if there is a subresource path 60// registered for "status" in the API server, but we do not have that 61// information, so genStatus returns true if the type has a status field. 62func genStatus(t *types.Type) bool { 63 // Default to true if we have a Status member 64 hasStatus := false 65 for _, m := range t.Members { 66 if m.Name == "Status" { 67 hasStatus = true 68 break 69 } 70 } 71 72 tags := util.MustParseClientGenTags(append(t.SecondClosestCommentLines, t.CommentLines...)) 73 return hasStatus && !tags.NoStatus 74} 75 76// hasObjectMeta returns true if the type has a ObjectMeta field. 77func hasObjectMeta(t *types.Type) bool { 78 for _, m := range t.Members { 79 if m.Embedded == true && m.Name == "ObjectMeta" { 80 return true 81 } 82 } 83 return false 84} 85 86// GenerateType makes the body of a file implementing the individual typed client for type t. 87func (g *genFakeForType) GenerateType(c *generator.Context, t *types.Type, w io.Writer) error { 88 sw := generator.NewSnippetWriter(w, c, "$", "$") 89 pkg := filepath.Base(t.Name.Package) 90 tags, err := util.ParseClientGenTags(append(t.SecondClosestCommentLines, t.CommentLines...)) 91 if err != nil { 92 return err 93 } 94 canonicalGroup := g.group 95 if canonicalGroup == "core" { 96 canonicalGroup = "" 97 } 98 99 groupName := g.group 100 if g.group == "core" { 101 groupName = "" 102 } 103 104 // allow user to define a group name that's different from the one parsed from the directory. 105 p := c.Universe.Package(path.Vendorless(g.inputPackage)) 106 if override := types.ExtractCommentTags("+", p.Comments)["groupName"]; override != nil { 107 groupName = override[0] 108 } 109 110 const pkgClientGoTesting = "k8s.io/client-go/testing" 111 m := map[string]interface{}{ 112 "type": t, 113 "inputType": t, 114 "resultType": t, 115 "subresourcePath": "", 116 "package": pkg, 117 "Package": namer.IC(pkg), 118 "namespaced": !tags.NonNamespaced, 119 "Group": namer.IC(g.group), 120 "GroupGoName": g.groupGoName, 121 "Version": namer.IC(g.version), 122 "group": canonicalGroup, 123 "groupName": groupName, 124 "version": g.version, 125 "DeleteOptions": c.Universe.Type(types.Name{Package: "k8s.io/apimachinery/pkg/apis/meta/v1", Name: "DeleteOptions"}), 126 "ListOptions": c.Universe.Type(types.Name{Package: "k8s.io/apimachinery/pkg/apis/meta/v1", Name: "ListOptions"}), 127 "GetOptions": c.Universe.Type(types.Name{Package: "k8s.io/apimachinery/pkg/apis/meta/v1", Name: "GetOptions"}), 128 "Everything": c.Universe.Function(types.Name{Package: "k8s.io/apimachinery/pkg/labels", Name: "Everything"}), 129 "GroupVersionResource": c.Universe.Type(types.Name{Package: "k8s.io/apimachinery/pkg/runtime/schema", Name: "GroupVersionResource"}), 130 "GroupVersionKind": c.Universe.Type(types.Name{Package: "k8s.io/apimachinery/pkg/runtime/schema", Name: "GroupVersionKind"}), 131 "PatchType": c.Universe.Type(types.Name{Package: "k8s.io/apimachinery/pkg/types", Name: "PatchType"}), 132 "watchInterface": c.Universe.Type(types.Name{Package: "k8s.io/apimachinery/pkg/watch", Name: "Interface"}), 133 134 "NewRootListAction": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewRootListAction"}), 135 "NewListAction": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewListAction"}), 136 "NewRootGetAction": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewRootGetAction"}), 137 "NewGetAction": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewGetAction"}), 138 "NewRootDeleteAction": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewRootDeleteAction"}), 139 "NewDeleteAction": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewDeleteAction"}), 140 "NewRootDeleteCollectionAction": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewRootDeleteCollectionAction"}), 141 "NewDeleteCollectionAction": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewDeleteCollectionAction"}), 142 "NewRootUpdateAction": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewRootUpdateAction"}), 143 "NewUpdateAction": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewUpdateAction"}), 144 "NewRootCreateAction": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewRootCreateAction"}), 145 "NewCreateAction": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewCreateAction"}), 146 "NewRootWatchAction": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewRootWatchAction"}), 147 "NewWatchAction": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewWatchAction"}), 148 "NewCreateSubresourceAction": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewCreateSubresourceAction"}), 149 "NewRootCreateSubresourceAction": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewRootCreateSubresourceAction"}), 150 "NewUpdateSubresourceAction": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewUpdateSubresourceAction"}), 151 "NewGetSubresourceAction": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewGetSubresourceAction"}), 152 "NewRootGetSubresourceAction": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewRootGetSubresourceAction"}), 153 "NewRootUpdateSubresourceAction": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewRootUpdateSubresourceAction"}), 154 "NewRootPatchAction": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewRootPatchAction"}), 155 "NewPatchAction": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewPatchAction"}), 156 "NewRootPatchSubresourceAction": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewRootPatchSubresourceAction"}), 157 "NewPatchSubresourceAction": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "NewPatchSubresourceAction"}), 158 "ExtractFromListOptions": c.Universe.Function(types.Name{Package: pkgClientGoTesting, Name: "ExtractFromListOptions"}), 159 } 160 161 if tags.NonNamespaced { 162 sw.Do(structNonNamespaced, m) 163 } else { 164 sw.Do(structNamespaced, m) 165 } 166 167 if tags.NoVerbs { 168 return sw.Error() 169 } 170 sw.Do(resource, m) 171 sw.Do(kind, m) 172 173 if tags.HasVerb("get") { 174 sw.Do(getTemplate, m) 175 } 176 if tags.HasVerb("list") { 177 if hasObjectMeta(t) { 178 sw.Do(listUsingOptionsTemplate, m) 179 } else { 180 sw.Do(listTemplate, m) 181 } 182 } 183 if tags.HasVerb("watch") { 184 sw.Do(watchTemplate, m) 185 } 186 187 if tags.HasVerb("create") { 188 sw.Do(createTemplate, m) 189 } 190 if tags.HasVerb("update") { 191 sw.Do(updateTemplate, m) 192 } 193 if tags.HasVerb("updateStatus") && genStatus(t) { 194 sw.Do(updateStatusTemplate, m) 195 } 196 if tags.HasVerb("delete") { 197 sw.Do(deleteTemplate, m) 198 } 199 if tags.HasVerb("deleteCollection") { 200 sw.Do(deleteCollectionTemplate, m) 201 } 202 if tags.HasVerb("patch") { 203 sw.Do(patchTemplate, m) 204 } 205 206 // generate extended client methods 207 for _, e := range tags.Extensions { 208 inputType := *t 209 resultType := *t 210 if len(e.InputTypeOverride) > 0 { 211 if name, pkg := e.Input(); len(pkg) > 0 { 212 newType := c.Universe.Type(types.Name{Package: pkg, Name: name}) 213 inputType = *newType 214 } else { 215 inputType.Name.Name = e.InputTypeOverride 216 } 217 } 218 if len(e.ResultTypeOverride) > 0 { 219 if name, pkg := e.Result(); len(pkg) > 0 { 220 newType := c.Universe.Type(types.Name{Package: pkg, Name: name}) 221 resultType = *newType 222 } else { 223 resultType.Name.Name = e.ResultTypeOverride 224 } 225 } 226 m["inputType"] = &inputType 227 m["resultType"] = &resultType 228 m["subresourcePath"] = e.SubResourcePath 229 230 if e.HasVerb("get") { 231 if e.IsSubresource() { 232 sw.Do(adjustTemplate(e.VerbName, e.VerbType, getSubresourceTemplate), m) 233 } else { 234 sw.Do(adjustTemplate(e.VerbName, e.VerbType, getTemplate), m) 235 } 236 } 237 238 if e.HasVerb("list") { 239 240 sw.Do(adjustTemplate(e.VerbName, e.VerbType, listTemplate), m) 241 } 242 243 // TODO: Figure out schemantic for watching a sub-resource. 244 if e.HasVerb("watch") { 245 sw.Do(adjustTemplate(e.VerbName, e.VerbType, watchTemplate), m) 246 } 247 248 if e.HasVerb("create") { 249 if e.IsSubresource() { 250 sw.Do(adjustTemplate(e.VerbName, e.VerbType, createSubresourceTemplate), m) 251 } else { 252 sw.Do(adjustTemplate(e.VerbName, e.VerbType, createTemplate), m) 253 } 254 } 255 256 if e.HasVerb("update") { 257 if e.IsSubresource() { 258 sw.Do(adjustTemplate(e.VerbName, e.VerbType, updateSubresourceTemplate), m) 259 } else { 260 sw.Do(adjustTemplate(e.VerbName, e.VerbType, updateTemplate), m) 261 } 262 } 263 264 // TODO: Figure out schemantic for deleting a sub-resource (what arguments 265 // are passed, does it need two names? etc. 266 if e.HasVerb("delete") { 267 sw.Do(adjustTemplate(e.VerbName, e.VerbType, deleteTemplate), m) 268 } 269 270 if e.HasVerb("patch") { 271 sw.Do(adjustTemplate(e.VerbName, e.VerbType, patchTemplate), m) 272 } 273 } 274 275 return sw.Error() 276} 277 278// adjustTemplate adjust the origin verb template using the expansion name. 279// TODO: Make the verbs in templates parametrized so the strings.Replace() is 280// not needed. 281func adjustTemplate(name, verbType, template string) string { 282 return strings.Replace(template, " "+strings.Title(verbType), " "+name, -1) 283} 284 285// template for the struct that implements the type's interface 286var structNamespaced = ` 287// Fake$.type|publicPlural$ implements $.type|public$Interface 288type Fake$.type|publicPlural$ struct { 289 Fake *Fake$.GroupGoName$$.Version$ 290 ns string 291} 292` 293 294// template for the struct that implements the type's interface 295var structNonNamespaced = ` 296// Fake$.type|publicPlural$ implements $.type|public$Interface 297type Fake$.type|publicPlural$ struct { 298 Fake *Fake$.GroupGoName$$.Version$ 299} 300` 301 302var resource = ` 303var $.type|allLowercasePlural$Resource = $.GroupVersionResource|raw${Group: "$.groupName$", Version: "$.version$", Resource: "$.type|resource$"} 304` 305 306var kind = ` 307var $.type|allLowercasePlural$Kind = $.GroupVersionKind|raw${Group: "$.groupName$", Version: "$.version$", Kind: "$.type|singularKind$"} 308` 309 310var listTemplate = ` 311// List takes label and field selectors, and returns the list of $.type|publicPlural$ that match those selectors. 312func (c *Fake$.type|publicPlural$) List(opts $.ListOptions|raw$) (result *$.type|raw$List, err error) { 313 obj, err := c.Fake. 314 $if .namespaced$Invokes($.NewListAction|raw$($.type|allLowercasePlural$Resource, $.type|allLowercasePlural$Kind, c.ns, opts), &$.type|raw$List{}) 315 $else$Invokes($.NewRootListAction|raw$($.type|allLowercasePlural$Resource, $.type|allLowercasePlural$Kind, opts), &$.type|raw$List{})$end$ 316 if obj == nil { 317 return nil, err 318 } 319 return obj.(*$.type|raw$List), err 320} 321` 322 323var listUsingOptionsTemplate = ` 324// List takes label and field selectors, and returns the list of $.type|publicPlural$ that match those selectors. 325func (c *Fake$.type|publicPlural$) List(opts $.ListOptions|raw$) (result *$.type|raw$List, err error) { 326 obj, err := c.Fake. 327 $if .namespaced$Invokes($.NewListAction|raw$($.type|allLowercasePlural$Resource, $.type|allLowercasePlural$Kind, c.ns, opts), &$.type|raw$List{}) 328 $else$Invokes($.NewRootListAction|raw$($.type|allLowercasePlural$Resource, $.type|allLowercasePlural$Kind, opts), &$.type|raw$List{})$end$ 329 if obj == nil { 330 return nil, err 331 } 332 333 label, _, _ := $.ExtractFromListOptions|raw$(opts) 334 if label == nil { 335 label = $.Everything|raw$() 336 } 337 list := &$.type|raw$List{ListMeta: obj.(*$.type|raw$List).ListMeta} 338 for _, item := range obj.(*$.type|raw$List).Items { 339 if label.Matches(labels.Set(item.Labels)) { 340 list.Items = append(list.Items, item) 341 } 342 } 343 return list, err 344} 345` 346 347var getTemplate = ` 348// Get takes name of the $.type|private$, and returns the corresponding $.resultType|private$ object, and an error if there is any. 349func (c *Fake$.type|publicPlural$) Get(name string, options $.GetOptions|raw$) (result *$.resultType|raw$, err error) { 350 obj, err := c.Fake. 351 $if .namespaced$Invokes($.NewGetAction|raw$($.type|allLowercasePlural$Resource, c.ns, name), &$.resultType|raw${}) 352 $else$Invokes($.NewRootGetAction|raw$($.type|allLowercasePlural$Resource, name), &$.resultType|raw${})$end$ 353 if obj == nil { 354 return nil, err 355 } 356 return obj.(*$.resultType|raw$), err 357} 358` 359 360var getSubresourceTemplate = ` 361// Get takes name of the $.type|private$, and returns the corresponding $.resultType|private$ object, and an error if there is any. 362func (c *Fake$.type|publicPlural$) Get($.type|private$Name string, options $.GetOptions|raw$) (result *$.resultType|raw$, err error) { 363 obj, err := c.Fake. 364 $if .namespaced$Invokes($.NewGetSubresourceAction|raw$($.type|allLowercasePlural$Resource, c.ns, "$.subresourcePath$", $.type|private$Name), &$.resultType|raw${}) 365 $else$Invokes($.NewRootGetSubresourceAction|raw$($.type|allLowercasePlural$Resource, "$.subresourcePath$", $.type|private$Name), &$.resultType|raw${})$end$ 366 if obj == nil { 367 return nil, err 368 } 369 return obj.(*$.resultType|raw$), err 370} 371` 372 373var deleteTemplate = ` 374// Delete takes name of the $.type|private$ and deletes it. Returns an error if one occurs. 375func (c *Fake$.type|publicPlural$) Delete(name string, options *$.DeleteOptions|raw$) error { 376 _, err := c.Fake. 377 $if .namespaced$Invokes($.NewDeleteAction|raw$($.type|allLowercasePlural$Resource, c.ns, name), &$.type|raw${}) 378 $else$Invokes($.NewRootDeleteAction|raw$($.type|allLowercasePlural$Resource, name), &$.type|raw${})$end$ 379 return err 380} 381` 382 383var deleteCollectionTemplate = ` 384// DeleteCollection deletes a collection of objects. 385func (c *Fake$.type|publicPlural$) DeleteCollection(options *$.DeleteOptions|raw$, listOptions $.ListOptions|raw$) error { 386 $if .namespaced$action := $.NewDeleteCollectionAction|raw$($.type|allLowercasePlural$Resource, c.ns, listOptions) 387 $else$action := $.NewRootDeleteCollectionAction|raw$($.type|allLowercasePlural$Resource, listOptions) 388 $end$ 389 _, err := c.Fake.Invokes(action, &$.type|raw$List{}) 390 return err 391} 392` 393var createTemplate = ` 394// Create takes the representation of a $.inputType|private$ and creates it. Returns the server's representation of the $.resultType|private$, and an error, if there is any. 395func (c *Fake$.type|publicPlural$) Create($.inputType|private$ *$.inputType|raw$) (result *$.resultType|raw$, err error) { 396 obj, err := c.Fake. 397 $if .namespaced$Invokes($.NewCreateAction|raw$($.inputType|allLowercasePlural$Resource, c.ns, $.inputType|private$), &$.resultType|raw${}) 398 $else$Invokes($.NewRootCreateAction|raw$($.inputType|allLowercasePlural$Resource, $.inputType|private$), &$.resultType|raw${})$end$ 399 if obj == nil { 400 return nil, err 401 } 402 return obj.(*$.resultType|raw$), err 403} 404` 405 406var createSubresourceTemplate = ` 407// Create takes the representation of a $.inputType|private$ and creates it. Returns the server's representation of the $.resultType|private$, and an error, if there is any. 408func (c *Fake$.type|publicPlural$) Create($.type|private$Name string, $.inputType|private$ *$.inputType|raw$) (result *$.resultType|raw$, err error) { 409 obj, err := c.Fake. 410 $if .namespaced$Invokes($.NewCreateSubresourceAction|raw$($.type|allLowercasePlural$Resource, $.type|private$Name, "$.subresourcePath$", c.ns, $.inputType|private$), &$.resultType|raw${}) 411 $else$Invokes($.NewRootCreateSubresourceAction|raw$($.type|allLowercasePlural$Resource, "$.subresourcePath$", $.inputType|private$), &$.resultType|raw${})$end$ 412 if obj == nil { 413 return nil, err 414 } 415 return obj.(*$.resultType|raw$), err 416} 417` 418 419var updateTemplate = ` 420// Update takes the representation of a $.inputType|private$ and updates it. Returns the server's representation of the $.resultType|private$, and an error, if there is any. 421func (c *Fake$.type|publicPlural$) Update($.inputType|private$ *$.inputType|raw$) (result *$.resultType|raw$, err error) { 422 obj, err := c.Fake. 423 $if .namespaced$Invokes($.NewUpdateAction|raw$($.inputType|allLowercasePlural$Resource, c.ns, $.inputType|private$), &$.resultType|raw${}) 424 $else$Invokes($.NewRootUpdateAction|raw$($.inputType|allLowercasePlural$Resource, $.inputType|private$), &$.resultType|raw${})$end$ 425 if obj == nil { 426 return nil, err 427 } 428 return obj.(*$.resultType|raw$), err 429} 430` 431 432var updateSubresourceTemplate = ` 433// Update takes the representation of a $.inputType|private$ and updates it. Returns the server's representation of the $.resultType|private$, and an error, if there is any. 434func (c *Fake$.type|publicPlural$) Update($.type|private$Name string, $.inputType|private$ *$.inputType|raw$) (result *$.resultType|raw$, err error) { 435 obj, err := c.Fake. 436 $if .namespaced$Invokes($.NewUpdateSubresourceAction|raw$($.type|allLowercasePlural$Resource, "$.subresourcePath$", c.ns, $.inputType|private$), &$.inputType|raw${}) 437 $else$Invokes($.NewRootUpdateSubresourceAction|raw$($.type|allLowercasePlural$Resource, "$.subresourcePath$", $.inputType|private$), &$.resultType|raw${})$end$ 438 if obj == nil { 439 return nil, err 440 } 441 return obj.(*$.resultType|raw$), err 442} 443` 444 445var updateStatusTemplate = ` 446// UpdateStatus was generated because the type contains a Status member. 447// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). 448func (c *Fake$.type|publicPlural$) UpdateStatus($.type|private$ *$.type|raw$) (*$.type|raw$, error) { 449 obj, err := c.Fake. 450 $if .namespaced$Invokes($.NewUpdateSubresourceAction|raw$($.type|allLowercasePlural$Resource, "status", c.ns, $.type|private$), &$.type|raw${}) 451 $else$Invokes($.NewRootUpdateSubresourceAction|raw$($.type|allLowercasePlural$Resource, "status", $.type|private$), &$.type|raw${})$end$ 452 if obj == nil { 453 return nil, err 454 } 455 return obj.(*$.type|raw$), err 456} 457` 458 459var watchTemplate = ` 460// Watch returns a $.watchInterface|raw$ that watches the requested $.type|privatePlural$. 461func (c *Fake$.type|publicPlural$) Watch(opts $.ListOptions|raw$) ($.watchInterface|raw$, error) { 462 return c.Fake. 463 $if .namespaced$InvokesWatch($.NewWatchAction|raw$($.type|allLowercasePlural$Resource, c.ns, opts)) 464 $else$InvokesWatch($.NewRootWatchAction|raw$($.type|allLowercasePlural$Resource, opts))$end$ 465} 466` 467 468var patchTemplate = ` 469// Patch applies the patch and returns the patched $.resultType|private$. 470func (c *Fake$.type|publicPlural$) Patch(name string, pt $.PatchType|raw$, data []byte, subresources ...string) (result *$.resultType|raw$, err error) { 471 obj, err := c.Fake. 472 $if .namespaced$Invokes($.NewPatchSubresourceAction|raw$($.type|allLowercasePlural$Resource, c.ns, name, pt, data, subresources... ), &$.resultType|raw${}) 473 $else$Invokes($.NewRootPatchSubresourceAction|raw$($.type|allLowercasePlural$Resource, name, pt, data, subresources...), &$.resultType|raw${})$end$ 474 if obj == nil { 475 return nil, err 476 } 477 return obj.(*$.resultType|raw$), err 478} 479` 480