Skip to content

Commit 05abe25

Browse files
authored
Merge pull request #2003 from Adirio/track-resources-without-api
🐛 (config/v3) Track in the project configuration file resources without API
2 parents 72f01e5 + 42091e9 commit 05abe25

File tree

29 files changed

+215
-87
lines changed

29 files changed

+215
-87
lines changed

generate_testdata.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ scaffold_test_project() {
9898

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

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

103103
if [ $project == "project-v3-multigroup" ]; then
104104
$kb create api --version v1 --kind Lakers --controller=true --resource=true --make=false

pkg/model/file/funcmap.go

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,18 @@ import (
2626
// DefaultFuncMap returns the default template.FuncMap for rendering the template.
2727
func DefaultFuncMap() template.FuncMap {
2828
return template.FuncMap{
29-
"title": strings.Title,
30-
"lower": strings.ToLower,
31-
"hashFNV": hashFNV,
29+
"title": strings.Title,
30+
"lower": strings.ToLower,
31+
"isEmptyStr": isEmptyString,
32+
"hashFNV": hashFNV,
3233
}
3334
}
3435

36+
// isEmptyString returns whether the string is empty
37+
func isEmptyString(s string) bool {
38+
return s == ""
39+
}
40+
3541
// hashFNV will generate a random string useful for generating a unique string
3642
func hashFNV(s string) (string, error) {
3743
hasher := fnv.New32a()

pkg/model/resource/resource.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -155,8 +155,12 @@ func (r *Resource) Update(other Resource) error {
155155
return fmt.Errorf("unable to update Resource with another with non-matching Plural")
156156
}
157157

158-
if r.Path != other.Path {
159-
return fmt.Errorf("unable to update Resource with another with non-matching Path")
158+
if other.Path != "" && r.Path != other.Path {
159+
if r.Path == "" {
160+
r.Path = other.Path
161+
} else {
162+
return fmt.Errorf("unable to update Resource with another with non-matching Path")
163+
}
160164
}
161165

162166
// Update API.

pkg/plugins/golang/options.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,6 @@ func (opts Options) GVK() resource.GVK {
137137
func (opts Options) NewResource(c newconfig.Config) resource.Resource {
138138
res := resource.Resource{
139139
GVK: opts.GVK(),
140-
Path: resource.APIPackagePath(c.GetRepository(), opts.Group, opts.Version, c.IsMultiGroup()),
141140
Controller: opts.DoController,
142141
}
143142

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

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

161161
if opts.DoDefaulting || opts.DoValidation || opts.DoConversion {
162+
res.Path = resource.APIPackagePath(c.GetRepository(), opts.Group, opts.Version, c.IsMultiGroup())
162163
res.Webhooks = &resource.Webhooks{
163164
WebhookVersion: opts.WebhookVersion,
164165
Defaulting: opts.DoDefaulting,
@@ -177,7 +178,9 @@ func (opts Options) NewResource(c newconfig.Config) resource.Resource {
177178
// - In any other case, default to => project resource
178179
// TODO: need to support '--resource-pkg-path' flag for specifying resourcePath
179180
if !opts.DoAPI {
180-
if !c.HasResource(opts.GVK()) {
181+
loadedRes, err := c.GetResource(opts.GVK())
182+
alreadyHasAPI := err == nil && loadedRes.HasAPI()
183+
if !alreadyHasAPI {
181184
if domain, found := coreGroups[opts.Group]; found {
182185
res.Domain = domain
183186
res.Path = path.Join("k8s.io", "api", opts.Group, opts.Version)

pkg/plugins/golang/options_test.go

Lines changed: 45 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ var _ = Describe("Options", func() {
6262
for _, multiGroup := range []bool{false, true} {
6363
if multiGroup {
6464
Expect(cfg.SetMultiGroup()).To(Succeed())
65+
} else {
66+
Expect(cfg.ClearMultiGroup()).To(Succeed())
6567
}
6668

6769
resource := options.NewResource(cfg)
@@ -70,19 +72,36 @@ var _ = Describe("Options", func() {
7072
Expect(resource.Domain).To(Equal(options.Domain))
7173
Expect(resource.Version).To(Equal(options.Version))
7274
Expect(resource.Kind).To(Equal(options.Kind))
73-
if multiGroup {
74-
Expect(resource.Path).To(Equal(
75-
path.Join(cfg.GetRepository(), "apis", options.Group, options.Version)))
75+
Expect(resource.API).NotTo(BeNil())
76+
if options.DoAPI || options.DoDefaulting || options.DoValidation || options.DoConversion {
77+
if multiGroup {
78+
Expect(resource.Path).To(Equal(
79+
path.Join(cfg.GetRepository(), "apis", options.Group, options.Version)))
80+
} else {
81+
Expect(resource.Path).To(Equal(path.Join(cfg.GetRepository(), "api", options.Version)))
82+
}
7683
} else {
77-
Expect(resource.Path).To(Equal(path.Join(cfg.GetRepository(), "api", options.Version)))
84+
// Core-resources have a path despite not having an API/Webhook but they are not tested here
85+
Expect(resource.Path).To(Equal(""))
86+
}
87+
if options.DoAPI {
88+
Expect(resource.API.CRDVersion).To(Equal(options.CRDVersion))
89+
Expect(resource.API.Namespaced).To(Equal(options.Namespaced))
90+
Expect(resource.API.IsEmpty()).To(BeFalse())
91+
} else {
92+
Expect(resource.API.IsEmpty()).To(BeTrue())
7893
}
79-
Expect(resource.API.CRDVersion).To(Equal(options.CRDVersion))
80-
Expect(resource.API.Namespaced).To(Equal(options.Namespaced))
8194
Expect(resource.Controller).To(Equal(options.DoController))
82-
Expect(resource.Webhooks.WebhookVersion).To(Equal(options.WebhookVersion))
83-
Expect(resource.Webhooks.Defaulting).To(Equal(options.DoDefaulting))
84-
Expect(resource.Webhooks.Validation).To(Equal(options.DoValidation))
85-
Expect(resource.Webhooks.Conversion).To(Equal(options.DoConversion))
95+
Expect(resource.Webhooks).NotTo(BeNil())
96+
if options.DoDefaulting || options.DoValidation || options.DoConversion {
97+
Expect(resource.Webhooks.WebhookVersion).To(Equal(options.WebhookVersion))
98+
Expect(resource.Webhooks.Defaulting).To(Equal(options.DoDefaulting))
99+
Expect(resource.Webhooks.Validation).To(Equal(options.DoValidation))
100+
Expect(resource.Webhooks.Conversion).To(Equal(options.DoConversion))
101+
Expect(resource.Webhooks.IsEmpty()).To(BeFalse())
102+
} else {
103+
Expect(resource.Webhooks.IsEmpty()).To(BeTrue())
104+
}
86105
Expect(resource.QualifiedGroup()).To(Equal(options.Group + "." + options.Domain))
87106
Expect(resource.PackageName()).To(Equal(options.Group))
88107
Expect(resource.ImportAlias()).To(Equal(options.Group + options.Version))
@@ -130,6 +149,8 @@ var _ = Describe("Options", func() {
130149
for _, multiGroup := range []bool{false, true} {
131150
if multiGroup {
132151
Expect(cfg.SetMultiGroup()).To(Succeed())
152+
} else {
153+
Expect(cfg.ClearMultiGroup()).To(Succeed())
133154
}
134155

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

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

174198
for _, multiGroup := range []bool{false, true} {
175199
if multiGroup {
176200
Expect(cfg.SetMultiGroup()).To(Succeed())
201+
} else {
202+
Expect(cfg.ClearMultiGroup()).To(Succeed())
177203
}
178204

179205
resource := options.NewResource(cfg)
@@ -201,6 +227,8 @@ var _ = Describe("Options", func() {
201227
for _, multiGroup := range []bool{false, true} {
202228
if multiGroup {
203229
Expect(cfg.SetMultiGroup()).To(Succeed())
230+
} else {
231+
Expect(cfg.ClearMultiGroup()).To(Succeed())
204232
}
205233

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

227257
resource := options.NewResource(cfg)
228258
Expect(resource.Validate()).To(Succeed())
229259
Expect(resource.Path).To(Equal(path.Join("k8s.io", "api", options.Group, options.Version)))
230-
Expect(resource.API.CRDVersion).To(Equal(""))
260+
Expect(resource.API).NotTo(BeNil())
261+
Expect(resource.API.IsEmpty()).To(BeTrue())
231262
Expect(resource.QualifiedGroup()).To(Equal(qualified))
232263
}
233264
},
@@ -242,12 +273,15 @@ var _ = Describe("Options", func() {
242273
Domain: "test.io",
243274
Version: "v1",
244275
Kind: "FirstMate",
276+
DoAPI: true, // Scaffold the API so that the path is saved
245277
}
246278
Expect(options.Validate()).To(Succeed())
247279

248280
for _, multiGroup := range []bool{false, true} {
249281
if multiGroup {
250282
Expect(cfg.SetMultiGroup()).To(Succeed())
283+
} else {
284+
Expect(cfg.ClearMultiGroup()).To(Succeed())
251285
}
252286

253287
resource := options.NewResource(cfg)

pkg/plugins/golang/v2/api.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -154,9 +154,9 @@ func (p *createAPISubcommand) Validate() error {
154154
}
155155

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

pkg/plugins/golang/v2/options.go

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"strings"
2323

2424
newconfig "sigs.k8s.io/kubebuilder/v3/pkg/config"
25+
cfgv2 "sigs.k8s.io/kubebuilder/v3/pkg/config/v2"
2526
"sigs.k8s.io/kubebuilder/v3/pkg/model/resource"
2627
)
2728

@@ -137,7 +138,6 @@ func (opts Options) GVK() resource.GVK {
137138
func (opts Options) NewResource(c newconfig.Config) resource.Resource {
138139
res := resource.Resource{
139140
GVK: opts.GVK(),
140-
Path: resource.APIPackagePath(c.GetRepository(), opts.Group, opts.Version, c.IsMultiGroup()),
141141
Controller: opts.DoController,
142142
}
143143

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

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

161162
if opts.DoDefaulting || opts.DoValidation || opts.DoConversion {
163+
res.Path = resource.APIPackagePath(c.GetRepository(), opts.Group, opts.Version, c.IsMultiGroup())
162164
res.Webhooks = &resource.Webhooks{
163165
WebhookVersion: opts.WebhookVersion,
164166
Defaulting: opts.DoDefaulting,
@@ -177,7 +179,14 @@ func (opts Options) NewResource(c newconfig.Config) resource.Resource {
177179
// - In any other case, default to => project resource
178180
// TODO: need to support '--resource-pkg-path' flag for specifying resourcePath
179181
if !opts.DoAPI {
180-
if !c.HasResource(opts.GVK()) {
182+
var alreadyHasAPI bool
183+
if c.GetVersion().Compare(cfgv2.Version) == 0 {
184+
alreadyHasAPI = c.HasResource(opts.GVK())
185+
} else {
186+
loadedRes, err := c.GetResource(opts.GVK())
187+
alreadyHasAPI = err == nil && loadedRes.HasAPI()
188+
}
189+
if !alreadyHasAPI {
181190
if domain, found := coreGroups[opts.Group]; found {
182191
res.Domain = domain
183192
res.Path = path.Join("k8s.io", "api", opts.Group, opts.Version)

pkg/plugins/golang/v2/options_test.go

Lines changed: 42 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ var _ = Describe("Options", func() {
6161
for _, multiGroup := range []bool{false, true} {
6262
if multiGroup {
6363
Expect(cfg.SetMultiGroup()).To(Succeed())
64+
} else {
65+
Expect(cfg.ClearMultiGroup()).To(Succeed())
6466
}
6567

6668
resource := options.NewResource(cfg)
@@ -69,19 +71,36 @@ var _ = Describe("Options", func() {
6971
Expect(resource.Domain).To(Equal(options.Domain))
7072
Expect(resource.Version).To(Equal(options.Version))
7173
Expect(resource.Kind).To(Equal(options.Kind))
72-
if multiGroup {
73-
Expect(resource.Path).To(Equal(
74-
path.Join(cfg.GetRepository(), "apis", options.Group, options.Version)))
74+
Expect(resource.API).NotTo(BeNil())
75+
if options.DoAPI || options.DoDefaulting || options.DoValidation || options.DoConversion {
76+
if multiGroup {
77+
Expect(resource.Path).To(Equal(
78+
path.Join(cfg.GetRepository(), "apis", options.Group, options.Version)))
79+
} else {
80+
Expect(resource.Path).To(Equal(path.Join(cfg.GetRepository(), "api", options.Version)))
81+
}
7582
} else {
76-
Expect(resource.Path).To(Equal(path.Join(cfg.GetRepository(), "api", options.Version)))
83+
// Core-resources have a path despite not having an API/Webhook but they are not tested here
84+
Expect(resource.Path).To(Equal(""))
85+
}
86+
if options.DoAPI {
87+
Expect(resource.API.CRDVersion).To(Equal(options.CRDVersion))
88+
Expect(resource.API.Namespaced).To(Equal(options.Namespaced))
89+
Expect(resource.API.IsEmpty()).To(BeFalse())
90+
} else {
91+
Expect(resource.API.IsEmpty()).To(BeTrue())
7792
}
78-
Expect(resource.API.CRDVersion).To(Equal(options.CRDVersion))
79-
Expect(resource.API.Namespaced).To(Equal(options.Namespaced))
8093
Expect(resource.Controller).To(Equal(options.DoController))
81-
Expect(resource.Webhooks.WebhookVersion).To(Equal(options.WebhookVersion))
82-
Expect(resource.Webhooks.Defaulting).To(Equal(options.DoDefaulting))
83-
Expect(resource.Webhooks.Validation).To(Equal(options.DoValidation))
84-
Expect(resource.Webhooks.Conversion).To(Equal(options.DoConversion))
94+
Expect(resource.Webhooks).NotTo(BeNil())
95+
if options.DoDefaulting || options.DoValidation || options.DoConversion {
96+
Expect(resource.Webhooks.WebhookVersion).To(Equal(options.WebhookVersion))
97+
Expect(resource.Webhooks.Defaulting).To(Equal(options.DoDefaulting))
98+
Expect(resource.Webhooks.Validation).To(Equal(options.DoValidation))
99+
Expect(resource.Webhooks.Conversion).To(Equal(options.DoConversion))
100+
Expect(resource.Webhooks.IsEmpty()).To(BeFalse())
101+
} else {
102+
Expect(resource.Webhooks.IsEmpty()).To(BeTrue())
103+
}
85104
Expect(resource.QualifiedGroup()).To(Equal(options.Group + "." + options.Domain))
86105
Expect(resource.PackageName()).To(Equal(options.Group))
87106
Expect(resource.ImportAlias()).To(Equal(options.Group + options.Version))
@@ -129,6 +148,8 @@ var _ = Describe("Options", func() {
129148
for _, multiGroup := range []bool{false, true} {
130149
if multiGroup {
131150
Expect(cfg.SetMultiGroup()).To(Succeed())
151+
} else {
152+
Expect(cfg.ClearMultiGroup()).To(Succeed())
132153
}
133154

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

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

173197
for _, multiGroup := range []bool{false, true} {
174198
if multiGroup {
175199
Expect(cfg.SetMultiGroup()).To(Succeed())
200+
} else {
201+
Expect(cfg.ClearMultiGroup()).To(Succeed())
176202
}
177203

178204
resource := options.NewResource(cfg)
@@ -200,6 +226,8 @@ var _ = Describe("Options", func() {
200226
for _, multiGroup := range []bool{false, true} {
201227
if multiGroup {
202228
Expect(cfg.SetMultiGroup()).To(Succeed())
229+
} else {
230+
Expect(cfg.ClearMultiGroup()).To(Succeed())
203231
}
204232

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

226256
resource := options.NewResource(cfg)
227257
Expect(resource.Validate()).To(Succeed())
228258
Expect(resource.Path).To(Equal(path.Join("k8s.io", "api", options.Group, options.Version)))
229-
Expect(resource.API.CRDVersion).To(Equal(""))
259+
Expect(resource.API).NotTo(BeNil())
260+
Expect(resource.API.IsEmpty()).To(BeTrue())
230261
Expect(resource.QualifiedGroup()).To(Equal(qualified))
231262
}
232263
},

0 commit comments

Comments
 (0)