Skip to content

Commit f77b0bf

Browse files
Feature: Deployment scopes field and filter (#237)
* deployment scopes field Signed-off-by: Michael Valdron <mvaldron@redhat.com> * deployment scopes validation Signed-off-by: Michael Valdron <mvaldron@redhat.com> * stack version deployment scopes Signed-off-by: Michael Valdron <mvaldron@redhat.com> * deployment scope validation test cases Signed-off-by: Michael Valdron <mvaldron@redhat.com> * deployment scopes and filter added to openapi spec Signed-off-by: Michael Valdron <mvaldron@redhat.com> * update index/generator to include deployment scope schema field Signed-off-by: Michael Valdron <mvaldron@redhat.com> * add deployment scopes parameter from openapi spec Signed-off-by: Michael Valdron <mvaldron@redhat.com> * deployment scope filter logic and test cases Signed-off-by: Michael Valdron <mvaldron@redhat.com> * fix naming convention of filter parameter name constants Signed-off-by: Michael Valdron <mvaldron@redhat.com> * add deployment scopes parameter to RESTful API docs Signed-off-by: Michael Valdron <mvaldron@redhat.com> * bump index/generator revision for registry library Signed-off-by: Michael Valdron <mvaldron@redhat.com> * go mod index server Signed-off-by: Michael Valdron <mvaldron@redhat.com> * bump index/generator revision for integration test suite Signed-off-by: Michael Valdron <mvaldron@redhat.com> --------- Signed-off-by: Michael Valdron <mvaldron@redhat.com>
1 parent cca4c9a commit f77b0bf

File tree

22 files changed

+992
-265
lines changed

22 files changed

+992
-265
lines changed

index/generator/library/library.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,35 @@ func (e *IconUrlBrokenError) Error() string {
7575
return fmt.Sprintf("Devfile %s has broken or not existing icon\n", e.devfile)
7676
}
7777

78+
// InvalidDeploymentScopes error type for when deploymentScopes contains any incorrect kinds
79+
type InvalidDeploymentScopes struct {
80+
devfile string
81+
deploymentScopeKind schema.DeploymentScopeKind
82+
}
83+
84+
func (e *InvalidDeploymentScopes) Error() string {
85+
return fmt.Sprintf("Deployment scope %s is incorrect for devfile %s, can only be '%s' or '%s'\n",
86+
e.deploymentScopeKind, e.devfile, schema.InnerloopKind, schema.OuterloopKind)
87+
}
88+
89+
// TooManyDeploymentScopes error type for when there are deploymentScopes set at once
90+
type TooManyDeploymentScopes struct {
91+
devfile string
92+
}
93+
94+
func (e *TooManyDeploymentScopes) Error() string {
95+
deploymentScopeKinds := []any{
96+
schema.InnerloopKind,
97+
schema.OuterloopKind,
98+
}
99+
params := []any{
100+
e.devfile,
101+
}
102+
params = append(params, len(deploymentScopeKinds))
103+
params = append(params, deploymentScopeKinds...)
104+
return fmt.Sprintf("Devfile %s has too many deployment scopes, can only be %s at most, '%s' or '%s'\n", params...)
105+
}
106+
78107
// GenerateIndexStruct parses registry then generates index struct according to the schema
79108
func GenerateIndexStruct(registryDirPath string, force bool) ([]schema.Schema, error) {
80109
// Parse devfile registry then populate index struct
@@ -193,6 +222,25 @@ func validateIndexComponent(indexComponent schema.Schema, componentType schema.D
193222
if len(indexComponent.Architectures) == 0 {
194223
return &MissingArchError{devfile: indexComponent.Name}
195224
}
225+
if len(indexComponent.DeploymentScopes) > 2 {
226+
return &TooManyDeploymentScopes{devfile: indexComponent.Name}
227+
}
228+
for kind := range indexComponent.DeploymentScopes {
229+
if kind != schema.InnerloopKind && kind != schema.OuterloopKind {
230+
return &InvalidDeploymentScopes{devfile: indexComponent.Name, deploymentScopeKind: kind}
231+
}
232+
}
233+
for _, version := range indexComponent.Versions {
234+
if len(version.DeploymentScopes) > 2 {
235+
return &TooManyDeploymentScopes{devfile: indexComponent.Name}
236+
}
237+
238+
for kind := range version.DeploymentScopes {
239+
if kind != schema.InnerloopKind && kind != schema.OuterloopKind {
240+
return &InvalidDeploymentScopes{devfile: indexComponent.Name, deploymentScopeKind: kind}
241+
}
242+
}
243+
}
196244

197245
return nil
198246
}

index/generator/library/library_test.go

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ func TestValidateIndexComponent(t *testing.T) {
4747
schemaVersionEmptyErr := ".*schema version is empty.*"
4848
multipleVersionErr := ".*has multiple default versions.*"
4949
iconUrlBrokenErr := ".*has broken or not existing icon.*"
50+
invalidDeploymentScopeErr := ".*Deployment scope \\S+ is incorrect for devfile \\S+.*"
51+
tooManyDeploymentScopesErr := ".*has too many deployment scopes.*"
5052

5153
tests := []struct {
5254
name string
@@ -530,6 +532,130 @@ func TestValidateIndexComponent(t *testing.T) {
530532
schema.StackDevfileType,
531533
&iconUrlBrokenErr,
532534
},
535+
{
536+
name: "Case 23: deployment scope has invalid kind",
537+
indexComponent: schema.Schema{
538+
Name: "java-maven",
539+
Icon: "https://raw.githubusercontent.com/devfile-samples/devfile-stack-icons/main/java-maven.jpg",
540+
Provider: "Red Hat",
541+
SupportUrl: "https://devfile.io",
542+
Architectures: []string{
543+
"amd64",
544+
},
545+
Versions: []schema.Version{
546+
{
547+
Version: "1.0.0",
548+
SchemaVersion: "2.0.0",
549+
Default: true,
550+
Links: map[string]string{
551+
"self": "devfile-catalog/java-maven:latest",
552+
},
553+
Resources: []string{
554+
"devfile.yaml",
555+
},
556+
},
557+
},
558+
DeploymentScopes: map[schema.DeploymentScopeKind]bool{
559+
"foo": true,
560+
},
561+
},
562+
componentType: schema.StackDevfileType,
563+
wantErr: &invalidDeploymentScopeErr,
564+
},
565+
{
566+
name: "Case 24: deployment scope has invalid kind",
567+
indexComponent: schema.Schema{
568+
Name: "java-maven",
569+
Icon: "https://raw.githubusercontent.com/devfile-samples/devfile-stack-icons/main/java-maven.jpg",
570+
Provider: "Red Hat",
571+
SupportUrl: "https://devfile.io",
572+
Architectures: []string{
573+
"amd64",
574+
},
575+
Versions: []schema.Version{
576+
{
577+
Version: "1.0.0",
578+
SchemaVersion: "2.0.0",
579+
Default: true,
580+
Links: map[string]string{
581+
"self": "devfile-catalog/java-maven:latest",
582+
},
583+
Resources: []string{
584+
"devfile.yaml",
585+
},
586+
},
587+
},
588+
DeploymentScopes: map[schema.DeploymentScopeKind]bool{
589+
schema.InnerloopKind: true,
590+
schema.OuterloopKind: false,
591+
"foo": false,
592+
},
593+
},
594+
componentType: schema.StackDevfileType,
595+
wantErr: &tooManyDeploymentScopesErr,
596+
},
597+
{
598+
name: "Case 25: version deployment scope has invalid kind",
599+
indexComponent: schema.Schema{
600+
Name: "java-maven",
601+
Icon: "https://raw.githubusercontent.com/devfile-samples/devfile-stack-icons/main/java-maven.jpg",
602+
Provider: "Red Hat",
603+
SupportUrl: "https://devfile.io",
604+
Architectures: []string{
605+
"amd64",
606+
},
607+
Versions: []schema.Version{
608+
{
609+
Version: "1.0.0",
610+
SchemaVersion: "2.0.0",
611+
Default: true,
612+
Links: map[string]string{
613+
"self": "devfile-catalog/java-maven:latest",
614+
},
615+
Resources: []string{
616+
"devfile.yaml",
617+
},
618+
DeploymentScopes: map[schema.DeploymentScopeKind]bool{
619+
"foo": true,
620+
},
621+
},
622+
},
623+
},
624+
componentType: schema.StackDevfileType,
625+
wantErr: &invalidDeploymentScopeErr,
626+
},
627+
{
628+
name: "Case 26: version deployment scope has invalid kind",
629+
indexComponent: schema.Schema{
630+
Name: "java-maven",
631+
Icon: "https://raw.githubusercontent.com/devfile-samples/devfile-stack-icons/main/java-maven.jpg",
632+
Provider: "Red Hat",
633+
SupportUrl: "https://devfile.io",
634+
Architectures: []string{
635+
"amd64",
636+
},
637+
Versions: []schema.Version{
638+
{
639+
Version: "1.0.0",
640+
SchemaVersion: "2.0.0",
641+
Default: true,
642+
Links: map[string]string{
643+
"self": "devfile-catalog/java-maven:latest",
644+
},
645+
Resources: []string{
646+
"devfile.yaml",
647+
},
648+
DeploymentScopes: map[schema.DeploymentScopeKind]bool{
649+
schema.InnerloopKind: true,
650+
schema.OuterloopKind: false,
651+
"foo": false,
652+
},
653+
},
654+
},
655+
},
656+
componentType: schema.StackDevfileType,
657+
wantErr: &tooManyDeploymentScopesErr,
658+
},
533659
}
534660

535661
for _, tt := range tests {

index/generator/schema/schema.go

Lines changed: 43 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ projectType: string - The project framework that is used in the devfile
130130
language: string - The project language that is used in the devfile
131131
links: map[string]string - Links related to the devfile
132132
commandGroups: map[CommandGroupKind]bool - The command groups that are used in the devfile
133+
deploymentScopes: map[DeploymentScopeKind]bool - The deployment scope that are detected in the devfile
133134
resources: []string - The file resources that compose a devfile stack.
134135
starterProjects: string[] - The project templates that can be used in the devfile
135136
git: *git - The information of remote repositories
@@ -139,26 +140,27 @@ versions: []Version - The list of stack versions information
139140

140141
// Schema is the index file schema
141142
type Schema struct {
142-
Name string `yaml:"name,omitempty" json:"name,omitempty"`
143-
Version string `yaml:"version,omitempty" json:"version,omitempty"`
144-
Attributes map[string]apiext.JSON `yaml:"attributes,omitempty" json:"attributes,omitempty"`
145-
DisplayName string `yaml:"displayName,omitempty" json:"displayName,omitempty"`
146-
Description string `yaml:"description,omitempty" json:"description,omitempty"`
147-
Type DevfileType `yaml:"type,omitempty" json:"type,omitempty"`
148-
Tags []string `yaml:"tags,omitempty" json:"tags,omitempty"`
149-
Architectures []string `yaml:"architectures,omitempty" json:"architectures,omitempty"`
150-
Icon string `yaml:"icon,omitempty" json:"icon,omitempty"`
151-
GlobalMemoryLimit string `yaml:"globalMemoryLimit,omitempty" json:"globalMemoryLimit,omitempty"`
152-
ProjectType string `yaml:"projectType,omitempty" json:"projectType,omitempty"`
153-
Language string `yaml:"language,omitempty" json:"language,omitempty"`
154-
Links map[string]string `yaml:"links,omitempty" json:"links,omitempty"`
155-
CommandGroups map[CommandGroupKind]bool `yaml:"commandGroups,omitempty" json:"commandGroups,omitempty"`
156-
Resources []string `yaml:"resources,omitempty" json:"resources,omitempty"`
157-
StarterProjects []string `yaml:"starterProjects,omitempty" json:"starterProjects,omitempty"`
158-
Git *Git `yaml:"git,omitempty" json:"git,omitempty"`
159-
Provider string `yaml:"provider,omitempty" json:"provider,omitempty"`
160-
SupportUrl string `yaml:"supportUrl,omitempty" json:"supportUrl,omitempty"`
161-
Versions []Version `yaml:"versions,omitempty" json:"versions,omitempty"`
143+
Name string `yaml:"name,omitempty" json:"name,omitempty"`
144+
Version string `yaml:"version,omitempty" json:"version,omitempty"`
145+
Attributes map[string]apiext.JSON `yaml:"attributes,omitempty" json:"attributes,omitempty"`
146+
DisplayName string `yaml:"displayName,omitempty" json:"displayName,omitempty"`
147+
Description string `yaml:"description,omitempty" json:"description,omitempty"`
148+
Type DevfileType `yaml:"type,omitempty" json:"type,omitempty"`
149+
Tags []string `yaml:"tags,omitempty" json:"tags,omitempty"`
150+
Architectures []string `yaml:"architectures,omitempty" json:"architectures,omitempty"`
151+
Icon string `yaml:"icon,omitempty" json:"icon,omitempty"`
152+
GlobalMemoryLimit string `yaml:"globalMemoryLimit,omitempty" json:"globalMemoryLimit,omitempty"`
153+
ProjectType string `yaml:"projectType,omitempty" json:"projectType,omitempty"`
154+
Language string `yaml:"language,omitempty" json:"language,omitempty"`
155+
Links map[string]string `yaml:"links,omitempty" json:"links,omitempty"`
156+
CommandGroups map[CommandGroupKind]bool `yaml:"commandGroups,omitempty" json:"commandGroups,omitempty"`
157+
DeploymentScopes map[DeploymentScopeKind]bool `yaml:"deploymentScopes,omitempty" json:"deploymentScopes,omitempty"`
158+
Resources []string `yaml:"resources,omitempty" json:"resources,omitempty"`
159+
StarterProjects []string `yaml:"starterProjects,omitempty" json:"starterProjects,omitempty"`
160+
Git *Git `yaml:"git,omitempty" json:"git,omitempty"`
161+
Provider string `yaml:"provider,omitempty" json:"provider,omitempty"`
162+
SupportUrl string `yaml:"supportUrl,omitempty" json:"supportUrl,omitempty"`
163+
Versions []Version `yaml:"versions,omitempty" json:"versions,omitempty"`
162164
}
163165

164166
// DevfileType describes the type of devfile
@@ -183,6 +185,14 @@ const (
183185
DeployCommandGroupKind CommandGroupKind = "deploy"
184186
)
185187

188+
// DeploymentScopeKind describes the kind of deployment scope
189+
type DeploymentScopeKind string
190+
191+
const (
192+
InnerloopKind DeploymentScopeKind = "innerloop"
193+
OuterloopKind DeploymentScopeKind = "outerloop"
194+
)
195+
186196
// StarterProject is the devfile starter project
187197
type StarterProject struct {
188198
Name string `yaml:"name,omitempty" json:"name,omitempty"`
@@ -241,16 +251,17 @@ type StackInfo struct {
241251

242252
// Version stores the information for each stack version
243253
type Version struct {
244-
Version string `yaml:"version,omitempty" json:"version,omitempty"`
245-
SchemaVersion string `yaml:"schemaVersion,omitempty" json:"schemaVersion,omitempty"`
246-
Default bool `yaml:"default,omitempty" json:"default,omitempty"`
247-
Git *Git `yaml:"git,omitempty" json:"git,omitempty"`
248-
Description string `yaml:"description,omitempty" json:"description,omitempty"`
249-
Tags []string `yaml:"tags,omitempty" json:"tags,omitempty"`
250-
Architectures []string `yaml:"architectures,omitempty" json:"architectures,omitempty"`
251-
Icon string `yaml:"icon,omitempty" json:"icon,omitempty"`
252-
Links map[string]string `yaml:"links,omitempty" json:"links,omitempty"`
253-
CommandGroups map[CommandGroupKind]bool `yaml:"commandGroups,omitempty" json:"commandGroups,omitempty"`
254-
Resources []string `yaml:"resources,omitempty" json:"resources,omitempty"`
255-
StarterProjects []string `yaml:"starterProjects,omitempty" json:"starterProjects,omitempty"`
254+
Version string `yaml:"version,omitempty" json:"version,omitempty"`
255+
SchemaVersion string `yaml:"schemaVersion,omitempty" json:"schemaVersion,omitempty"`
256+
Default bool `yaml:"default,omitempty" json:"default,omitempty"`
257+
Git *Git `yaml:"git,omitempty" json:"git,omitempty"`
258+
Description string `yaml:"description,omitempty" json:"description,omitempty"`
259+
Tags []string `yaml:"tags,omitempty" json:"tags,omitempty"`
260+
Architectures []string `yaml:"architectures,omitempty" json:"architectures,omitempty"`
261+
Icon string `yaml:"icon,omitempty" json:"icon,omitempty"`
262+
Links map[string]string `yaml:"links,omitempty" json:"links,omitempty"`
263+
CommandGroups map[CommandGroupKind]bool `yaml:"commandGroups,omitempty" json:"commandGroups,omitempty"`
264+
DeploymentScopes map[DeploymentScopeKind]bool `yaml:"deploymentScopes,omitempty" json:"deploymentScopes,omitempty"`
265+
Resources []string `yaml:"resources,omitempty" json:"resources,omitempty"`
266+
StarterProjects []string `yaml:"starterProjects,omitempty" json:"starterProjects,omitempty"`
256267
}

index/server/go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ require (
127127
go.opentelemetry.io/otel v1.24.0 // indirect
128128
go.opentelemetry.io/otel/metric v1.24.0 // indirect
129129
go.opentelemetry.io/otel/trace v1.24.0 // indirect
130+
go.uber.org/atomic v1.9.0 // indirect
130131
golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect
131132
golang.org/x/crypto v0.21.0 // indirect
132133
golang.org/x/mod v0.16.0 // indirect

index/server/go.sum

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -540,7 +540,8 @@ go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGX
540540
go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco=
541541
go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI=
542542
go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
543-
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
543+
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
544+
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
544545
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
545546
go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60=
546547
golang.org/x/arch v0.0.0-20210923205945-b76863e36670 h1:18EFjUmQOcUvxNYSkA6jO9VAiXCnxFY6NyDX0bHDmkU=

index/server/openapi.yaml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,7 @@ paths:
268268
- $ref: '#/components/parameters/linkNamesParam'
269269
- $ref: '#/components/parameters/linksParam'
270270
- $ref: '#/components/parameters/commandGroupsParam'
271+
- $ref: '#/components/parameters/deploymentScopesParam'
271272
- $ref: '#/components/parameters/gitRemoteNamesParam'
272273
- $ref: '#/components/parameters/gitRemotesParam'
273274
- $ref: '#/components/parameters/gitUrlParam'
@@ -339,6 +340,7 @@ paths:
339340
- $ref: '#/components/parameters/linkNamesParam'
340341
- $ref: '#/components/parameters/linksParam'
341342
- $ref: '#/components/parameters/commandGroupsParam'
343+
- $ref: '#/components/parameters/deploymentScopesParam'
342344
- $ref: '#/components/parameters/gitRemoteNamesParam'
343345
- $ref: '#/components/parameters/gitRemotesParam'
344346
- $ref: '#/components/parameters/gitUrlParam'
@@ -860,6 +862,8 @@ components:
860862
$ref: '#/components/schemas/Links'
861863
commandGroups:
862864
$ref: '#/components/schemas/CommandGroups'
865+
deploymentScopes:
866+
$ref: '#/components/schemas/DeploymentScopes'
863867
gitRemoteNames:
864868
$ref: '#/components/schemas/GitRemoteNames'
865869
gitRemotes:
@@ -979,6 +983,15 @@ components:
979983
- test
980984
- debug
981985
- deploy
986+
DeploymentScopes:
987+
description: List of deployment scopes that are detected in the devfile
988+
type: array
989+
uniqueItems: true
990+
items:
991+
type: string
992+
enum:
993+
- innerloop
994+
- outerloop
982995
GitRemoteName:
983996
description: Git repository remote name
984997
type: string
@@ -1167,6 +1180,15 @@ components:
11671180
groups
11681181
schema:
11691182
$ref: '#/components/schemas/CommandGroups'
1183+
deploymentScopesParam:
1184+
name: deploymentScopes
1185+
in: query
1186+
required: false
1187+
description: |-
1188+
Collection of search strings to filter stacks by their present deployment
1189+
scopes
1190+
schema:
1191+
$ref: '#/components/schemas/DeploymentScopes'
11701192
gitRemoteNamesParam:
11711193
name: gitRemoteNames
11721194
in: query

0 commit comments

Comments
 (0)