Skip to content

Commit

Permalink
Track resources without an API in the config file (config/v3)
Browse files Browse the repository at this point in the history
- Remove path from resources that do not know the path
- Add scaffold to controllers if we do know the path instead of if we scaffold an API
- Track the resources in the config file even if we don't scaffold an API

Signed-off-by: Adrian Orive <adrian.orive.oneca@gmail.com>
  • Loading branch information
Adirio committed Feb 11, 2021
1 parent 72f01e5 commit 42091e9
Show file tree
Hide file tree
Showing 29 changed files with 215 additions and 87 deletions.
2 changes: 1 addition & 1 deletion generate_testdata.sh
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ scaffold_test_project() {

$kb create api --group foo.policy --version v1 --kind HealthCheckPolicy --controller=true --resource=true --make=false

$kb create api --group apps --version v1 --kind Pod --controller=true --resource=false --make=false
$kb create api --group apps --version v1 --kind Deployment --controller=true --resource=false --make=false

if [ $project == "project-v3-multigroup" ]; then
$kb create api --version v1 --kind Lakers --controller=true --resource=true --make=false
Expand Down
12 changes: 9 additions & 3 deletions pkg/model/file/funcmap.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,18 @@ import (
// DefaultFuncMap returns the default template.FuncMap for rendering the template.
func DefaultFuncMap() template.FuncMap {
return template.FuncMap{
"title": strings.Title,
"lower": strings.ToLower,
"hashFNV": hashFNV,
"title": strings.Title,
"lower": strings.ToLower,
"isEmptyStr": isEmptyString,
"hashFNV": hashFNV,
}
}

// isEmptyString returns whether the string is empty
func isEmptyString(s string) bool {
return s == ""
}

// hashFNV will generate a random string useful for generating a unique string
func hashFNV(s string) (string, error) {
hasher := fnv.New32a()
Expand Down
8 changes: 6 additions & 2 deletions pkg/model/resource/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,8 +155,12 @@ func (r *Resource) Update(other Resource) error {
return fmt.Errorf("unable to update Resource with another with non-matching Plural")
}

if r.Path != other.Path {
return fmt.Errorf("unable to update Resource with another with non-matching Path")
if other.Path != "" && r.Path != other.Path {
if r.Path == "" {
r.Path = other.Path
} else {
return fmt.Errorf("unable to update Resource with another with non-matching Path")
}
}

// Update API.
Expand Down
7 changes: 5 additions & 2 deletions pkg/plugins/golang/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,6 @@ func (opts Options) GVK() resource.GVK {
func (opts Options) NewResource(c newconfig.Config) resource.Resource {
res := resource.Resource{
GVK: opts.GVK(),
Path: resource.APIPackagePath(c.GetRepository(), opts.Group, opts.Version, c.IsMultiGroup()),
Controller: opts.DoController,
}

Expand All @@ -149,6 +148,7 @@ func (opts Options) NewResource(c newconfig.Config) resource.Resource {
}

if opts.DoAPI {
res.Path = resource.APIPackagePath(c.GetRepository(), opts.Group, opts.Version, c.IsMultiGroup())
res.API = &resource.API{
CRDVersion: opts.CRDVersion,
Namespaced: opts.Namespaced,
Expand All @@ -159,6 +159,7 @@ func (opts Options) NewResource(c newconfig.Config) resource.Resource {
}

if opts.DoDefaulting || opts.DoValidation || opts.DoConversion {
res.Path = resource.APIPackagePath(c.GetRepository(), opts.Group, opts.Version, c.IsMultiGroup())
res.Webhooks = &resource.Webhooks{
WebhookVersion: opts.WebhookVersion,
Defaulting: opts.DoDefaulting,
Expand All @@ -177,7 +178,9 @@ func (opts Options) NewResource(c newconfig.Config) resource.Resource {
// - In any other case, default to => project resource
// TODO: need to support '--resource-pkg-path' flag for specifying resourcePath
if !opts.DoAPI {
if !c.HasResource(opts.GVK()) {
loadedRes, err := c.GetResource(opts.GVK())
alreadyHasAPI := err == nil && loadedRes.HasAPI()
if !alreadyHasAPI {
if domain, found := coreGroups[opts.Group]; found {
res.Domain = domain
res.Path = path.Join("k8s.io", "api", opts.Group, opts.Version)
Expand Down
56 changes: 45 additions & 11 deletions pkg/plugins/golang/options_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ var _ = Describe("Options", func() {
for _, multiGroup := range []bool{false, true} {
if multiGroup {
Expect(cfg.SetMultiGroup()).To(Succeed())
} else {
Expect(cfg.ClearMultiGroup()).To(Succeed())
}

resource := options.NewResource(cfg)
Expand All @@ -70,19 +72,36 @@ var _ = Describe("Options", func() {
Expect(resource.Domain).To(Equal(options.Domain))
Expect(resource.Version).To(Equal(options.Version))
Expect(resource.Kind).To(Equal(options.Kind))
if multiGroup {
Expect(resource.Path).To(Equal(
path.Join(cfg.GetRepository(), "apis", options.Group, options.Version)))
Expect(resource.API).NotTo(BeNil())
if options.DoAPI || options.DoDefaulting || options.DoValidation || options.DoConversion {
if multiGroup {
Expect(resource.Path).To(Equal(
path.Join(cfg.GetRepository(), "apis", options.Group, options.Version)))
} else {
Expect(resource.Path).To(Equal(path.Join(cfg.GetRepository(), "api", options.Version)))
}
} else {
Expect(resource.Path).To(Equal(path.Join(cfg.GetRepository(), "api", options.Version)))
// Core-resources have a path despite not having an API/Webhook but they are not tested here
Expect(resource.Path).To(Equal(""))
}
if options.DoAPI {
Expect(resource.API.CRDVersion).To(Equal(options.CRDVersion))
Expect(resource.API.Namespaced).To(Equal(options.Namespaced))
Expect(resource.API.IsEmpty()).To(BeFalse())
} else {
Expect(resource.API.IsEmpty()).To(BeTrue())
}
Expect(resource.API.CRDVersion).To(Equal(options.CRDVersion))
Expect(resource.API.Namespaced).To(Equal(options.Namespaced))
Expect(resource.Controller).To(Equal(options.DoController))
Expect(resource.Webhooks.WebhookVersion).To(Equal(options.WebhookVersion))
Expect(resource.Webhooks.Defaulting).To(Equal(options.DoDefaulting))
Expect(resource.Webhooks.Validation).To(Equal(options.DoValidation))
Expect(resource.Webhooks.Conversion).To(Equal(options.DoConversion))
Expect(resource.Webhooks).NotTo(BeNil())
if options.DoDefaulting || options.DoValidation || options.DoConversion {
Expect(resource.Webhooks.WebhookVersion).To(Equal(options.WebhookVersion))
Expect(resource.Webhooks.Defaulting).To(Equal(options.DoDefaulting))
Expect(resource.Webhooks.Validation).To(Equal(options.DoValidation))
Expect(resource.Webhooks.Conversion).To(Equal(options.DoConversion))
Expect(resource.Webhooks.IsEmpty()).To(BeFalse())
} else {
Expect(resource.Webhooks.IsEmpty()).To(BeTrue())
}
Expect(resource.QualifiedGroup()).To(Equal(options.Group + "." + options.Domain))
Expect(resource.PackageName()).To(Equal(options.Group))
Expect(resource.ImportAlias()).To(Equal(options.Group + options.Version))
Expand Down Expand Up @@ -130,6 +149,8 @@ var _ = Describe("Options", func() {
for _, multiGroup := range []bool{false, true} {
if multiGroup {
Expect(cfg.SetMultiGroup()).To(Succeed())
} else {
Expect(cfg.ClearMultiGroup()).To(Succeed())
}

resource := options.NewResource(cfg)
Expand All @@ -150,6 +171,8 @@ var _ = Describe("Options", func() {
for _, multiGroup := range []bool{false, true} {
if multiGroup {
Expect(cfg.SetMultiGroup()).To(Succeed())
} else {
Expect(cfg.ClearMultiGroup()).To(Succeed())
}

resource := options.NewResource(cfg)
Expand All @@ -168,12 +191,15 @@ var _ = Describe("Options", func() {
Domain: "test.io",
Version: "v1",
Kind: "FirstMate",
DoAPI: true, // Scaffold the API so that the path is saved
}
Expect(options.Validate()).To(Succeed())

for _, multiGroup := range []bool{false, true} {
if multiGroup {
Expect(cfg.SetMultiGroup()).To(Succeed())
} else {
Expect(cfg.ClearMultiGroup()).To(Succeed())
}

resource := options.NewResource(cfg)
Expand Down Expand Up @@ -201,6 +227,8 @@ var _ = Describe("Options", func() {
for _, multiGroup := range []bool{false, true} {
if multiGroup {
Expect(cfg.SetMultiGroup()).To(Succeed())
} else {
Expect(cfg.ClearMultiGroup()).To(Succeed())
}

resource := options.NewResource(cfg)
Expand All @@ -222,12 +250,15 @@ var _ = Describe("Options", func() {
for _, multiGroup := range []bool{false, true} {
if multiGroup {
Expect(cfg.SetMultiGroup()).To(Succeed())
} else {
Expect(cfg.ClearMultiGroup()).To(Succeed())
}

resource := options.NewResource(cfg)
Expect(resource.Validate()).To(Succeed())
Expect(resource.Path).To(Equal(path.Join("k8s.io", "api", options.Group, options.Version)))
Expect(resource.API.CRDVersion).To(Equal(""))
Expect(resource.API).NotTo(BeNil())
Expect(resource.API.IsEmpty()).To(BeTrue())
Expect(resource.QualifiedGroup()).To(Equal(qualified))
}
},
Expand All @@ -242,12 +273,15 @@ var _ = Describe("Options", func() {
Domain: "test.io",
Version: "v1",
Kind: "FirstMate",
DoAPI: true, // Scaffold the API so that the path is saved
}
Expect(options.Validate()).To(Succeed())

for _, multiGroup := range []bool{false, true} {
if multiGroup {
Expect(cfg.SetMultiGroup()).To(Succeed())
} else {
Expect(cfg.ClearMultiGroup()).To(Succeed())
}

resource := options.NewResource(cfg)
Expand Down
6 changes: 3 additions & 3 deletions pkg/plugins/golang/v2/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,9 +154,9 @@ func (p *createAPISubcommand) Validate() error {
}

// In case we want to scaffold a resource API we need to do some checks
if p.resource.HasAPI() {
// Check that resource doesn't exist or flag force was set
if !p.force && p.config.HasResource(p.resource.GVK) {
if p.options.DoAPI {
// Check that resource doesn't have the API scaffolded or flag force was set
if res, err := p.config.GetResource(p.resource.GVK); err == nil && res.HasAPI() && !p.force {
return errors.New("API resource already exists")
}

Expand Down
13 changes: 11 additions & 2 deletions pkg/plugins/golang/v2/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"strings"

newconfig "sigs.k8s.io/kubebuilder/v3/pkg/config"
cfgv2 "sigs.k8s.io/kubebuilder/v3/pkg/config/v2"
"sigs.k8s.io/kubebuilder/v3/pkg/model/resource"
)

Expand Down Expand Up @@ -137,7 +138,6 @@ func (opts Options) GVK() resource.GVK {
func (opts Options) NewResource(c newconfig.Config) resource.Resource {
res := resource.Resource{
GVK: opts.GVK(),
Path: resource.APIPackagePath(c.GetRepository(), opts.Group, opts.Version, c.IsMultiGroup()),
Controller: opts.DoController,
}

Expand All @@ -149,6 +149,7 @@ func (opts Options) NewResource(c newconfig.Config) resource.Resource {
}

if opts.DoAPI {
res.Path = resource.APIPackagePath(c.GetRepository(), opts.Group, opts.Version, c.IsMultiGroup())
res.API = &resource.API{
CRDVersion: opts.CRDVersion,
Namespaced: opts.Namespaced,
Expand All @@ -159,6 +160,7 @@ func (opts Options) NewResource(c newconfig.Config) resource.Resource {
}

if opts.DoDefaulting || opts.DoValidation || opts.DoConversion {
res.Path = resource.APIPackagePath(c.GetRepository(), opts.Group, opts.Version, c.IsMultiGroup())
res.Webhooks = &resource.Webhooks{
WebhookVersion: opts.WebhookVersion,
Defaulting: opts.DoDefaulting,
Expand All @@ -177,7 +179,14 @@ func (opts Options) NewResource(c newconfig.Config) resource.Resource {
// - In any other case, default to => project resource
// TODO: need to support '--resource-pkg-path' flag for specifying resourcePath
if !opts.DoAPI {
if !c.HasResource(opts.GVK()) {
var alreadyHasAPI bool
if c.GetVersion().Compare(cfgv2.Version) == 0 {
alreadyHasAPI = c.HasResource(opts.GVK())
} else {
loadedRes, err := c.GetResource(opts.GVK())
alreadyHasAPI = err == nil && loadedRes.HasAPI()
}
if !alreadyHasAPI {
if domain, found := coreGroups[opts.Group]; found {
res.Domain = domain
res.Path = path.Join("k8s.io", "api", opts.Group, opts.Version)
Expand Down
53 changes: 42 additions & 11 deletions pkg/plugins/golang/v2/options_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ var _ = Describe("Options", func() {
for _, multiGroup := range []bool{false, true} {
if multiGroup {
Expect(cfg.SetMultiGroup()).To(Succeed())
} else {
Expect(cfg.ClearMultiGroup()).To(Succeed())
}

resource := options.NewResource(cfg)
Expand All @@ -69,19 +71,36 @@ var _ = Describe("Options", func() {
Expect(resource.Domain).To(Equal(options.Domain))
Expect(resource.Version).To(Equal(options.Version))
Expect(resource.Kind).To(Equal(options.Kind))
if multiGroup {
Expect(resource.Path).To(Equal(
path.Join(cfg.GetRepository(), "apis", options.Group, options.Version)))
Expect(resource.API).NotTo(BeNil())
if options.DoAPI || options.DoDefaulting || options.DoValidation || options.DoConversion {
if multiGroup {
Expect(resource.Path).To(Equal(
path.Join(cfg.GetRepository(), "apis", options.Group, options.Version)))
} else {
Expect(resource.Path).To(Equal(path.Join(cfg.GetRepository(), "api", options.Version)))
}
} else {
Expect(resource.Path).To(Equal(path.Join(cfg.GetRepository(), "api", options.Version)))
// Core-resources have a path despite not having an API/Webhook but they are not tested here
Expect(resource.Path).To(Equal(""))
}
if options.DoAPI {
Expect(resource.API.CRDVersion).To(Equal(options.CRDVersion))
Expect(resource.API.Namespaced).To(Equal(options.Namespaced))
Expect(resource.API.IsEmpty()).To(BeFalse())
} else {
Expect(resource.API.IsEmpty()).To(BeTrue())
}
Expect(resource.API.CRDVersion).To(Equal(options.CRDVersion))
Expect(resource.API.Namespaced).To(Equal(options.Namespaced))
Expect(resource.Controller).To(Equal(options.DoController))
Expect(resource.Webhooks.WebhookVersion).To(Equal(options.WebhookVersion))
Expect(resource.Webhooks.Defaulting).To(Equal(options.DoDefaulting))
Expect(resource.Webhooks.Validation).To(Equal(options.DoValidation))
Expect(resource.Webhooks.Conversion).To(Equal(options.DoConversion))
Expect(resource.Webhooks).NotTo(BeNil())
if options.DoDefaulting || options.DoValidation || options.DoConversion {
Expect(resource.Webhooks.WebhookVersion).To(Equal(options.WebhookVersion))
Expect(resource.Webhooks.Defaulting).To(Equal(options.DoDefaulting))
Expect(resource.Webhooks.Validation).To(Equal(options.DoValidation))
Expect(resource.Webhooks.Conversion).To(Equal(options.DoConversion))
Expect(resource.Webhooks.IsEmpty()).To(BeFalse())
} else {
Expect(resource.Webhooks.IsEmpty()).To(BeTrue())
}
Expect(resource.QualifiedGroup()).To(Equal(options.Group + "." + options.Domain))
Expect(resource.PackageName()).To(Equal(options.Group))
Expect(resource.ImportAlias()).To(Equal(options.Group + options.Version))
Expand Down Expand Up @@ -129,6 +148,8 @@ var _ = Describe("Options", func() {
for _, multiGroup := range []bool{false, true} {
if multiGroup {
Expect(cfg.SetMultiGroup()).To(Succeed())
} else {
Expect(cfg.ClearMultiGroup()).To(Succeed())
}

resource := options.NewResource(cfg)
Expand All @@ -149,6 +170,8 @@ var _ = Describe("Options", func() {
for _, multiGroup := range []bool{false, true} {
if multiGroup {
Expect(cfg.SetMultiGroup()).To(Succeed())
} else {
Expect(cfg.ClearMultiGroup()).To(Succeed())
}

resource := options.NewResource(cfg)
Expand All @@ -167,12 +190,15 @@ var _ = Describe("Options", func() {
Domain: "test.io",
Version: "v1",
Kind: "FirstMate",
DoAPI: true, // Scaffold the API so that the path is saved
}
Expect(options.Validate()).To(Succeed())

for _, multiGroup := range []bool{false, true} {
if multiGroup {
Expect(cfg.SetMultiGroup()).To(Succeed())
} else {
Expect(cfg.ClearMultiGroup()).To(Succeed())
}

resource := options.NewResource(cfg)
Expand Down Expand Up @@ -200,6 +226,8 @@ var _ = Describe("Options", func() {
for _, multiGroup := range []bool{false, true} {
if multiGroup {
Expect(cfg.SetMultiGroup()).To(Succeed())
} else {
Expect(cfg.ClearMultiGroup()).To(Succeed())
}

resource := options.NewResource(cfg)
Expand All @@ -221,12 +249,15 @@ var _ = Describe("Options", func() {
for _, multiGroup := range []bool{false, true} {
if multiGroup {
Expect(cfg.SetMultiGroup()).To(Succeed())
} else {
Expect(cfg.ClearMultiGroup()).To(Succeed())
}

resource := options.NewResource(cfg)
Expect(resource.Validate()).To(Succeed())
Expect(resource.Path).To(Equal(path.Join("k8s.io", "api", options.Group, options.Version)))
Expect(resource.API.CRDVersion).To(Equal(""))
Expect(resource.API).NotTo(BeNil())
Expect(resource.API.IsEmpty()).To(BeTrue())
Expect(resource.QualifiedGroup()).To(Equal(qualified))
}
},
Expand Down
Loading

0 comments on commit 42091e9

Please sign in to comment.