Skip to content

Commit 07016d2

Browse files
committed
add support for ximportresources
Signed-off-by: Doug Davis <[email protected]>
1 parent 49499fb commit 07016d2

File tree

4 files changed

+323
-26
lines changed

4 files changed

+323
-26
lines changed

registry/model.go

Lines changed: 83 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1134,7 +1134,14 @@ func (gm *GroupModel) GetImports() map[string]*ResourceModel {
11341134
for _, grName := range gm.XImportResources {
11351135
parts := strings.Split(grName, "/")
11361136
r := gm.Model.FindResourceModel(parts[1], parts[2])
1137-
PanicIf(r == nil, "Can't find %q", grName)
1137+
if r == nil {
1138+
// While this should technically be an error, assume that
1139+
// we'll flag it during the verify() work and not here since
1140+
// some calls to this are just looking to see if something
1141+
// exists in the list and we don't really care about bad
1142+
// refs right then.
1143+
continue
1144+
}
11381145
gm.imports[parts[2]] = r
11391146
}
11401147
}
@@ -1840,55 +1847,112 @@ func (gm *GroupModel) Verify(gmName string) error {
18401847

18411848
// TODO: verify the Groups data are model compliant
18421849

1850+
// Save the plurals/singulars are we go so we can quickly see when
1851+
// dups are specified. We don't use the values, yet... maybe one day
1852+
plurals := map[string]string{} // plural->singular
1853+
singulars := map[string]string{} // singular->plural
1854+
18431855
// Verify the "ximportresources" list, same names for later checking
1844-
resList := map[string]bool{}
18451856
for _, grName := range gm.XImportResources {
1857+
// In this func be careful using the *.Plural value because some
1858+
// may not be filled in yet - like r.Plural. gm.Plural as done above
1859+
18461860
parts := strings.Split(grName, "/")
18471861
if len(parts) != 3 {
1848-
return fmt.Errorf("Group %q has an invalid ximportresources value "+
1849-
"(%s), must be of the form \"/GroupType/ResourceType\"",
1862+
return fmt.Errorf("Group %q has an invalid \"ximportresources\" "+
1863+
"value (%s), must be of the form \"/Group/Resource\"",
18501864
gm.Plural, grName)
18511865
}
18521866
if parts[0] != "" {
1853-
return fmt.Errorf("Group %q has an invalid ximportresources value "+
1854-
"(%s), must start with \"/\" and be of the form "+
1855-
"\"/GroupType/ResourceType\"", gm.Plural, grName)
1867+
return fmt.Errorf("Group %q has an invalid \"ximportresources\" "+
1868+
"value (%s), must start with \"/\" and be of the form "+
1869+
"\"/Group/Resource\"", gm.Plural, grName)
18561870
}
18571871
if parts[1] == gm.Plural {
1858-
return fmt.Errorf("Group %q has a bad ximportresources value "+
1872+
return fmt.Errorf("Group %q has a bad \"ximportresources\" value "+
18591873
"(%s), it can't reference itself", gm.Plural, grName)
18601874
}
18611875

18621876
g := gm.Model.FindGroupModel(parts[1])
18631877
if g == nil {
1864-
return fmt.Errorf("Group %q references a non-existing Group in: "+
1865-
"%s", gm.Plural, grName)
1878+
return fmt.Errorf("Group %q references a non-existing "+
1879+
"Group %q", gm.Plural, parts[1])
18661880
}
18671881

1868-
r := g.FindResourceModel(parts[2])
1882+
// r := g.FindResourceModel(parts[2]) - don't use, don't want ximps
1883+
r := g.Resources[parts[2]]
18691884
if r == nil {
1870-
return fmt.Errorf("Group %q references a non-existing Resource "+
1871-
"in: "+"%s", gm.Plural, grName)
1885+
for _, imp := range g.XImportResources {
1886+
if strings.HasSuffix(imp, "/"+parts[2]) {
1887+
return fmt.Errorf("Group %q references an imported "+
1888+
"Resource %q, try using %q instead",
1889+
gm.Plural, grName, imp)
1890+
}
1891+
}
1892+
return fmt.Errorf("Group %q references a non-existing "+
1893+
"Resource %q", gm.Plural, grName)
1894+
}
1895+
1896+
if _, ok := plurals[parts[2]]; ok {
1897+
return fmt.Errorf("Group %q has a duplicate Resource \"plural\" "+
1898+
"name %q", gm.Plural, parts[2])
1899+
}
1900+
1901+
if _, ok := plurals[r.Singular]; ok {
1902+
return fmt.Errorf("Group %q has a \"singular\" Resource name "+
1903+
"%q that conflicts with a \"plural\" Resource name",
1904+
gm.Plural, parts[2])
18721905
}
1873-
resList[r.Plural] = true
1906+
1907+
if _, ok := singulars[r.Singular]; ok {
1908+
return fmt.Errorf("Group %q has a duplicate \"singular\" "+
1909+
"Resource name %q", gm.Plural, r.Singular)
1910+
}
1911+
1912+
plurals[parts[2]] = r.Singular
1913+
singulars[r.Singular] = parts[2]
18741914
}
18751915

18761916
// Verify the Resources to catch invalid Resource names early
1877-
for rmName, rm := range gm.Resources {
1917+
// Just so we always do them in order for consistent testing
1918+
rList := SortedKeys(gm.Resources)
1919+
for _, rmName := range rList {
1920+
rm := gm.Resources[rmName]
18781921
if rm == nil {
1879-
return fmt.Errorf("Resource %q can't be empty", rmName)
1922+
return fmt.Errorf("Group %q has an empty Resource %q",
1923+
gm.Plural, rmName)
1924+
}
1925+
1926+
if _, ok := plurals[rmName]; ok {
1927+
return fmt.Errorf("Group %q has a Resource %q that has a "+
1928+
"duplicate \"plural\" name", gm.Plural, rmName)
18801929
}
18811930

1882-
if resList[rmName] == true {
1883-
return fmt.Errorf("Resource %q is a duplicate name from the "+
1884-
"\"ximportresources\" list", rmName)
1931+
if _, ok := singulars[rmName]; ok {
1932+
return fmt.Errorf("Group %q has a Resource %q that has a "+
1933+
"duplicate \"singular\" name", gm.Plural, rmName)
18851934
}
18861935

18871936
rm.GroupModel = gm
18881937

18891938
if err := rm.Verify(rmName); err != nil {
18901939
return err
18911940
}
1941+
1942+
if _, ok := plurals[rm.Singular]; ok {
1943+
return fmt.Errorf("Group %q has a Resource %q that has a "+
1944+
"\"singular\" name that conflicts with an existing "+
1945+
"\"plural\" name", gm.Plural, rmName)
1946+
}
1947+
1948+
if _, ok := singulars[rm.Singular]; ok {
1949+
return fmt.Errorf("Group %q has a Resource %q that has a "+
1950+
"duplicate \"singular\" name %q",
1951+
gm.Plural, rmName, rm.Singular)
1952+
}
1953+
1954+
plurals[rmName] = rm.Singular
1955+
singulars[rm.Singular] = rmName
18921956
}
18931957

18941958
// Make sure we have the xRegistry core/spec defined attributes

tests/model3_test.go

Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
package tests
2+
3+
import (
4+
"testing"
5+
// "github.com/xregistry/server/registry"
6+
)
7+
8+
func TestModelXImportErrors(t *testing.T) {
9+
reg := NewRegistry("TestModelXImportErrors")
10+
defer PassDeleteReg(t, reg)
11+
12+
xHTTP(t, reg, "PUT", "/model", `{
13+
"groups": {
14+
"g1p": {
15+
"singular": "g1s",
16+
"resources": { "r1p": { "singular": "r1s" } }
17+
},
18+
"g2p": {
19+
"singular": "g2s",
20+
"ximportresources": [ "/g1p" ]
21+
}
22+
}
23+
}`, 400, `Group "g2p" has an invalid "ximportresources" value (/g1p), must be of the form "/Group/Resource"
24+
`)
25+
26+
xHTTP(t, reg, "PUT", "/model", `{
27+
"groups": {
28+
"g1p": {
29+
"singular": "g1s",
30+
"resources": { "r1p": { "singular": "r1s" } }
31+
},
32+
"g2p": {
33+
"singular": "g2s",
34+
"ximportresources": [ "/gxx/xxx" ]
35+
}
36+
}
37+
}`, 400, `Group "g2p" references a non-existing Group "gxx"
38+
`)
39+
40+
xHTTP(t, reg, "PUT", "/model", `{
41+
"groups": {
42+
"g1p": {
43+
"singular": "g1s",
44+
"resources": { "r1p": { "singular": "r1s" } }
45+
},
46+
"g2p": {
47+
"singular": "g2s",
48+
"ximportresources": [ "/g1p/xxx" ]
49+
}
50+
}
51+
}`, 400, `Group "g2p" references a non-existing Resource "/g1p/xxx"
52+
`)
53+
54+
xHTTP(t, reg, "PUT", "/model", `{
55+
"groups": {
56+
"g1p": {
57+
"singular": "g1s",
58+
"resources": { "r1p": { "singular": "r1p" } }
59+
},
60+
"g2p": {
61+
"singular": "g2s"
62+
}
63+
}
64+
}`, 400, `Resource "r1p" has same value for "plural" and "singular"
65+
`)
66+
67+
xHTTP(t, reg, "PUT", "/model", `{
68+
"groups": {
69+
"g1p": {
70+
"singular": "g1s",
71+
"resources": {
72+
"r1p": { "singular": "r1s" },
73+
"r2p": { "singular": "r1s" }
74+
}
75+
},
76+
"g2p": {
77+
"singular": "g2s"
78+
}
79+
}
80+
}`, 400, `Group "g1p" has a Resource "r2p" that has a duplicate "singular" name "r1s"
81+
`)
82+
83+
xHTTP(t, reg, "PUT", "/model", `{
84+
"groups": {
85+
"g1p": {
86+
"singular": "g1s",
87+
"resources": { "r1p": { "singular": "r1s" } }
88+
},
89+
"g2p": {
90+
"singular": "g2s",
91+
"ximportresources": [ "/g1p/r1p", "/g1p/r1p" ]
92+
}
93+
}
94+
}`, 400, `Group "g2p" has a duplicate Resource "plural" name "r1p"
95+
`)
96+
97+
xHTTP(t, reg, "PUT", "/model", `{
98+
"groups": {
99+
"g1p": {
100+
"singular": "g1s",
101+
"resources": {
102+
"r1p": { "singular": "r1s" },
103+
"r2p": { "singular": "R1S" }
104+
}
105+
},
106+
"g2p": {
107+
"singular": "g2s"
108+
}
109+
}
110+
}`, 400, `Invalid model type name "R1S", must match: ^[a-z_][a-z_0-9]{0,57}$
111+
`)
112+
113+
xHTTP(t, reg, "PUT", "/model", `{
114+
"groups": {
115+
"g1p": {
116+
"singular": "g1s",
117+
"resources": {
118+
"r1p": { "singular": "r1s" }
119+
}
120+
},
121+
"g2p": {
122+
"singular": "g2s",
123+
"ximportresources": [ "/g1p/r1p" ]
124+
},
125+
"g3p": {
126+
"singular": "g3s",
127+
"ximportresources": [ "/g2p/r1p" ]
128+
}
129+
}
130+
}`, 400, `Group "g3p" references an imported Resource "/g2p/r1p", try using "/g1p/r1p" instead
131+
`)
132+
133+
}
134+
135+
func TestModelXImport(t *testing.T) {
136+
reg := NewRegistry("TestModel")
137+
defer PassDeleteReg(t, reg)
138+
139+
xHTTP(t, reg, "PUT", "/model", `{
140+
"groups": {
141+
"g1p": {
142+
"singular": "g1s",
143+
"resources": {
144+
"r1p": { "singular": "r1s" },
145+
"r2p": { "singular": "r2s" }
146+
}
147+
},
148+
"g2p": {
149+
"singular": "g2s",
150+
"ximportresources": [ "/g1p/r1p" ]
151+
}
152+
}
153+
}`, 200, "*")
154+
155+
xHTTP(t, reg, "PUT", "/g1p/g1/r1p/r1", "{}", 201, "*")
156+
xHTTP(t, reg, "PUT", "/g2p/g1/r1p/r1", "{}", 201, "*")
157+
xHTTP(t, reg, "PUT", "/g2p/g1/r2p/r2", "{}", 404, "*")
158+
159+
// Erase everything, including the model itself
160+
xHTTP(t, reg, "DELETE", "/g1p", "", 204, "*")
161+
xHTTP(t, reg, "DELETE", "/g2p", "", 204, "*")
162+
xHTTP(t, reg, "PUT", "/model", `{}`, 200, "*")
163+
164+
xHTTP(t, reg, "PUT", "/model", `{
165+
"groups": {
166+
"g1p": {
167+
"singular": "g1s",
168+
"ximportresources": [ "/g2p/g2r2p" ],
169+
"resources": {
170+
"r1p": { "singular": "r1s" },
171+
"r2p": { "singular": "r2s" }
172+
}
173+
},
174+
"g2p": {
175+
"singular": "g2s",
176+
"ximportresources": [ "/g1p/r1p", "/g1p/r2p" ],
177+
"resources": {
178+
"g2r2p": { "singular": "g2r2s" }
179+
}
180+
}
181+
}
182+
}`, 200, "*")
183+
184+
xHTTP(t, reg, "PUT", "/g1p/g1/r1p/r1", "{}", 201, "*")
185+
xHTTP(t, reg, "PUT", "/g1p/g1/r2p/r1", "{}", 201, "*")
186+
xHTTP(t, reg, "PUT", "/g2p/g1/r1p/r1", "{}", 201, "*")
187+
xHTTP(t, reg, "PUT", "/g2p/g1/r2p/r1", "{}", 201, "*")
188+
xHTTP(t, reg, "PUT", "/g2p/g1/g2r2p/r1", "{}", 201, "*")
189+
190+
xHTTP(t, reg, "PUT", "/g2p/g1/r2p/r2/meta", `{"xref":"/g1p/g1/r2p/r1"}`,
191+
201, "*")
192+
}

0 commit comments

Comments
 (0)